295 lines
8.3 KiB
Vue
295 lines
8.3 KiB
Vue
<template>
|
||
<view>
|
||
<view class="summary-title">{{ title }}</view>
|
||
<slot name="before-list" />
|
||
<view class="summary-wrap">
|
||
<scroll-view scroll-x class="summary-scroll">
|
||
<t-table v-if="rows.length" border="1" border-color="#e5e5e5" :style="{ width: tableWidth }">
|
||
<t-tr class="summary-head" font-size="18" color="#666666" align="left">
|
||
<t-th v-for="col in columns" :key="col.key" :style="cellStyle(col)">{{ col.label }}</t-th>
|
||
</t-tr>
|
||
<t-tr
|
||
v-for="row in rows"
|
||
:key="row.group_key"
|
||
font-size="18"
|
||
color="#333333"
|
||
align="left"
|
||
:class="{ 'summary-row--open': expandedKey === row.group_key }"
|
||
@click="toggleRow(row.group_key)"
|
||
>
|
||
<t-td v-for="col in columns" :key="col.key" :style="cellStyle(col)">{{ formatCell(row, col.key) }}</t-td>
|
||
</t-tr>
|
||
<t-tr class="summary-total" font-size="18" color="#333333" align="left">
|
||
<t-td v-for="col in columns" :key="col.key" :style="cellStyle(col)">{{ totalCell(col.key) }}</t-td>
|
||
</t-tr>
|
||
</t-table>
|
||
<view v-else class="summary-empty">{{ emptyText }}</view>
|
||
</scroll-view>
|
||
|
||
<view v-if="expandedRow" class="summary-fine">
|
||
<view class="summary-fineTitle">
|
||
细码({{ expandedRow.fine_codes.length }})
|
||
<text class="summary-fineHint">{{ expandedKey ? '点击行收起' : '' }}</text>
|
||
</view>
|
||
<view v-if="!expandedRow.fine_codes.length" class="summary-empty">暂无细码</view>
|
||
<scroll-view v-else scroll-x class="summary-scroll">
|
||
<t-table border="1" border-color="#e5e5e5" :style="{ width: fineTableWidth }">
|
||
<t-tr class="summary-head" font-size="18" color="#666666" align="left">
|
||
<t-th v-for="col in fineCols" :key="col.key" :style="cellStyle(col)">{{ col.label }}</t-th>
|
||
</t-tr>
|
||
<t-tr
|
||
v-for="(fc, fcIndex) in expandedRow.fine_codes"
|
||
:key="fc.id || fcIndex"
|
||
font-size="18"
|
||
color="#333333"
|
||
align="left"
|
||
>
|
||
<t-td
|
||
v-for="col in fineCols"
|
||
:key="col.key"
|
||
:class="{ 'summary-action': col.key === 'action' }"
|
||
:style="cellStyle(col)"
|
||
@click="onFineCellTap(col.key, fc)"
|
||
>{{ formatFineCell(fc, col.key) }}</t-td>
|
||
</t-tr>
|
||
</t-table>
|
||
</scroll-view>
|
||
<view v-if="deletable && expandedRow.fine_codes.length" class="summary-tip">
|
||
点击「删除」移除对应细码
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import tTable from '@/components/t-table/t-table.vue';
|
||
import tTh from '@/components/t-table/t-th.vue';
|
||
import tTr from '@/components/t-table/t-tr.vue';
|
||
import tTd from '@/components/t-table/t-td.vue';
|
||
|
||
const SUMMARY_COLUMNS = [
|
||
{ label: '成品编号', key: 'product_code', width: 260 },
|
||
{ label: '色号', key: 'product_color_code', width: 220 },
|
||
{ label: '匹数', key: 'roll', width: 140 },
|
||
{ label: '数量', key: 'weight', width: 160 },
|
||
{ label: '染整单', key: 'quote_order_no', width: 260 },
|
||
{ label: '缸号', key: 'dye_factory_dyelot_number', width: 220 },
|
||
];
|
||
|
||
const FINE_COLUMNS = [
|
||
{ label: '卷号', key: 'volume_number', width: 140 },
|
||
{ label: '外部卷号', key: 'extern_volume_number', width: 180 },
|
||
{ label: '匹数', key: 'roll', width: 120 },
|
||
{ label: '数量', key: 'weight', width: 150 },
|
||
{ label: '仓位', key: 'warehouse_bin_name', width: 180 },
|
||
{ label: '缸号', key: 'dye_factory_dyelot_number', width: 200 },
|
||
];
|
||
|
||
const TARE_COLUMNS = [
|
||
{ label: '纸筒重量', key: 'paper_tube_weight', width: 180 },
|
||
{ label: '包装重量', key: 'package_weight', width: 180 },
|
||
];
|
||
|
||
export default {
|
||
components: { tTable, tTh, tTr, tTd },
|
||
props: {
|
||
groups: { type: Array, default: () => [] },
|
||
title: { type: String, default: '明细' },
|
||
emptyText: { type: String, default: '暂无明细' },
|
||
rollKey: { type: String, default: 'in_roll' },
|
||
weightKey: { type: String, default: 'in_weight' },
|
||
deletable: { type: Boolean, default: false },
|
||
showTareColumns: { type: Boolean, default: false },
|
||
showBarCode: { type: Boolean, default: false },
|
||
},
|
||
data() {
|
||
return {
|
||
expandedKey: '',
|
||
};
|
||
},
|
||
computed: {
|
||
columns() {
|
||
return SUMMARY_COLUMNS;
|
||
},
|
||
rows() {
|
||
const rows = [];
|
||
(this.groups || []).forEach((product) => {
|
||
(product.colors || []).forEach((color) => {
|
||
rows.push({
|
||
group_key: color.group_key,
|
||
product_code: product.product_code || '',
|
||
product_name: product.product_name || '',
|
||
product_color_code: color.product_color_code || '',
|
||
product_color_name: color.product_color_name || '',
|
||
quote_order_no: color.quote_order_no || '',
|
||
dye_factory_dyelot_number: color.dye_factory_dyelot_number || '',
|
||
roll: color[this.rollKey],
|
||
weight: color[this.weightKey],
|
||
fine_codes: color.fine_codes || [],
|
||
});
|
||
});
|
||
});
|
||
return rows;
|
||
},
|
||
expandedRow() {
|
||
return this.rows.find((row) => row.group_key === this.expandedKey) || null;
|
||
},
|
||
fineCols() {
|
||
const cols = [...FINE_COLUMNS];
|
||
if (this.showBarCode) {
|
||
const idx = cols.findIndex((col) => col.key === 'weight');
|
||
cols.splice(idx + 1, 0, { label: '条码', key: 'bar_code', width: 280 });
|
||
}
|
||
if (this.showTareColumns) {
|
||
const idx = cols.findIndex((col) => col.key === 'weight');
|
||
cols.splice(idx + 1, 0, ...TARE_COLUMNS);
|
||
}
|
||
if (this.deletable) {
|
||
cols.push({ label: '操作', key: 'action', width: 120 });
|
||
}
|
||
return cols;
|
||
},
|
||
tableWidth() {
|
||
return this.sumWidth(this.columns);
|
||
},
|
||
fineTableWidth() {
|
||
return this.sumWidth(this.fineCols);
|
||
},
|
||
totalRoll() {
|
||
return this.sumField('roll');
|
||
},
|
||
totalWeight() {
|
||
return this.sumField('weight');
|
||
},
|
||
},
|
||
watch: {
|
||
rows() {
|
||
if (this.expandedKey && !this.rows.some((row) => row.group_key === this.expandedKey)) {
|
||
this.expandedKey = '';
|
||
}
|
||
},
|
||
},
|
||
methods: {
|
||
sumWidth(cols) {
|
||
const total = cols.reduce((sum, col) => sum + (col.width || 120), 0);
|
||
return `${total}rpx`;
|
||
},
|
||
sumField(key) {
|
||
const total = this.rows.reduce((sum, row) => sum + Number(row[key] || 0), 0);
|
||
return Number(total.toFixed(2));
|
||
},
|
||
cellStyle(col) {
|
||
const width = `${col.width}rpx`;
|
||
return {
|
||
width,
|
||
minWidth: width,
|
||
maxWidth: width,
|
||
flex: `0 0 ${width}`,
|
||
};
|
||
},
|
||
joinName(code, name) {
|
||
const main = code || '-';
|
||
return name ? `${main} ${name}` : main;
|
||
},
|
||
formatCell(row, key) {
|
||
if (key === 'product_code') return this.joinName(row.product_code, row.product_name);
|
||
if (key === 'product_color_code') return this.joinName(row.product_color_code, row.product_color_name);
|
||
if (key === 'roll' || key === 'weight') {
|
||
const val = row[key];
|
||
if (val === 0) return '0';
|
||
return val != null && val !== '' ? val : '-';
|
||
}
|
||
const val = row[key];
|
||
if (val === 0) return '0';
|
||
return val != null && val !== '' ? val : '-';
|
||
},
|
||
totalCell(key) {
|
||
if (key === 'product_code') return '合计';
|
||
if (key === 'roll') return this.totalRoll;
|
||
if (key === 'weight') return this.totalWeight;
|
||
return '';
|
||
},
|
||
toggleRow(key) {
|
||
this.expandedKey = this.expandedKey === key ? '' : key;
|
||
},
|
||
formatFineCell(fc, key) {
|
||
if (key === 'action') return '删除';
|
||
const val = fc[key];
|
||
if (val === 0) return '0';
|
||
return val != null && val !== '' ? val : '-';
|
||
},
|
||
onFineCellTap(key, fc) {
|
||
if (!this.deletable || key !== 'action') return;
|
||
this.$emit('delete', fc);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@import '@/common/pda-typography.scss';
|
||
|
||
.summary-title {
|
||
font-size: $pda-font-body;
|
||
font-weight: bold;
|
||
padding: 24rpx 26rpx 12rpx;
|
||
border-left: 6rpx solid #007AFF;
|
||
margin: 20rpx 26rpx 0;
|
||
}
|
||
.summary-wrap {
|
||
margin: 0 26rpx 16rpx;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
}
|
||
.summary-scroll {
|
||
width: 100%;
|
||
}
|
||
::v-deep .t-th,
|
||
::v-deep .t-td {
|
||
box-sizing: border-box;
|
||
min-height: $pda-row-min-height;
|
||
word-break: break-all;
|
||
}
|
||
::v-deep .summary-head {
|
||
background: #f7f8fa;
|
||
}
|
||
::v-deep .summary-row--open {
|
||
background: #f0f7ff;
|
||
}
|
||
::v-deep .summary-total {
|
||
background: #fff7e6;
|
||
}
|
||
::v-deep .summary-action {
|
||
color: #f5222d;
|
||
}
|
||
.summary-empty {
|
||
padding: 28rpx 16rpx;
|
||
font-size: $pda-font-placeholder;
|
||
color: #999;
|
||
}
|
||
.summary-fine {
|
||
padding: 8rpx 0 16rpx;
|
||
}
|
||
.summary-fineTitle {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 12rpx 16rpx 8rpx;
|
||
font-size: $pda-font-action;
|
||
color: #2979ff;
|
||
}
|
||
.summary-fineHint {
|
||
color: #999;
|
||
font-size: $pda-font-popup-sub;
|
||
}
|
||
.summary-action {
|
||
color: #f5222d;
|
||
}
|
||
.summary-tip {
|
||
padding: 8rpx 16rpx 0;
|
||
font-size: $pda-font-popup-sub;
|
||
color: #999;
|
||
}
|
||
</style>
|