|
@@ -1,16 +1,20 @@
|
|
|
<template>
|
|
<template>
|
|
|
<el-form ref="form" :model="form" :rules="rules">
|
|
<el-form ref="form" :model="form" :rules="rules">
|
|
|
- <ele-pro-table
|
|
|
|
|
- ref="table"
|
|
|
|
|
- :needPage="false"
|
|
|
|
|
- :columns="columns"
|
|
|
|
|
- @columns-change="handleColumnChange"
|
|
|
|
|
- :cache-key="cacheKeyUrl"
|
|
|
|
|
- max-height="500px"
|
|
|
|
|
- :datasource="form.datasource"
|
|
|
|
|
- class="time-form"
|
|
|
|
|
- :selection.sync="selection"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <div class="inventory-table-wrap">
|
|
|
|
|
+ <ele-pro-table
|
|
|
|
|
+ ref="table"
|
|
|
|
|
+ :needPage="false"
|
|
|
|
|
+ :columns="columns"
|
|
|
|
|
+ @columns-change="handleColumnChange"
|
|
|
|
|
+ :cache-key="cacheKeyUrl"
|
|
|
|
|
+ @header-dragend="handleHeaderDragend"
|
|
|
|
|
+ row-key="key"
|
|
|
|
|
+ max-height="500px"
|
|
|
|
|
+ :datasource="form.datasource"
|
|
|
|
|
+ class="time-form"
|
|
|
|
|
+ :class="{ 'time-form-busy': tableBusy }"
|
|
|
|
|
+ :selection.sync="selection"
|
|
|
|
|
+ >
|
|
|
<!-- 表头工具栏 -->
|
|
<!-- 表头工具栏 -->
|
|
|
<template v-slot:toolbar>
|
|
<template v-slot:toolbar>
|
|
|
<div class="headbox">
|
|
<div class="headbox">
|
|
@@ -21,6 +25,7 @@
|
|
|
icon="el-icon-plus"
|
|
icon="el-icon-plus"
|
|
|
class="ele-btn-icon"
|
|
class="ele-btn-icon"
|
|
|
@click="handParent('', -1)"
|
|
@click="handParent('', -1)"
|
|
|
|
|
+ :disabled="tableBusy"
|
|
|
v-if="!isContractId"
|
|
v-if="!isContractId"
|
|
|
>
|
|
>
|
|
|
新增
|
|
新增
|
|
@@ -553,7 +558,7 @@
|
|
|
@confirm="remove(row, $index)"
|
|
@confirm="remove(row, $index)"
|
|
|
>
|
|
>
|
|
|
<template v-slot:reference>
|
|
<template v-slot:reference>
|
|
|
- <el-link type="danger" :underline="false" icon="el-icon-delete">
|
|
|
|
|
|
|
+ <el-link type="danger" :underline="false" icon="el-icon-delete" :disabled="tableBusy">
|
|
|
删除
|
|
删除
|
|
|
</el-link>
|
|
</el-link>
|
|
|
</template>
|
|
</template>
|
|
@@ -618,6 +623,13 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</template> -->
|
|
</template> -->
|
|
|
</ele-pro-table>
|
|
</ele-pro-table>
|
|
|
|
|
+ <div v-if="tableLoading" class="table-loading-mask">
|
|
|
|
|
+ <i class="el-icon-loading"></i>
|
|
|
|
|
+ <span class="table-loading-text">
|
|
|
|
|
+ {{ batchLoading ? `正在加载 ${loadedCount}/${totalCount}` : '正在加载,请稍候' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
<product-list
|
|
<product-list
|
|
|
ref="productListRef"
|
|
ref="productListRef"
|
|
@@ -682,6 +694,10 @@
|
|
|
showSaleCount: {
|
|
showSaleCount: {
|
|
|
type: Boolean,
|
|
type: Boolean,
|
|
|
default: false
|
|
default: false
|
|
|
|
|
+ },
|
|
|
|
|
+ appendBatchSize: {
|
|
|
|
|
+ type: Number,
|
|
|
|
|
+ default: 20
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
components: {
|
|
components: {
|
|
@@ -693,53 +709,33 @@
|
|
|
setAllValue
|
|
setAllValue
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
|
|
|
+ tableBusy() {
|
|
|
|
|
+ return this.batchLoading;
|
|
|
|
|
+ },
|
|
|
canHandl() {
|
|
canHandl() {
|
|
|
return this.form.datasource.length;
|
|
return this.form.datasource.length;
|
|
|
},
|
|
},
|
|
|
contractId() {
|
|
contractId() {
|
|
|
return this.$store.state.order.contractId;
|
|
return this.$store.state.order.contractId;
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- data() {
|
|
|
|
|
- const defaultForm = {
|
|
|
|
|
- key: null,
|
|
|
|
|
- endTime: '',
|
|
|
|
|
- isFirst: 0,
|
|
|
|
|
- name: '',
|
|
|
|
|
- startTime: '',
|
|
|
|
|
- workHour: '',
|
|
|
|
|
- technicalDrawings: [],
|
|
|
|
|
- discountRatio: 100,
|
|
|
|
|
- quoteWay: 1
|
|
|
|
|
- };
|
|
|
|
|
- return {
|
|
|
|
|
- selection: [],
|
|
|
|
|
- quoteTypeOp,
|
|
|
|
|
- levelList,
|
|
|
|
|
- pricingWayList,
|
|
|
|
|
- discountTotalPrice: 0.0,
|
|
|
|
|
- allPrice: 0.0,
|
|
|
|
|
- numberReg,
|
|
|
|
|
- positiveIntegerReg,
|
|
|
|
|
- defaultForm,
|
|
|
|
|
- contractStartDate: new Date(),
|
|
|
|
|
- form: {
|
|
|
|
|
- discountTotalPrice: 0,
|
|
|
|
|
- datasource: []
|
|
|
|
|
- },
|
|
|
|
|
- rules: {},
|
|
|
|
|
- dictList: {},
|
|
|
|
|
- billDetailDialogFlag: false,
|
|
|
|
|
- taskinstanceDialogFlag: false,
|
|
|
|
|
- columns: [
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ columns() {
|
|
|
|
|
+ this.widthsVersion;
|
|
|
|
|
+ const cacheWidths = this.getStorage(this.cacheKeyUrl + 'Widths') || {};
|
|
|
|
|
+ const applyWidth = (col) => {
|
|
|
|
|
+ if (col.prop && cacheWidths[col.prop]) {
|
|
|
|
|
+ return { ...col, width: cacheWidths[col.prop], minWidth: undefined };
|
|
|
|
|
+ }
|
|
|
|
|
+ return col;
|
|
|
|
|
+ };
|
|
|
|
|
+ return [
|
|
|
{
|
|
{
|
|
|
label: '选择',
|
|
label: '选择',
|
|
|
width: 45,
|
|
width: 45,
|
|
|
type: 'selection',
|
|
type: 'selection',
|
|
|
columnKey: 'selection',
|
|
columnKey: 'selection',
|
|
|
align: 'center',
|
|
align: 'center',
|
|
|
- fixed: 'left'
|
|
|
|
|
|
|
+ fixed: 'left',
|
|
|
|
|
+ reserveSelection: true
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
width: 45,
|
|
width: 45,
|
|
@@ -1165,7 +1161,46 @@
|
|
|
fixed: 'right',
|
|
fixed: 'right',
|
|
|
showOverflowTooltip: true
|
|
showOverflowTooltip: true
|
|
|
}
|
|
}
|
|
|
- ],
|
|
|
|
|
|
|
+ ].map(applyWidth);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ data() {
|
|
|
|
|
+ const defaultForm = {
|
|
|
|
|
+ key: null,
|
|
|
|
|
+ endTime: '',
|
|
|
|
|
+ isFirst: 0,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ startTime: '',
|
|
|
|
|
+ workHour: '',
|
|
|
|
|
+ technicalDrawings: [],
|
|
|
|
|
+ discountRatio: 100,
|
|
|
|
|
+ quoteWay: 1
|
|
|
|
|
+ };
|
|
|
|
|
+ return {
|
|
|
|
|
+ selection: [],
|
|
|
|
|
+ quoteTypeOp,
|
|
|
|
|
+ levelList,
|
|
|
|
|
+ pricingWayList,
|
|
|
|
|
+ discountTotalPrice: 0.0,
|
|
|
|
|
+ allPrice: 0.0,
|
|
|
|
|
+ numberReg,
|
|
|
|
|
+ positiveIntegerReg,
|
|
|
|
|
+ tableLoading: false,
|
|
|
|
|
+ batchLoading: false,
|
|
|
|
|
+ loadedCount: 0,
|
|
|
|
|
+ totalCount: 0,
|
|
|
|
|
+ widthsVersion: 0,
|
|
|
|
|
+ defaultForm,
|
|
|
|
|
+ contractStartDate: new Date(),
|
|
|
|
|
+ form: {
|
|
|
|
|
+ discountTotalPrice: 0,
|
|
|
|
|
+ datasource: []
|
|
|
|
|
+ },
|
|
|
|
|
+ rules: {},
|
|
|
|
|
+ dictList: {},
|
|
|
|
|
+ billDetailDialogFlag: false,
|
|
|
|
|
+ taskinstanceDialogFlag: false,
|
|
|
purchaseSinglePriceData: []
|
|
purchaseSinglePriceData: []
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
@@ -1185,6 +1220,7 @@
|
|
|
methods: {
|
|
methods: {
|
|
|
//批量设置税率
|
|
//批量设置税率
|
|
|
setAllTaxRateChange({ key, value }) {
|
|
setAllTaxRateChange({ key, value }) {
|
|
|
|
|
+ if (this.tableBusy) return;
|
|
|
let indexS = this.selection.map((item) => item.key - 1);
|
|
let indexS = this.selection.map((item) => item.key - 1);
|
|
|
indexS.forEach((i) => {
|
|
indexS.forEach((i) => {
|
|
|
this.$set(this.form.datasource[i], key, value);
|
|
this.$set(this.form.datasource[i], key, value);
|
|
@@ -1212,6 +1248,11 @@
|
|
|
if (comitDatasource.length === 0) return [];
|
|
if (comitDatasource.length === 0) return [];
|
|
|
comitDatasource.forEach((v, i) => {
|
|
comitDatasource.forEach((v, i) => {
|
|
|
v.detailType = this.detailType;
|
|
v.detailType = this.detailType;
|
|
|
|
|
+ const purchaseCount = v.purchaseCount;
|
|
|
|
|
+ if ((v.totalCount === undefined || v.totalCount === null || v.totalCount === '') &&
|
|
|
|
|
+ purchaseCount !== undefined && purchaseCount !== null && purchaseCount !== '') {
|
|
|
|
|
+ v.totalCount = purchaseCount;
|
|
|
|
|
+ }
|
|
|
if (v.modelKey) {
|
|
if (v.modelKey) {
|
|
|
v.modelKey = v.modelKey;
|
|
v.modelKey = v.modelKey;
|
|
|
}
|
|
}
|
|
@@ -1250,6 +1291,128 @@
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 重算表格。dirtyIndexes 只指定本次真正变动的行下标,
|
|
|
|
|
+ * 其余行直接复用原对象引用,省掉深拷贝与重复观测的成本。
|
|
|
|
|
+ * 不传 dirtyIndexes 时退化为全量重算(数量/税率等手工输入场景)。
|
|
|
|
|
+ * 采购端没把 countObj 绑成 prop,所以这里用内联字面量。
|
|
|
|
|
+ */
|
|
|
|
|
+ recalculateTableOnce(dirtyIndexes) {
|
|
|
|
|
+ const countObj = {
|
|
|
|
|
+ countKey: 'purchaseCount',
|
|
|
|
|
+ unitKey: 'purchaseUnit',
|
|
|
|
|
+ unitIdKey: 'purchaseUnitId',
|
|
|
|
|
+ type: 'purchase'
|
|
|
|
|
+ };
|
|
|
|
|
+ const dirty = Array.isArray(dirtyIndexes)
|
|
|
|
|
+ ? new Set(dirtyIndexes)
|
|
|
|
|
+ : null;
|
|
|
|
|
+ this.form.datasource = this.form.datasource.map((item, index) => {
|
|
|
|
|
+ if (dirty && !dirty.has(index)) {
|
|
|
|
|
+ return item;
|
|
|
|
|
+ }
|
|
|
|
|
+ const row = changeCount(item, countObj, false);
|
|
|
|
|
+ if (row.pricingWay == 3) {
|
|
|
|
|
+ row.totalPrice = formatPrice(
|
|
|
|
|
+ (row.increaseTotalWeight || 0) * row.singlePrice
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ row.notaxSinglePrice =
|
|
|
|
|
+ row.singlePrice && row.taxRate
|
|
|
|
|
+ ? formatPrice(row.singlePrice / (1 + row.taxRate / 100))
|
|
|
|
|
+ : '';
|
|
|
|
|
+ return row;
|
|
|
|
|
+ });
|
|
|
|
|
+ this.changeAll();
|
|
|
|
|
+ },
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 大批量回显时先让 loading 遮罩绘制出来,再执行会阻塞主线程的计算。
|
|
|
|
|
+ * pro-table 内部在同一个 tick 里把 tableLoading 置 true 又置 false,
|
|
|
|
|
+ * 遮罩来不及显示,所以这里必须跨帧控制。
|
|
|
|
|
+ */
|
|
|
|
|
+ runAfterPaint(task) {
|
|
|
|
|
+ this._loadingStart = Date.now();
|
|
|
|
|
+ this.tableLoading = true;
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ requestAnimationFrame(task);
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ // 保证遮罩最短展示时间,避免一闪而过
|
|
|
|
|
+ endTableLoading(minDuration = 200) {
|
|
|
|
|
+ const start = this._loadingStart || Date.now();
|
|
|
|
|
+ const rest = minDuration - (Date.now() - start);
|
|
|
|
|
+ if (rest > 0) {
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.tableLoading = false;
|
|
|
|
|
+ }, rest);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.tableLoading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 分批追加:一次选中上百个物品时,一次性塞进表格会让主线程长时间阻塞,
|
|
|
|
|
+ * 这里按 appendBatchSize 分批写入并重算,每批之间让出一帧,
|
|
|
|
|
+ * 浏览器可以正常绘制 loading 遮罩和已经渲染出来的行。
|
|
|
|
|
+ */
|
|
|
|
|
+ appendRowsInBatches(
|
|
|
|
|
+ rows,
|
|
|
|
|
+ startIndex = this.form.datasource.length,
|
|
|
|
|
+ onDone
|
|
|
|
|
+ ) {
|
|
|
|
|
+ if (!rows.length) {
|
|
|
|
|
+ if (onDone) onDone();
|
|
|
|
|
+ this.endTableLoading();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const batchSize = Math.max(1, this.appendBatchSize || 20);
|
|
|
|
|
+ const total = rows.length;
|
|
|
|
|
+ this.batchLoading = true;
|
|
|
|
|
+ this.loadedCount = 0;
|
|
|
|
|
+ this.totalCount = total;
|
|
|
|
|
+ let done = 0;
|
|
|
|
|
+
|
|
|
|
|
+ const step = () => {
|
|
|
|
|
+ const batch = rows.slice(done, done + batchSize);
|
|
|
|
|
+ if (batch.length) {
|
|
|
|
|
+ const from = startIndex + done;
|
|
|
|
|
+ this.form.datasource = this.form.datasource.concat(batch);
|
|
|
|
|
+ // 只重算本批新增的行,其余行复用原对象
|
|
|
|
|
+ this.recalculateTableOnce(batch.map((row, i) => from + i));
|
|
|
|
|
+ done += batch.length;
|
|
|
|
|
+ this.loadedCount = done;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (done < total) {
|
|
|
|
|
+ requestAnimationFrame(step);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 全部批次都写完并渲染完,才收尾关闭 loading
|
|
|
|
|
+ this.batchLoading = false;
|
|
|
|
|
+ if (onDone) onDone();
|
|
|
|
|
+ this.endTableLoading();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ requestAnimationFrame(step);
|
|
|
|
|
+ },
|
|
|
|
|
+ afterReplaceTable() {
|
|
|
|
|
+ // 计量数量为后端非空字段:只补齐留空的行,避免覆盖已按计量单位换算好的数据
|
|
|
|
|
+ this.form.datasource.forEach((item) => {
|
|
|
|
|
+ const count = item.purchaseCount;
|
|
|
|
|
+ if (
|
|
|
|
|
+ (item.totalCount === undefined ||
|
|
|
|
|
+ item.totalCount === null ||
|
|
|
|
|
+ item.totalCount === '') &&
|
|
|
|
|
+ count !== undefined &&
|
|
|
|
|
+ count !== null &&
|
|
|
|
|
+ count !== ''
|
|
|
|
|
+ ) {
|
|
|
|
|
+ this.$set(item, 'totalCount', count);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ if (this.showSaleCount) {
|
|
|
|
|
+ this.changeCount();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
//改变数量
|
|
//改变数量
|
|
|
changeCount(row, index, weightType) {
|
|
changeCount(row, index, weightType) {
|
|
|
// if (this.detailType) {
|
|
// if (this.detailType) {
|
|
@@ -1505,6 +1668,7 @@
|
|
|
},
|
|
},
|
|
|
//选择产品
|
|
//选择产品
|
|
|
handParent(row, index) {
|
|
handParent(row, index) {
|
|
|
|
|
+ if (this.tableBusy) return;
|
|
|
let item = {
|
|
let item = {
|
|
|
id: row.productCode
|
|
id: row.productCode
|
|
|
};
|
|
};
|
|
@@ -1538,90 +1702,121 @@
|
|
|
});
|
|
});
|
|
|
},
|
|
},
|
|
|
//选择产品回调
|
|
//选择产品回调
|
|
|
- async changeParent(obj = [], idx) {
|
|
|
|
|
- console.log('选择产品回调', obj);
|
|
|
|
|
- const promises = obj.map(async (item, index) => {
|
|
|
|
|
- let i = idx == -1 ? index : idx;
|
|
|
|
|
- let row = JSON.parse(JSON.stringify(this.defaultForm));
|
|
|
|
|
- row.key = this.form.datasource.length + 1;
|
|
|
|
|
- let parasm = idx == -1 ? row : this.form.datasource[i];
|
|
|
|
|
- this.$set(parasm, 'productId', item.id);
|
|
|
|
|
- this.$set(parasm, 'categoryName', item.name);
|
|
|
|
|
- this.$set(parasm, 'productCategoryId', item.categoryLevelId);
|
|
|
|
|
- this.$set(parasm, 'productBrand', item.brandNum);
|
|
|
|
|
- this.$set(parasm, 'productCategoryName', item.categoryLevelPath);
|
|
|
|
|
- this.$set(parasm, 'productCode', item.code);
|
|
|
|
|
- this.$set(parasm, 'productName', item.name);
|
|
|
|
|
- this.$set(parasm, 'modelType', item.modelType);
|
|
|
|
|
- this.$set(parasm, 'availableCountBase', item.availableCountBase);
|
|
|
|
|
- this.$set(parasm, 'measuringUnit', item.measuringUnit);
|
|
|
|
|
- this.$set(parasm, 'specification', item.specification);
|
|
|
|
|
- this.$set(parasm, 'weightUnit', item.weightUnit);
|
|
|
|
|
- this.$set(parasm, 'imgCode', item.imgCode);
|
|
|
|
|
- this.$set(parasm, 'produceType', item.componentAttribute);
|
|
|
|
|
- this.$set(parasm, 'approvalNumber', item.extField?.approvalNumber);
|
|
|
|
|
- this.$set(parasm, 'singleWeight', item.netWeight);
|
|
|
|
|
- this.$set(parasm, 'pricingWay', item.pricingWay || 1);
|
|
|
|
|
- this.$set(parasm, 'goodsLevel', item.level);
|
|
|
|
|
- this.$set(parasm, 'taxRate', 13);
|
|
|
|
|
- this.$set(parasm, 'totalCount', 0);
|
|
|
|
|
- if(this.showSaleCount) {
|
|
|
|
|
- this.$set(parasm, 'purchaseCount', item.saleCount);
|
|
|
|
|
- }
|
|
|
|
|
- if (item.modelKey) {
|
|
|
|
|
- this.$set(parasm, 'modelKey', item.modelKey?.split(',')?.[0] || '');
|
|
|
|
|
- }
|
|
|
|
|
- if (item.colorKey) {
|
|
|
|
|
- this.$set(parasm, 'colorKey', item.colorKey?.split(',')?.[0] || '');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 构造选择产品回显的一行。
|
|
|
|
|
+ * 不传 oldRow 时返回普通对象(尚未进入响应式数组,赋值零成本)。
|
|
|
|
|
+ */
|
|
|
|
|
+ buildProductRow(item, oldRow, key) {
|
|
|
|
|
+ let parasm = oldRow || JSON.parse(JSON.stringify(this.defaultForm));
|
|
|
|
|
+ parasm.key = key;
|
|
|
|
|
+ this.$set(parasm, 'productId', item.id);
|
|
|
|
|
+ this.$set(parasm, 'categoryName', item.name);
|
|
|
|
|
+ this.$set(parasm, 'productCategoryId', item.categoryLevelId);
|
|
|
|
|
+ this.$set(parasm, 'productBrand', item.brandNum);
|
|
|
|
|
+ this.$set(parasm, 'productCategoryName', item.categoryLevelPath);
|
|
|
|
|
+ this.$set(parasm, 'productCode', item.code);
|
|
|
|
|
+ this.$set(parasm, 'productName', item.name);
|
|
|
|
|
+ this.$set(parasm, 'modelType', item.modelType);
|
|
|
|
|
+ this.$set(parasm, 'availableCountBase', item.availableCountBase);
|
|
|
|
|
+ this.$set(parasm, 'measuringUnit', item.measuringUnit);
|
|
|
|
|
+ this.$set(parasm, 'specification', item.specification);
|
|
|
|
|
+ this.$set(parasm, 'weightUnit', item.weightUnit);
|
|
|
|
|
+ this.$set(parasm, 'imgCode', item.imgCode);
|
|
|
|
|
+ this.$set(parasm, 'produceType', item.componentAttribute);
|
|
|
|
|
+ this.$set(parasm, 'approvalNumber', item.extField?.approvalNumber);
|
|
|
|
|
+ this.$set(parasm, 'singleWeight', item.netWeight);
|
|
|
|
|
+ this.$set(parasm, 'pricingWay', item.pricingWay || 1);
|
|
|
|
|
+ this.$set(parasm, 'goodsLevel', item.level);
|
|
|
|
|
+ this.$set(parasm, 'taxRate', 13);
|
|
|
|
|
+ this.$set(parasm, 'totalCount', 0);
|
|
|
|
|
+ if (this.showSaleCount) {
|
|
|
|
|
+ this.$set(parasm, 'purchaseCount', item.saleCount);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.modelKey) {
|
|
|
|
|
+ this.$set(parasm, 'modelKey', item.modelKey?.split(',')?.[0] || '');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.colorKey) {
|
|
|
|
|
+ this.$set(parasm, 'colorKey', item.colorKey?.split(',')?.[0] || '');
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- this.$set(parasm, 'discountRatio', parasm.discountRatio || 100);
|
|
|
|
|
|
|
+ this.$set(parasm, 'discountRatio', parasm.discountRatio || 100);
|
|
|
|
|
|
|
|
- this.$set(parasm, 'quoteWay', parasm.quoteWay || 100);
|
|
|
|
|
|
|
+ this.$set(parasm, 'quoteWay', parasm.quoteWay || 100);
|
|
|
|
|
|
|
|
|
|
+ this.$set(
|
|
|
|
|
+ parasm,
|
|
|
|
|
+ 'packingSpecification',
|
|
|
|
|
+ item.extField?.packingSpecification
|
|
|
|
|
+ );
|
|
|
|
|
+ this.$set(
|
|
|
|
|
+ parasm,
|
|
|
|
|
+ 'packageDispositionList',
|
|
|
|
|
+ item.packageDispositionList
|
|
|
|
|
+ );
|
|
|
|
|
+ if (item.packageDispositionList?.length) {
|
|
|
this.$set(
|
|
this.$set(
|
|
|
parasm,
|
|
parasm,
|
|
|
- 'packingSpecification',
|
|
|
|
|
- item.extField?.packingSpecification
|
|
|
|
|
|
|
+ 'purchaseUnitId',
|
|
|
|
|
+ item.packageDispositionList[0].id
|
|
|
);
|
|
);
|
|
|
this.$set(
|
|
this.$set(
|
|
|
parasm,
|
|
parasm,
|
|
|
- 'packageDispositionList',
|
|
|
|
|
- item.packageDispositionList
|
|
|
|
|
|
|
+ 'purchaseUnit',
|
|
|
|
|
+ item.packageDispositionList[0].conversionUnit
|
|
|
);
|
|
);
|
|
|
- if (item.packageDispositionList?.length) {
|
|
|
|
|
- this.$set(
|
|
|
|
|
- parasm,
|
|
|
|
|
- 'purchaseUnitId',
|
|
|
|
|
- item.packageDispositionList[0].id
|
|
|
|
|
- );
|
|
|
|
|
- this.$set(
|
|
|
|
|
- parasm,
|
|
|
|
|
- 'purchaseUnit',
|
|
|
|
|
- item.packageDispositionList[0].conversionUnit
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
- this.$set(parasm, 'supplierMark', this.supplierMark);
|
|
|
|
|
- // this.$set(parasm, 'batchNo', await getCode('1900101420390125570'));
|
|
|
|
|
- if (item.purchaseOrigins?.length > 0) {
|
|
|
|
|
- item.purchaseOrigins = item.purchaseOrigins.map((val) => val + '');
|
|
|
|
|
- }
|
|
|
|
|
- this.$set(parasm, 'provenance', item.purchaseOrigins || []);
|
|
|
|
|
- if (idx == -1) {
|
|
|
|
|
- this.form.datasource.push(row);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$set(parasm, 'supplierMark', this.supplierMark);
|
|
|
|
|
+ if (item.purchaseOrigins?.length > 0) {
|
|
|
|
|
+ item.purchaseOrigins = item.purchaseOrigins.map((val) => val + '');
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$set(parasm, 'provenance', item.purchaseOrigins || []);
|
|
|
|
|
|
|
|
- // 等待所有异步操作完成
|
|
|
|
|
- await Promise.all(promises);
|
|
|
|
|
-
|
|
|
|
|
- // 只有当idx == -1时才emit数据给父组件
|
|
|
|
|
|
|
+ return parasm;
|
|
|
|
|
+ },
|
|
|
|
|
+ updateProductRow(item, idx) {
|
|
|
|
|
+ this.$set(
|
|
|
|
|
+ this.form.datasource,
|
|
|
|
|
+ idx,
|
|
|
|
|
+ this.buildProductRow(
|
|
|
|
|
+ item,
|
|
|
|
|
+ this.form.datasource[idx],
|
|
|
|
|
+ this.form.datasource[idx]?.key || idx + 1
|
|
|
|
|
+ )
|
|
|
|
|
+ );
|
|
|
|
|
+ this.recalculateTableOnce([idx]);
|
|
|
|
|
+ },
|
|
|
|
|
+ //选择产品回调
|
|
|
|
|
+ changeParent(obj = [], idx) {
|
|
|
if (idx == -1) {
|
|
if (idx == -1) {
|
|
|
- this.$emit('getProductData', this.form.datasource);
|
|
|
|
|
- this.changeCount();
|
|
|
|
|
|
|
+ // 新增:先构造好所有行,再分批追加并渲染
|
|
|
|
|
+ const startIndex = this.form.datasource.length;
|
|
|
|
|
+ const maxKey = this.form.datasource.reduce(
|
|
|
|
|
+ (max, item) => Math.max(max, Number(item.key) || 0),
|
|
|
|
|
+ 0
|
|
|
|
|
+ );
|
|
|
|
|
+ const rows = obj.map((item, index) =>
|
|
|
|
|
+ this.buildProductRow(item, null, maxKey + index + 1)
|
|
|
|
|
+ );
|
|
|
|
|
+ this.runAfterPaint(() => {
|
|
|
|
|
+ this.appendRowsInBatches(rows, startIndex, () => {
|
|
|
|
|
+ this.$emit('getProductData', this.form.datasource);
|
|
|
|
|
+ this.changeCount();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 编辑已有行:单行替换
|
|
|
|
|
+ this.runAfterPaint(() => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.updateProductRow(obj[0], idx);
|
|
|
|
|
+ this.afterReplaceTable();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.endTableLoading();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
},
|
|
},
|
|
|
remove(row, index) {
|
|
remove(row, index) {
|
|
|
|
|
+ if (this.tableBusy) return;
|
|
|
this.form.datasource.splice(index, 1);
|
|
this.form.datasource.splice(index, 1);
|
|
|
// let index = this.form.datasource.findIndex((n) => n.key == row.key);
|
|
// let index = this.form.datasource.findIndex((n) => n.key == row.key);
|
|
|
// if (index !== -1) {
|
|
// if (index !== -1) {
|
|
@@ -1642,6 +1837,7 @@
|
|
|
},
|
|
},
|
|
|
// 添加
|
|
// 添加
|
|
|
handlAdd() {
|
|
handlAdd() {
|
|
|
|
|
+ if (this.tableBusy) return;
|
|
|
let item = JSON.parse(JSON.stringify(this.defaultForm));
|
|
let item = JSON.parse(JSON.stringify(this.defaultForm));
|
|
|
item.key = this.form.datasource.length + 1;
|
|
item.key = this.form.datasource.length + 1;
|
|
|
this.form.datasource.push(item);
|
|
this.form.datasource.push(item);
|
|
@@ -1683,6 +1879,7 @@
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
validateForm(callback) {
|
|
validateForm(callback) {
|
|
|
|
|
+ // 保持 purchase 既有:计价方式=重量且单重为空时的预校验
|
|
|
let singleWeightData = {};
|
|
let singleWeightData = {};
|
|
|
this.form.datasource.forEach((item) => {
|
|
this.form.datasource.forEach((item) => {
|
|
|
if (item.pricingWay == 2 && !item.singleWeight) {
|
|
if (item.pricingWay == 2 && !item.singleWeight) {
|
|
@@ -1694,11 +1891,25 @@
|
|
|
singleWeightData.name + '计价方式为重量,单重不能为空'
|
|
singleWeightData.name + '计价方式为重量,单重不能为空'
|
|
|
);
|
|
);
|
|
|
callback(false);
|
|
callback(false);
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- //开始表单校验
|
|
|
|
|
- this.$refs.form.validate((valid) => {
|
|
|
|
|
|
|
+ // 开始表单校验,并把第一个错误信息提示给用户
|
|
|
|
|
+ this.$refs.form.validate((valid, obj) => {
|
|
|
|
|
+ if (obj) {
|
|
|
|
|
+ let messages = Object.keys(obj).map((key) => obj[key][0]);
|
|
|
|
|
+ if (messages.length > 0) {
|
|
|
|
|
+ this.$message.warning(messages[0].message);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
callback(valid);
|
|
callback(valid);
|
|
|
});
|
|
});
|
|
|
|
|
+ },
|
|
|
|
|
+ // 列头拖拽结束 - 保存列宽到本地缓存
|
|
|
|
|
+ handleHeaderDragend(newWidth, oldWidth, column) {
|
|
|
|
|
+ if (!column.property) return;
|
|
|
|
|
+ const widths = this.getStorage(this.cacheKeyUrl + 'Widths') || {};
|
|
|
|
|
+ widths[column.property] = newWidth;
|
|
|
|
|
+ this.setStorage(this.cacheKeyUrl + 'Widths', widths);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -1752,4 +1963,45 @@
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
font-weight: bold;
|
|
font-weight: bold;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .inventory-table-wrap {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .table-loading-mask {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ z-index: 10;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ background-color: rgba(255, 255, 255, 0.75);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .table-loading-mask .el-icon-loading {
|
|
|
|
|
+ font-size: 30px;
|
|
|
|
|
+ color: #409eff;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .table-loading-text {
|
|
|
|
|
+ margin-top: 10px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 批量追加期间禁止单元格交互:只关掉 .cell 的指针事件,
|
|
|
|
|
+ 滚轮事件仍会落在滚动容器上,所以表格照常可以滚动 */
|
|
|
|
|
+ ::v-deep .time-form-busy .el-table__body-wrapper .cell,
|
|
|
|
|
+ ::v-deep .time-form-busy .el-table__fixed-body-wrapper .cell {
|
|
|
|
|
+ pointer-events: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ::v-deep .time-form-busy .el-table__body-wrapper,
|
|
|
|
|
+ ::v-deep .time-form-busy .el-table__fixed-body-wrapper {
|
|
|
|
|
+ cursor: progress;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|