| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870 |
- <template>
- <el-dialog
- :title="dialogTitle"
- :visible.sync="visible"
- width="1050px"
- :close-on-click-modal="false"
- @close="reset"
- >
- <template v-if="mode === 'records'">
- <ele-pro-table
- ref="recordTable"
- :columns="recordColumns"
- :datasource="recordSource"
- height="450px"
- :pageSize="10"
- >
- <template v-slot:status="{ row }">
- <el-tag :type="statusType(row.verifyStatus)">
- {{ statusText(row.verifyStatus) }}
- </el-tag>
- </template>
- <template v-slot:action="{ row }">
- <el-button type="text" @click="showDetail(row)">详情</el-button>
- <el-button
- v-if="row.verifyStatus === 0"
- type="text"
- :loading="recordActionId === row.id"
- @click="submitRecord(row)"
- >提交</el-button
- >
- <el-button
- v-if="row.verifyStatus === 1"
- type="text"
- :loading="recordActionId === row.id"
- @click="withdrawRecord(row)"
- >撤回</el-button
- >
- </template>
- </ele-pro-table>
- </template>
- <div v-else v-loading="sourceLoading">
- <el-alert
- v-if="mode === 'merge'"
- title="仅允许同仓库、批次号、物品属性、供应商和价格完全一致的库存合批,审批通过后生成新系统批次。"
- type="info"
- :closable="false"
- />
- <el-alert
- v-else
- title="包装明细必须整行分配;锁定明细按锁定单分配,关联包装时自动跟随包装目标。"
- type="info"
- :closable="false"
- />
- <el-alert
- v-if="pendingApproveId"
- title="申请已保存,当前仅续提审批,不会重复生成审核代码。"
- type="warning"
- :closable="false"
- class="saved-alert"
- />
- <div class="section-title">来源库存</div>
- <el-table :data="sources" size="mini" border>
- <el-table-column
- prop="systemBatchNo"
- label="来源系统批次"
- min-width="150"
- />
- <el-table-column prop="batchNo" label="业务批次" />
- <el-table-column prop="warehouseName" label="仓库" />
- <el-table-column prop="categoryCode" label="物品编码" />
- <el-table-column prop="stockCountBase" label="计量数量" />
- <el-table-column prop="packingQuantity" label="包装数量" />
- <el-table-column prop="weight" label="重量" />
- </el-table>
- <template v-if="mode === 'split'">
- <div class="section-title">目标子批次</div>
- <el-table :data="targetRows" size="mini" border>
- <el-table-column label="目标" width="90">
- <template slot-scope="{ row }"
- >子批次 {{ row.targetDetailId }}</template
- >
- </el-table-column>
- <el-table-column label="系统批次号" width="130">
- <template>审批后生成</template>
- </el-table-column>
- <el-table-column label="计量数量">
- <template slot-scope="{ row }">
- <el-input-number
- v-model="row.target.measureQuantity"
- :min="hasPacking ? Number(row.packingMeasureQuantity) : 0"
- :precision="6"
- :step="0.000001"
- :disabled="Boolean(pendingApproveId)"
- size="mini"
- />
- </template>
- </el-table-column>
- <el-table-column label="包装数量">
- <template slot-scope="{ row }">
- <span v-if="hasPacking">{{ row.packageQuantity }}</span>
- <el-input-number
- v-else
- v-model="row.target.packageQuantity"
- :min="0"
- :precision="6"
- :step="0.000001"
- :disabled="Boolean(pendingApproveId)"
- size="mini"
- />
- </template>
- </el-table-column>
- <el-table-column label="重量">
- <template slot-scope="{ row }">
- <el-input-number
- v-model="row.target.weight"
- :min="hasPacking ? Number(row.packingWeight) : 0"
- :precision="6"
- :step="0.000001"
- :disabled="Boolean(pendingApproveId)"
- size="mini"
- />
- </template>
- </el-table-column>
- <el-table-column prop="lockQuantity" label="锁库数量" />
- <el-table-column width="70">
- <template slot-scope="{ row }">
- <el-button
- type="text"
- :disabled="Boolean(pendingApproveId)"
- @click="removeTarget(row.targetDetailId)"
- >删除</el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <el-button
- type="text"
- icon="el-icon-plus"
- :disabled="Boolean(pendingApproveId)"
- @click="addTarget"
- >新增子批次</el-button
- >
- </template>
- <div v-else class="merge-summary">
- 合并后计量数量:{{ total('stockCountBase') }},包装数量:{{
- total('packingQuantity')
- }},重量:{{ total('weight') }};存货周期取来源最长值。
- </div>
- <template v-if="packingRows.length">
- <div class="section-title">整包装分配</div>
- <el-table :data="packingRows" size="mini" border max-height="260">
- <el-table-column
- prop="systemBatchNo"
- label="来源系统批次"
- min-width="140"
- />
- <el-table-column prop="packageNo" label="包装编码" min-width="130" />
- <el-table-column prop="measureQuantity" label="计量数量" />
- <el-table-column prop="packageQuantity" label="包装数量" />
- <el-table-column prop="weight" label="重量" />
- <el-table-column prop="lockCountBase" label="锁库数量" />
- <el-table-column label="目标子批次" min-width="150">
- <template slot-scope="{ row }">
- <el-select
- v-model="row.targetDetailId"
- size="mini"
- :disabled="mode === 'merge' || Boolean(pendingApproveId)"
- placeholder="请选择"
- @change="syncLockTargets"
- >
- <el-option
- v-for="target in targets"
- :key="target.targetDetailId"
- :label="targetLabel(target.targetDetailId)"
- :value="target.targetDetailId"
- />
- </el-select>
- </template>
- </el-table-column>
- </el-table>
- </template>
- <template v-if="lockRows.length">
- <div class="section-title">锁定单分配</div>
- <el-table :data="lockRows" size="mini" border max-height="260">
- <el-table-column prop="orderNo" label="锁定单号" min-width="140" />
- <el-table-column
- prop="lockOrderDetailId"
- label="锁定明细ID"
- min-width="110"
- />
- <el-table-column prop="quantity" label="锁定数量" />
- <el-table-column label="关联包装" min-width="180">
- <template slot-scope="{ row }">{{ packingNames(row) }}</template>
- </el-table-column>
- <el-table-column label="目标子批次" min-width="150">
- <template slot-scope="{ row }">
- <el-select
- v-model="row.targetDetailId"
- size="mini"
- :disabled="
- mode === 'merge' ||
- row.lockedPackingStockIds.length > 0 ||
- Boolean(pendingApproveId)
- "
- placeholder="请选择"
- >
- <el-option
- v-for="target in targets"
- :key="target.targetDetailId"
- :label="targetLabel(target.targetDetailId)"
- :value="target.targetDetailId"
- />
- </el-select>
- <div v-if="row.targetConflict" class="error-text"
- >关联包装不能跨目标</div
- >
- </template>
- </el-table-column>
- </el-table>
- </template>
- <el-input
- v-model="remark"
- type="textarea"
- :rows="2"
- :disabled="Boolean(pendingApproveId)"
- placeholder="申请说明(可选)"
- class="remark"
- />
- </div>
- <span slot="footer">
- <el-button @click="visible = false">取 消</el-button>
- <el-button
- v-if="mode !== 'records'"
- type="primary"
- :loading="saving"
- :disabled="sourceLoading"
- @click="submitApply"
- >{{ pendingApproveId ? '继续提交审批' : '提交审批' }}</el-button
- >
- </span>
- <el-dialog
- title="批次变更申请详情"
- :visible.sync="detailVisible"
- width="850px"
- append-to-body
- >
- <el-descriptions
- v-loading="detailLoading"
- :column="3"
- border
- size="small"
- >
- <el-descriptions-item label="审核代码">{{
- detailData.changeNo || '-'
- }}</el-descriptions-item>
- <el-descriptions-item label="变更类型">{{
- detailData.changeType === 1 ? '拆批' : '合批'
- }}</el-descriptions-item>
- <el-descriptions-item label="审批状态">{{
- statusText(detailData.verifyStatus)
- }}</el-descriptions-item>
- <el-descriptions-item label="仓库">{{
- detailData.warehouseName || '-'
- }}</el-descriptions-item>
- <el-descriptions-item label="申请时间">{{
- detailData.createTime || '-'
- }}</el-descriptions-item>
- <el-descriptions-item label="说明">{{
- detailData.remark || '-'
- }}</el-descriptions-item>
- </el-descriptions>
- <el-table
- :data="detailData.details || []"
- size="mini"
- border
- class="detail-table"
- >
- <el-table-column prop="sourceStockBatchId" label="来源批次ID" />
- <el-table-column
- prop="systemBatchNo"
- label="来源系统批次"
- min-width="140"
- />
- <el-table-column
- prop="targetSystemBatchNo"
- label="目标系统批次"
- min-width="140"
- />
- <el-table-column prop="measureQuantity" label="计量数量" />
- <el-table-column prop="packageQuantity" label="包装数量" />
- <el-table-column prop="weight" label="重量" />
- <el-table-column prop="lockQuantity" label="锁库数量" />
- <el-table-column
- prop="packingStockIds"
- label="包装库存分配"
- min-width="180"
- show-overflow-tooltip
- />
- <el-table-column
- prop="lockAllocations"
- label="锁定单分配"
- min-width="220"
- show-overflow-tooltip
- />
- </el-table>
- </el-dialog>
- </el-dialog>
- </template>
- <script>
- import BigNumber from 'bignumber.js';
- import stockBatchChange from '@/api/warehouseManagement/stockBatchChange';
- function decimal(value) {
- const result = new BigNumber(value == null || value === '' ? 0 : value);
- return result.isFinite() ? result : new BigNumber(0);
- }
- function decimalValue(value) {
- return decimal(value).decimalPlaces(6).toFixed(6);
- }
- export default {
- data() {
- return {
- visible: false,
- mode: 'split',
- sources: [],
- targets: [],
- remark: '',
- saving: false,
- sourceLoading: false,
- detailVisible: false,
- detailLoading: false,
- detailData: {},
- pendingApproveId: null,
- recordActionId: null
- };
- },
- computed: {
- dialogTitle() {
- if (this.mode === 'records') return '批次变更申请记录';
- return this.mode === 'split' ? '库存拆批申请' : '库存合批申请';
- },
- packingRows() {
- return this.sources.reduce(
- (rows, source) => rows.concat(source.packingDetails || []),
- []
- );
- },
- lockRows() {
- return this.sources.reduce(
- (rows, source) => rows.concat(source.lockDetails || []),
- []
- );
- },
- hasPacking() {
- return this.packingRows.length > 0;
- },
- targetRows() {
- return this.targets.map((target) => {
- const packages = this.packingRows.filter(
- (row) => row.targetDetailId === target.targetDetailId
- );
- const locks = this.lockRows.filter(
- (row) => row.targetDetailId === target.targetDetailId
- );
- const packingMeasureQuantity = this.sum(packages, 'measureQuantity');
- const packingWeight = this.sum(packages, 'weight');
- return {
- target,
- targetDetailId: target.targetDetailId,
- packingMeasureQuantity,
- packingWeight,
- measureQuantity: target.measureQuantity,
- packageQuantity: this.hasPacking
- ? this.sum(packages, 'packageQuantity')
- : target.packageQuantity,
- weight: target.weight,
- lockQuantity: this.sum(locks, 'quantity')
- };
- });
- },
- recordColumns() {
- return [
- { prop: 'changeNo', label: '审核代码' },
- {
- prop: 'changeType',
- label: '类型',
- formatter: (row) => (row.changeType === 1 ? '拆批' : '合批')
- },
- { prop: 'verifyStatus', slot: 'status', label: '审批状态' },
- { prop: 'createTime', label: '申请时间' },
- {
- columnKey: 'action',
- slot: 'action',
- label: '操作',
- width: 140,
- fixed: 'right'
- }
- ];
- }
- },
- methods: {
- async open(mode, rows) {
- this.mode = mode;
- this.pendingApproveId = null;
- this.visible = true;
- if (mode === 'records') return;
- this.sourceLoading = true;
- try {
- const selectedRows = rows || [];
- const ids = selectedRows.map((row) => row.stockBatchId || row.id);
- const result = await stockBatchChange.sourceDetails(ids);
- const sourceList = Array.isArray(result)
- ? result
- : result.sources || [];
- this.sources = sourceList.map((source) => {
- const selected =
- selectedRows.find(
- (row) =>
- String(row.stockBatchId || row.id) ===
- String(source.stockBatchId)
- ) || {};
- return this.normalizeSource({ ...selected, ...source });
- });
- this.targets =
- mode === 'merge'
- ? [this.createTarget(1)]
- : [this.createTarget(1), this.createTarget(2)];
- if (mode === 'merge') this.assignMergeTarget();
- } catch (error) {
- this.visible = false;
- this.$message.error(error.message || '批次来源详情加载失败');
- } finally {
- this.sourceLoading = false;
- }
- },
- normalizeSource(source) {
- const packingDetails = (source.packingDetails || []).map((row) => ({
- ...row,
- sourceStockBatchId: source.stockBatchId,
- systemBatchNo: source.systemBatchNo,
- targetDetailId: null
- }));
- const lockDetails = (source.lockDetails || []).map((row) => ({
- ...row,
- sourceStockBatchId: source.stockBatchId,
- lockedPackingStockIds: this.parseIdList(row.lockedPackingStockIds),
- targetDetailId: null,
- targetConflict: false
- }));
- return {
- ...source,
- stockCountBase:
- source.stockCountBase == null
- ? source.measureQuantity
- : source.stockCountBase,
- packingQuantity:
- source.packingQuantity == null
- ? source.packageQuantity
- : source.packingQuantity,
- packingDetails,
- lockDetails
- };
- },
- parseIdList(value) {
- if (Array.isArray(value)) return value.map(String);
- if (!value) return [];
- try {
- const parsed = JSON.parse(value);
- return Array.isArray(parsed) ? parsed.map(String) : [];
- } catch (error) {
- return [];
- }
- },
- createTarget(targetDetailId) {
- return {
- targetDetailId,
- measureQuantity: 0,
- packageQuantity: 0,
- weight: 0
- };
- },
- reset() {
- this.sources = [];
- this.targets = [];
- this.remark = '';
- this.detailVisible = false;
- this.detailData = {};
- this.pendingApproveId = null;
- },
- addTarget() {
- const maxId = this.targets.reduce(
- (max, target) => Math.max(max, target.targetDetailId),
- 0
- );
- this.targets.push(this.createTarget(maxId + 1));
- },
- removeTarget(targetDetailId) {
- if (this.targets.length <= 2)
- return this.$message.warning('拆批至少保留两个目标子批次');
- if (
- this.packingRows.some(
- (row) => row.targetDetailId === targetDetailId
- ) ||
- this.lockRows.some((row) => row.targetDetailId === targetDetailId)
- ) {
- return this.$message.warning('该目标已有包装或锁定单分配,不能删除');
- }
- this.targets = this.targets.filter(
- (target) => target.targetDetailId !== targetDetailId
- );
- },
- assignMergeTarget() {
- this.packingRows.forEach((row) => {
- row.targetDetailId = 1;
- });
- this.lockRows.forEach((row) => {
- row.targetDetailId = 1;
- });
- this.syncTargetPackingMinimums();
- },
- /** 包装目标变化后,关联包装的锁定明细必须自动落到同一目标。 */
- syncLockTargets() {
- const targetByPackingId = this.packingRows.reduce((map, row) => {
- map[String(row.packingStockId)] = row.targetDetailId;
- return map;
- }, {});
- this.lockRows.forEach((lock) => {
- if (!lock.lockedPackingStockIds.length) return;
- const targetIds = lock.lockedPackingStockIds
- .map((id) => targetByPackingId[String(id)])
- .filter(Boolean);
- const distinct = Array.from(new Set(targetIds));
- lock.targetConflict = distinct.length > 1;
- lock.targetDetailId =
- !lock.targetConflict &&
- targetIds.length === lock.lockedPackingStockIds.length
- ? distinct[0]
- : null;
- });
- this.syncTargetPackingMinimums();
- },
- /** 包装变更后同步目标下限,超出包装汇总的部分作为散量。 */
- syncTargetPackingMinimums() {
- this.targets.forEach((target) => {
- const packages = this.packingRows.filter(
- (row) => row.targetDetailId === target.targetDetailId
- );
- const measureMinimum = decimal(this.sum(packages, 'measureQuantity'));
- const weightMinimum = decimal(this.sum(packages, 'weight'));
- if (decimal(target.measureQuantity).lt(measureMinimum)) {
- target.measureQuantity = Number(measureMinimum.toFixed(6));
- }
- if (decimal(target.weight).lt(weightMinimum)) {
- target.weight = Number(weightMinimum.toFixed(6));
- }
- });
- },
- targetLabel(id) {
- return this.mode === 'merge' ? '合并目标' : `子批次 ${id}`;
- },
- packingNames(lock) {
- if (!lock.lockedPackingStockIds.length) return '-';
- return lock.lockedPackingStockIds
- .map((id) => {
- const packing = this.packingRows.find(
- (row) => String(row.packingStockId) === String(id)
- );
- return packing ? packing.packageNo : id;
- })
- .join('、');
- },
- sum(rows, key) {
- return rows
- .reduce((sum, row) => sum.plus(decimal(row[key])), new BigNumber(0))
- .toFixed(6);
- },
- total(key) {
- return this.sum(this.sources, key);
- },
- statusText(value) {
- return (
- { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回', 4: '已撤回' }[
- value
- ] || '-'
- );
- },
- statusType(value) {
- return value === 2 ? 'success' : value === 3 ? 'danger' : 'info';
- },
- recordSource({ page, limit, where }) {
- return stockBatchChange.page({ ...where, pageNum: page, size: limit });
- },
- async showDetail(row) {
- this.detailVisible = true;
- this.detailLoading = true;
- this.detailData = {};
- try {
- this.detailData = (await stockBatchChange.detail(row.id)) || {};
- } catch (error) {
- this.detailVisible = false;
- this.$message.error(error.message || '申请详情加载失败');
- } finally {
- this.detailLoading = false;
- }
- },
- async submitRecord(row) {
- this.recordActionId = row.id;
- try {
- await stockBatchChange.submit({ id: row.id });
- this.$message.success('申请已提交审批');
- this.reloadRecords();
- } finally {
- this.recordActionId = null;
- }
- },
- async withdrawRecord(row) {
- try {
- await this.$confirm('确认撤回该批次变更申请?', '提示', {
- type: 'warning'
- });
- } catch (error) {
- return;
- }
- this.recordActionId = row.id;
- try {
- await stockBatchChange.withdraw({ id: row.id });
- this.$message.success('申请已撤回');
- this.reloadRecords();
- } finally {
- this.recordActionId = null;
- }
- },
- reloadRecords() {
- this.$nextTick(() => {
- if (this.$refs.recordTable) this.$refs.recordTable.reload();
- });
- },
- getBatchAttributeDifferences() {
- const attributes = [
- { key: 'warehouseId', label: '仓库', displayKey: 'warehouseName' },
- { key: 'batchNo', label: '业务批次号' },
- { key: 'categoryCode', label: '物品编码' },
- { key: 'categoryName', label: '物品名称' },
- { key: 'specification', label: '规格' },
- { key: 'categoryModel', label: '型号' },
- { key: 'brandNum', label: '牌号' },
- { key: 'colorKey', label: '颜色' },
- { key: 'modelKey', label: '机型' },
- { key: 'unitPrice', label: '单价' },
- { key: 'supplierId', label: '供应商', displayKey: 'supplierName' }
- ];
- return attributes.reduce((differences, attribute) => {
- const values = this.sources.map((row) =>
- String(row[attribute.key] == null ? '' : row[attribute.key])
- );
- if (new Set(values).size < 2) return differences;
- const displayValues = Array.from(
- new Set(
- this.sources.map((row) => {
- const displayValue = attribute.displayKey
- ? row[attribute.displayKey]
- : null;
- const value =
- displayValue == null || displayValue === ''
- ? row[attribute.key]
- : displayValue;
- return value == null || value === '' ? '未填写' : value;
- })
- )
- );
- differences.push(`${attribute.label}(${displayValues.join('、')})`);
- return differences;
- }, []);
- },
- validateAllocation() {
- if (this.mode === 'split' && this.targets.length < 2)
- return '拆批至少需要两个目标子批次';
- if (this.packingRows.some((row) => !row.targetDetailId))
- return '请为每条包装明细选择目标子批次';
- if (
- this.lockRows.some((row) => row.targetConflict || !row.targetDetailId)
- )
- return '请完成锁定单分配,关联包装必须属于同一目标';
- if (this.targetRows.some((row) => decimal(row.measureQuantity).lte(0)))
- return '各目标子批次计量数量必须大于零';
- if (
- this.targetRows.some((row) =>
- decimal(row.lockQuantity).gt(decimal(row.measureQuantity))
- )
- )
- return '目标子批次锁库数量不能大于计量数量';
- if (
- this.hasPacking &&
- this.targetRows.some(
- (row) =>
- decimal(row.measureQuantity).lt(
- decimal(row.packingMeasureQuantity)
- ) || decimal(row.weight).lt(decimal(row.packingWeight))
- )
- )
- return '目标计量数量和重量不能小于所选整包装汇总';
- const source = this.sources[0];
- if (this.mode === 'split') {
- if (
- !decimal(this.sum(this.targetRows, 'measureQuantity')).eq(
- decimal(source.stockCountBase)
- ) ||
- !decimal(this.sum(this.targetRows, 'packageQuantity')).eq(
- decimal(source.packingQuantity)
- ) ||
- !decimal(this.sum(this.targetRows, 'weight')).eq(
- decimal(source.weight)
- )
- ) {
- return '目标计量数量、包装数量和重量合计必须等于来源库存';
- }
- }
- return '';
- },
- buildLockAllocations(sourceStockBatchId, targetDetailId) {
- return JSON.stringify(
- this.lockRows
- .filter(
- (row) =>
- row.sourceStockBatchId === sourceStockBatchId &&
- row.targetDetailId === targetDetailId
- )
- .map((row) => ({
- lockOrderDetailId: row.lockOrderDetailId,
- quantity: decimalValue(row.quantity)
- }))
- );
- },
- buildPackingIds(sourceStockBatchId, targetDetailId) {
- return JSON.stringify(
- this.packingRows
- .filter(
- (row) =>
- row.sourceStockBatchId === sourceStockBatchId &&
- row.targetDetailId === targetDetailId
- )
- .map((row) => row.packingStockId)
- );
- },
- buildSplitDetails() {
- const source = this.sources[0];
- return this.targetRows.map((row) => ({
- detailType: 2,
- sourceStockBatchId: source.stockBatchId,
- targetDetailId: row.targetDetailId,
- batchNo: source.batchNo,
- systemBatchNo: source.systemBatchNo,
- measureQuantity: decimalValue(row.measureQuantity),
- packageQuantity: decimalValue(row.packageQuantity),
- weight: decimalValue(row.weight),
- lockQuantity: decimalValue(row.lockQuantity),
- packingStockIds: this.buildPackingIds(
- source.stockBatchId,
- row.targetDetailId
- ),
- lockAllocations: this.buildLockAllocations(
- source.stockBatchId,
- row.targetDetailId
- )
- }));
- },
- buildMergeDetails() {
- return this.sources.map((source) => ({
- detailType: 1,
- sourceStockBatchId: source.stockBatchId,
- targetDetailId: 1,
- batchNo: source.batchNo,
- systemBatchNo: source.systemBatchNo,
- measureQuantity: decimalValue(source.stockCountBase),
- packageQuantity: decimalValue(source.packingQuantity),
- weight: decimalValue(source.weight),
- lockQuantity: this.sum(source.lockDetails || [], 'quantity'),
- packingStockIds: this.buildPackingIds(source.stockBatchId, 1),
- lockAllocations: this.buildLockAllocations(source.stockBatchId, 1)
- }));
- },
- async submitApply() {
- if (this.pendingApproveId) return this.continueSubmit();
- if (!this.sources.length)
- return this.$message.warning('请选择库存批次');
- if (this.mode === 'merge' && this.sources.length < 2)
- return this.$message.warning('合批至少选择两条库存');
- const attributeDifferences = this.getBatchAttributeDifferences();
- if (this.mode === 'merge' && attributeDifferences.length)
- return this.$message.warning(
- `所选库存无法合批,以下信息不一致:${attributeDifferences.join(
- ';'
- )}。`
- );
- const allocationError = this.validateAllocation();
- if (allocationError) return this.$message.warning(allocationError);
- this.saving = true;
- try {
- const source = this.sources[0];
- const saved = await stockBatchChange.save({
- changeType: this.mode === 'split' ? 1 : 2,
- warehouseId: source.warehouseId,
- warehouseName: source.warehouseName,
- remark: this.remark,
- details:
- this.mode === 'split'
- ? this.buildSplitDetails()
- : this.buildMergeDetails()
- });
- this.pendingApproveId = saved.id || saved;
- await stockBatchChange.submit({ id: this.pendingApproveId });
- this.pendingApproveId = null;
- this.$message.success('申请已提交,审批通过后生效');
- this.visible = false;
- this.$emit('success');
- } finally {
- this.saving = false;
- }
- },
- async continueSubmit() {
- this.saving = true;
- try {
- await stockBatchChange.submit({ id: this.pendingApproveId });
- this.pendingApproveId = null;
- this.$message.success('申请已提交,审批通过后生效');
- this.visible = false;
- this.$emit('success');
- } finally {
- this.saving = false;
- }
- }
- }
- };
- </script>
- <style scoped>
- .section-title {
- margin: 14px 0 8px;
- font-weight: 600;
- }
- .merge-summary {
- padding: 14px 0 0;
- color: #606266;
- }
- .remark {
- margin-top: 14px;
- }
- .saved-alert {
- margin-top: 10px;
- }
- .error-text {
- margin-top: 4px;
- color: #f56c6c;
- font-size: 12px;
- }
- .detail-table {
- margin-top: 14px;
- }
- </style>
|