|
|
@@ -1,156 +1,187 @@
|
|
|
<template>
|
|
|
<div class="account-detail-table">
|
|
|
- <el-form ref="detailForm" :model="{ flatList }">
|
|
|
- <ele-pro-table
|
|
|
- ref="detailTable"
|
|
|
- row-key="id"
|
|
|
- :needPage="false"
|
|
|
- :columns="columns"
|
|
|
- max-height="500px"
|
|
|
- :datasource="flatList"
|
|
|
- :cache-key="cacheKeyUrl"
|
|
|
- class="time-form"
|
|
|
- >
|
|
|
- <template v-slot:toolbar>
|
|
|
- <div class="detail-toolbar">
|
|
|
- <el-button
|
|
|
- v-if="!isView"
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- @click="batchSetTaxRate"
|
|
|
- >批量设置税率</el-button
|
|
|
- >
|
|
|
- <el-button
|
|
|
- v-if="!isView"
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- @click="batchSetPrice"
|
|
|
- >批量设置单价</el-button
|
|
|
- >
|
|
|
- <el-button
|
|
|
- v-if="!isView"
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- @click="batchSetDiscount"
|
|
|
- >批量设置折让比例</el-button
|
|
|
- >
|
|
|
- <!-- <div class="summary-info">
|
|
|
- <span
|
|
|
- >本次总对账金额:<b>{{ totalStatementAmount }}</b> 元</span
|
|
|
- >
|
|
|
- <span
|
|
|
- >本次总对账数量:<b>{{ totalStatementCount }}</b></span
|
|
|
- >
|
|
|
- </div> -->
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-slot:statementCount="scope">
|
|
|
- <el-form-item
|
|
|
- :prop="'flatList.' + scope.$index + '.statementCount'"
|
|
|
- :rules="{
|
|
|
- validator: (rule, value, cb) =>
|
|
|
- validateStatementCount(rule, value, cb, scope.row),
|
|
|
- trigger: 'change'
|
|
|
- }"
|
|
|
+ <el-tabs v-model="activeTab" class="detail-tabs">
|
|
|
+ <!-- 单据明细:按单据展开的可编辑明细,提交以这里的行数据为准 -->
|
|
|
+ <el-tab-pane label="单据明细" name="detail">
|
|
|
+ <el-form ref="detailForm" :model="{ flatList }">
|
|
|
+ <ele-pro-table
|
|
|
+ ref="detailTable"
|
|
|
+ row-key="id"
|
|
|
+ :needPage="false"
|
|
|
+ :columns="columns"
|
|
|
+ max-height="500px"
|
|
|
+ :datasource="flatList"
|
|
|
+ :cache-key="cacheKeyUrl"
|
|
|
+ class="time-form"
|
|
|
>
|
|
|
- <el-input
|
|
|
- v-model="scope.row.statementCount"
|
|
|
- type="number"
|
|
|
- :disabled="isView || queryDimension == 1"
|
|
|
- @input="handleCountChange(scope.row, scope.$index)"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <template v-slot:toolbar>
|
|
|
+ <div class="detail-toolbar">
|
|
|
+ <el-button
|
|
|
+ v-if="!isView"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="batchSetTaxRate"
|
|
|
+ >批量设置税率</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-if="!isView"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="batchSetPrice"
|
|
|
+ >批量设置单价</el-button
|
|
|
+ >
|
|
|
+ <el-button
|
|
|
+ v-if="!isView"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="batchSetDiscount"
|
|
|
+ >批量设置折让比例</el-button
|
|
|
+ >
|
|
|
+ <!-- <div class="summary-info">
|
|
|
+ <span
|
|
|
+ >本次总对账金额:<b>{{ totalStatementAmount }}</b> 元</span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ >本次总对账数量:<b>{{ totalStatementCount }}</b></span
|
|
|
+ >
|
|
|
+ </div> -->
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-slot:statementCount="scope">
|
|
|
+ <el-form-item
|
|
|
+ :prop="'flatList.' + scope.$index + '.statementCount'"
|
|
|
+ :rules="{
|
|
|
+ validator: (rule, value, cb) =>
|
|
|
+ validateStatementCount(rule, value, cb, scope.row),
|
|
|
+ trigger: 'change'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.statementCount"
|
|
|
+ type="number"
|
|
|
+ :disabled="isView || queryDimension == 1"
|
|
|
+ @input="handleCountChange(scope.row, scope.$index)"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:technologyRouteName="scope">
|
|
|
- <el-input
|
|
|
- v-model="scope.row.technologyRouteName"
|
|
|
- placeholder="请选择"
|
|
|
- :disabled="isView"
|
|
|
- @click.native="openVersion(scope.$index)"
|
|
|
- />
|
|
|
- </template>
|
|
|
+ <template v-slot:technologyRouteName="scope">
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.technologyRouteName"
|
|
|
+ placeholder="请选择"
|
|
|
+ :disabled="isView"
|
|
|
+ @click.native="openVersion(scope.$index)"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:singlePrice="scope">
|
|
|
- <el-form-item
|
|
|
- :prop="'flatList.' + scope.$index + '.singlePrice'"
|
|
|
- :rules="{
|
|
|
- required: true,
|
|
|
- message: '请输入单价',
|
|
|
- trigger: 'change'
|
|
|
- }"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="scope.row.singlePrice"
|
|
|
- type="number"
|
|
|
- :disabled="isView"
|
|
|
- @input="handlePriceChange(scope.row, scope.$index)"
|
|
|
- />
|
|
|
- <!-- <div v-if="isPriceInvalid(scope.row)" class="price-warning">
|
|
|
- 单价无效,请核对商品价格
|
|
|
- </div> -->
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <template v-slot:singlePrice="scope">
|
|
|
+ <el-form-item
|
|
|
+ :prop="'flatList.' + scope.$index + '.singlePrice'"
|
|
|
+ :rules="{
|
|
|
+ required: true,
|
|
|
+ message: '请输入单价',
|
|
|
+ trigger: 'change'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.singlePrice"
|
|
|
+ type="number"
|
|
|
+ :disabled="isView"
|
|
|
+ @input="handlePriceChange(scope.row, scope.$index)"
|
|
|
+ />
|
|
|
+ <!-- <div v-if="isPriceInvalid(scope.row)" class="price-warning">
|
|
|
+ 单价无效,请核对商品价格
|
|
|
+ </div> -->
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:taxRate="scope">
|
|
|
- <el-form-item
|
|
|
- :prop="'flatList.' + scope.$index + '.taxRate'"
|
|
|
- :rules="{
|
|
|
- required: isTaxRate == 1,
|
|
|
- message: '请输入税率',
|
|
|
- trigger: 'change'
|
|
|
- }"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="scope.row.taxRate"
|
|
|
- type="number"
|
|
|
- :disabled="isView"
|
|
|
- @input="handleTaxChange(scope.row, scope.$index)"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <template v-slot:taxRate="scope">
|
|
|
+ <el-form-item
|
|
|
+ :prop="'flatList.' + scope.$index + '.taxRate'"
|
|
|
+ :rules="{
|
|
|
+ required: isTaxRate == 1,
|
|
|
+ message: '请输入税率',
|
|
|
+ trigger: 'change'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.taxRate"
|
|
|
+ type="number"
|
|
|
+ :disabled="isView"
|
|
|
+ @input="handleTaxChange(scope.row, scope.$index)"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:discountRatio="scope">
|
|
|
- <el-form-item
|
|
|
- :prop="'flatList.' + scope.$index + '.discountRatio'"
|
|
|
- :rules="{
|
|
|
- required: true,
|
|
|
- message: '请输入折让比例',
|
|
|
- trigger: 'change'
|
|
|
- }"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="scope.row.discountRatio"
|
|
|
- type="number"
|
|
|
- :min="0"
|
|
|
- :max="100"
|
|
|
- :disabled="isView"
|
|
|
- @input="handleDiscountChange(scope.row, scope.$index)"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <template v-slot:discountRatio="scope">
|
|
|
+ <el-form-item
|
|
|
+ :prop="'flatList.' + scope.$index + '.discountRatio'"
|
|
|
+ :rules="{
|
|
|
+ required: true,
|
|
|
+ message: '请输入折让比例',
|
|
|
+ trigger: 'change'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.discountRatio"
|
|
|
+ type="number"
|
|
|
+ :min="0"
|
|
|
+ :max="100"
|
|
|
+ :disabled="isView"
|
|
|
+ @input="handleDiscountChange(scope.row, scope.$index)"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:statementAmount="scope">
|
|
|
- <span>{{ scope.row.statementAmount }}</span>
|
|
|
- </template>
|
|
|
+ <template v-slot:statementAmount="scope">
|
|
|
+ <span>{{ scope.row.statementAmount }}</span>
|
|
|
+ </template>
|
|
|
|
|
|
- <template v-slot:headerRequired="{ column }">
|
|
|
+ <template v-slot:headerRequired="{ column }">
|
|
|
<span class="is-required">{{ column.label }}</span>
|
|
|
</template>
|
|
|
|
|
|
- <template v-slot:action="scope">
|
|
|
- <el-link
|
|
|
- v-if="!isView && queryDimension == 2"
|
|
|
- type="danger"
|
|
|
- :underline="false"
|
|
|
- icon="el-icon-delete"
|
|
|
- @click="removeRow(scope.$index)"
|
|
|
- >删除</el-link
|
|
|
- >
|
|
|
- <span v-else>--</span>
|
|
|
- </template>
|
|
|
- </ele-pro-table>
|
|
|
- </el-form>
|
|
|
+ <template v-slot:action="scope">
|
|
|
+ <el-link
|
|
|
+ v-if="!isView && queryDimension == 2"
|
|
|
+ type="danger"
|
|
|
+ :underline="false"
|
|
|
+ icon="el-icon-delete"
|
|
|
+ @click="removeRow(scope.$index)"
|
|
|
+ >删除</el-link
|
|
|
+ >
|
|
|
+ <span v-else>--</span>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </el-form>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 物品明细:按物品编码汇总,只读展示数量,不参与提交 -->
|
|
|
+ <el-tab-pane label="物品明细" name="item">
|
|
|
+ <ele-pro-table
|
|
|
+ ref="itemTable"
|
|
|
+ row-key="groupKey"
|
|
|
+ :needPage="false"
|
|
|
+ :columns="itemColumns"
|
|
|
+ max-height="500px"
|
|
|
+ :datasource="itemGroupList"
|
|
|
+ :cache-key="itemCacheKey"
|
|
|
+ show-summary
|
|
|
+ :summary-method="itemSummaries"
|
|
|
+ class="time-form"
|
|
|
+ >
|
|
|
+ <template v-slot:toolbar>
|
|
|
+ <div class="detail-toolbar">
|
|
|
+ <span class="item-summary">
|
|
|
+ 共 <b>{{ itemGroupList.length }}</b> 种物品,本次对账数量合计:<b
|
|
|
+ >{{ totalStatementCount }}</b
|
|
|
+ >
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
|
|
|
<!-- 批量设置弹窗 -->
|
|
|
<el-dialog
|
|
|
@@ -213,6 +244,8 @@
|
|
|
data() {
|
|
|
return {
|
|
|
cacheKeyUrl: 'eom-202608171423-account-detail-table',
|
|
|
+ // 当前激活的页签:detail=单据明细(可编辑,提交依据),item=物品明细(只读汇总)
|
|
|
+ activeTab: 'detail',
|
|
|
isTaxRate: 0,
|
|
|
flatList: [],
|
|
|
totalStatementAmount: 0,
|
|
|
@@ -422,12 +455,172 @@
|
|
|
}
|
|
|
},
|
|
|
]
|
|
|
+ },
|
|
|
+ // 物品明细表的 cacheKey:与主表列配置隔离,避免两张表互相覆盖
|
|
|
+ itemCacheKey() {
|
|
|
+ return this.cacheKeyUrl + '_item';
|
|
|
+ },
|
|
|
+ // 物品明细列:不含任何价格/金额列
|
|
|
+ // (同一物品编码在不同单据上价格可能不同,汇总金额没有意义)
|
|
|
+ itemColumns() {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ width: 60,
|
|
|
+ label: '序号',
|
|
|
+ type: 'index',
|
|
|
+ columnKey: 'index',
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 140,
|
|
|
+ prop: 'productCode',
|
|
|
+ label: '产品编码',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 160,
|
|
|
+ prop: 'productName',
|
|
|
+ label: '产品名称',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 100,
|
|
|
+ prop: 'specification',
|
|
|
+ label: '规格',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 100,
|
|
|
+ prop: 'modelType',
|
|
|
+ label: '型号',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 80,
|
|
|
+ prop: 'colorKey',
|
|
|
+ label: '颜色',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 100,
|
|
|
+ prop: 'measuringUnit',
|
|
|
+ label: '计量单位',
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 140,
|
|
|
+ prop: 'batchNo',
|
|
|
+ label: '批次号',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 140,
|
|
|
+ prop: 'technologyRouteName',
|
|
|
+ label: '工艺路线',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 140,
|
|
|
+ prop: 'statementCount',
|
|
|
+ label: '本次对账数量',
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 120,
|
|
|
+ prop: 'totalCount',
|
|
|
+ label: this.queryDimension == 1 ? '发货数量' : '调拨数量',
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 120,
|
|
|
+ prop: 'outInCount',
|
|
|
+ label: '出库数量',
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 120,
|
|
|
+ prop: 'sendTotalWeight',
|
|
|
+ label: '出库重量',
|
|
|
+ align: 'center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ minWidth: 100,
|
|
|
+ prop: 'weightUnit',
|
|
|
+ label: '重量单位',
|
|
|
+ align: 'center'
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ },
|
|
|
+ // 物品明细数据:由单据明细按物品编码汇总而来
|
|
|
+ // 只读展示,不参与校验与提交;单据明细里改了数量,这里会跟着变
|
|
|
+ itemGroupList() {
|
|
|
+ const map = new Map();
|
|
|
+ const order = [];
|
|
|
+ this.flatList.forEach((row, index) => {
|
|
|
+ const code = (row.productCode || '').trim();
|
|
|
+ // 没有物品编码的行不参与合并,各自单独一行
|
|
|
+ const key = code || `__row_${index}`;
|
|
|
+ if (!map.has(key)) {
|
|
|
+ map.set(key, {
|
|
|
+ groupKey: key,
|
|
|
+ productCode: code,
|
|
|
+ productName: row.productName || '',
|
|
|
+ specification: row.specification || '',
|
|
|
+ modelType: row.modelType || '',
|
|
|
+ colorKey: row.colorKey || '',
|
|
|
+ measuringUnit: row.measuringUnit || '',
|
|
|
+ weightUnit: row.weightUnit || '',
|
|
|
+ batchNoList: [],
|
|
|
+ technologyRouteNameList: [],
|
|
|
+ statementCount: 0,
|
|
|
+ totalCount: 0,
|
|
|
+ outInCount: 0,
|
|
|
+ sendTotalWeight: 0
|
|
|
+ });
|
|
|
+ order.push(key);
|
|
|
+ }
|
|
|
+ const group = map.get(key);
|
|
|
+ group.statementCount += +row.statementCount || 0;
|
|
|
+ group.totalCount += +row.totalCount || 0;
|
|
|
+ group.outInCount += +row.outInCount || 0;
|
|
|
+ group.sendTotalWeight += +row.sendTotalWeight || 0;
|
|
|
+ // 批次号/工艺路线组内可能不一致,去重后合并展示
|
|
|
+ this.pushDistinct(group.batchNoList, row.batchNo);
|
|
|
+ this.pushDistinct(group.technologyRouteNameList, row.technologyRouteName);
|
|
|
+ });
|
|
|
+ return order.map((key) => {
|
|
|
+ const group = map.get(key);
|
|
|
+ // 求和后收敛小数,避免浮点误差
|
|
|
+ group.statementCount = this.roundNumber(group.statementCount);
|
|
|
+ group.totalCount = this.roundNumber(group.totalCount);
|
|
|
+ group.outInCount = this.roundNumber(group.outInCount);
|
|
|
+ group.sendTotalWeight = this.roundNumber(group.sendTotalWeight);
|
|
|
+ group.batchNo = group.batchNoList.join('、');
|
|
|
+ group.technologyRouteName = group.technologyRouteNameList.join('、');
|
|
|
+ delete group.batchNoList;
|
|
|
+ delete group.technologyRouteNameList;
|
|
|
+ return group;
|
|
|
+ });
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
|
datasource: {
|
|
|
handler(newVal, oldVal) {
|
|
|
this.$nextTick(() => {
|
|
|
+ // 区分「重新查询拿到新数据」和「行内编辑回写」:
|
|
|
+ // 编辑回写的是同一批对象引用,只有新查询才把页签切回单据明细
|
|
|
+ const isFresh =
|
|
|
+ !(newVal || []).length ||
|
|
|
+ (newVal || []).some((item) => !this.flatList.includes(item));
|
|
|
+ if (isFresh) this.activeTab = 'detail';
|
|
|
// 直接基于原始 productList 扁平行初始化派生字段(单价/税率/折让比例默认值及本次对账金额)
|
|
|
const list = (newVal || []).map((item) => this.buildRow(item, item.orderNo || '', false));
|
|
|
this.flatList = list;
|
|
|
@@ -450,6 +643,35 @@
|
|
|
);
|
|
|
},
|
|
|
methods: {
|
|
|
+ // 数组去重追加(批次号/工艺路线合并展示用)
|
|
|
+ pushDistinct(list, value) {
|
|
|
+ if (value === undefined || value === null || value === '') return;
|
|
|
+ const text = String(value);
|
|
|
+ if (!list.includes(text)) list.push(text);
|
|
|
+ },
|
|
|
+ // 数值收敛到 4 位小数
|
|
|
+ roundNumber(value) {
|
|
|
+ const num = +value || 0;
|
|
|
+ return Math.round(num * 10000) / 10000;
|
|
|
+ },
|
|
|
+ // 物品明细合计行:数量列求和
|
|
|
+ itemSummaries({ columns, data }) {
|
|
|
+ const sumProps = [
|
|
|
+ 'statementCount',
|
|
|
+ 'totalCount',
|
|
|
+ 'outInCount',
|
|
|
+ 'sendTotalWeight'
|
|
|
+ ];
|
|
|
+ return columns.map((column) => {
|
|
|
+ const prop = column.property;
|
|
|
+ if (prop === 'productCode') return '合计';
|
|
|
+ if (sumProps.includes(prop)) {
|
|
|
+ const total = data.reduce((pre, cur) => pre + (+cur[prop] || 0), 0);
|
|
|
+ return String(this.roundNumber(total));
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+ });
|
|
|
+ },
|
|
|
buildRow(item, orderNo, isReturn) {
|
|
|
const outboundCount = +item.totalCount || 0;
|
|
|
// 退货单数量为负
|
|
|
@@ -717,6 +939,18 @@
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.account-detail-table {
|
|
|
+ .detail-tabs {
|
|
|
+ ::v-deep .el-tabs__header {
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .item-summary {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ b {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+ }
|
|
|
.detail-toolbar {
|
|
|
display: flex;
|
|
|
justify-content: flex-start;
|