|
|
@@ -116,331 +116,11 @@
|
|
|
<ViewKvTable :fields="freightViewFields" mode="sept" />
|
|
|
</section>
|
|
|
|
|
|
- <section class="pl-plan-form-section pl-plan-form-section--plain">
|
|
|
- <div class="pl-plan-form-section-head">
|
|
|
- <h3 class="pl-plan-form-section-title">司机回传信息</h3>
|
|
|
- <div v-if="feedbackEditable" class="pl-plan-form-section-actions">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- class="btn-op-item"
|
|
|
- :disabled="!canEditDriverFeedback"
|
|
|
- @click="openDriverFeedbackDialog"
|
|
|
- >
|
|
|
- <el-icon><Edit /></el-icon>
|
|
|
- {{ hasDriverFeedback ? '编辑' : '补充' }}
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- v-if="arrangeDriverUrl"
|
|
|
- class="btn-op-item"
|
|
|
- @click="copyArrangeDriverUrl"
|
|
|
- >
|
|
|
- <el-icon><CopyDocument /></el-icon>
|
|
|
- 复制司机链接
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <p v-if="!hasDriverFeedback" class="pl-plan-form-empty-hint">暂无司机回传</p>
|
|
|
- <template v-else>
|
|
|
- <div class="view-subtable-wrap">
|
|
|
- <el-table :data="driverFeedbackTableRows" class="view-subtable list-table" stripe>
|
|
|
- <el-table-column
|
|
|
- type="index"
|
|
|
- label="序号"
|
|
|
- min-width="68"
|
|
|
- align="center"
|
|
|
- header-align="center"
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="productName"
|
|
|
- label="产品"
|
|
|
- min-width="140"
|
|
|
- show-overflow-tooltip
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="actualQty"
|
|
|
- label="回传数量"
|
|
|
- min-width="100"
|
|
|
- align="right"
|
|
|
- >
|
|
|
- <template #default="{ row }">
|
|
|
- {{ row.actualQty ?? '—' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="品牌数量" min-width="180" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- {{ row.brandSummary || '—' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- <div class="pl-plan-io-site-meta">
|
|
|
- <div class="pl-plan-io-site-meta__item">
|
|
|
- <p class="pl-plan-form-empty-hint" style="margin-bottom: 6px">回传时间</p>
|
|
|
- <p class="pl-plan-view-remark">{{ displayFeedbackTime }}</p>
|
|
|
- </div>
|
|
|
- <div class="pl-plan-io-site-meta__item">
|
|
|
- <p class="pl-plan-form-empty-hint" style="margin-bottom: 6px">回传备注</p>
|
|
|
- <p class="pl-plan-view-remark">{{ transport?.feedbackRemark || '—' }}</p>
|
|
|
- </div>
|
|
|
- <div class="pl-plan-io-site-meta__item">
|
|
|
- <p class="pl-plan-form-empty-hint" style="margin-bottom: 6px">回传附件</p>
|
|
|
- <AttachmentUploadField
|
|
|
- v-if="feedbackImageCsv"
|
|
|
- :model-value="feedbackImageCsv"
|
|
|
- disabled
|
|
|
- compact
|
|
|
- />
|
|
|
- <span v-else class="pl-plan-feedback-table__empty-cell">—</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </section>
|
|
|
-
|
|
|
- <section class="pl-plan-form-section pl-plan-form-section--plain">
|
|
|
- <div class="pl-plan-form-section-head">
|
|
|
- <h3 class="pl-plan-form-section-title">
|
|
|
- {{ ioOrder ? (ioOrder.kind === 'inbound' ? '入库单回传' : '出库单回传') : '现场回传信息' }}
|
|
|
- </h3>
|
|
|
- <div v-if="ioOrder" class="pl-plan-form-section-actions">
|
|
|
- <el-button
|
|
|
- v-if="feedbackEditable && ioOrder.status !== 'voided'"
|
|
|
- type="primary"
|
|
|
- class="btn-op-item"
|
|
|
- @click="openIoOrderEditDialog"
|
|
|
- >
|
|
|
- <el-icon><Edit /></el-icon>
|
|
|
- {{ ioOrderHasFeedback ? '编辑' : '补充' }}
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- v-if="ioOrder.siteUrl"
|
|
|
- class="btn-op-item"
|
|
|
- @click="copyArrangeSiteUrl(ioOrder.siteUrl)"
|
|
|
- >
|
|
|
- <el-icon><CopyDocument /></el-icon>
|
|
|
- 复制现场链接
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <!-- 新链路:入库/出库单权威展示 -->
|
|
|
- <template v-if="ioOrder">
|
|
|
- <ViewKvTable :fields="ioOrderViewFields" mode="quint" />
|
|
|
- <div class="view-subtable-wrap" style="margin-top: 12px">
|
|
|
- <el-table :data="ioOrder.items || []" class="view-subtable list-table" stripe>
|
|
|
- <el-table-column type="index" label="序号" min-width="68" align="center" />
|
|
|
- <el-table-column prop="productNo" label="产品编码" min-width="110" show-overflow-tooltip />
|
|
|
- <el-table-column prop="productName" label="产品名称" min-width="120" show-overflow-tooltip />
|
|
|
- <el-table-column prop="specification" label="型号/规格" min-width="100" show-overflow-tooltip />
|
|
|
- <el-table-column label="单位" min-width="72" align="center">
|
|
|
- <template #default="{ row }">{{ row.unitLabel || row.unit || '—' }}</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="planQty" label="计划数量" min-width="100" align="right" />
|
|
|
- <el-table-column prop="actualQty" label="实际数量" min-width="100" align="right" />
|
|
|
- <el-table-column label="品牌数量" min-width="180" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- {{ resolveSiteBrandSummary(row) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="packageSpec" label="包装规格" min-width="110" show-overflow-tooltip />
|
|
|
- <el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- <div class="pl-plan-io-site-meta">
|
|
|
- <div class="pl-plan-io-site-meta__item">
|
|
|
- <p class="pl-plan-form-empty-hint" style="margin-bottom: 6px">现场备注</p>
|
|
|
- <p class="pl-plan-view-remark">{{ ioOrder.siteFeedbackRemark || '—' }}</p>
|
|
|
- </div>
|
|
|
- <div class="pl-plan-io-site-meta__item">
|
|
|
- <p class="pl-plan-form-empty-hint" style="margin-bottom: 6px">现场附件</p>
|
|
|
- <AttachmentUploadField
|
|
|
- v-if="ioOrderImageCsv"
|
|
|
- :model-value="ioOrderImageCsv"
|
|
|
- disabled
|
|
|
- compact
|
|
|
- />
|
|
|
- <span v-else class="pl-plan-feedback-table__empty-cell">—</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <!-- 旧链路:按产品现场 link -->
|
|
|
- <template v-else>
|
|
|
- <p v-if="!siteInventoryRows.length && !loading" class="pl-plan-form-empty-hint">
|
|
|
- 暂无现场链接
|
|
|
- </p>
|
|
|
- <div v-else class="view-subtable-wrap">
|
|
|
- <el-table :data="siteFeedbackTableRows" class="view-subtable list-table" stripe>
|
|
|
- <el-table-column type="index" label="序号" min-width="68" align="center" />
|
|
|
- <el-table-column prop="productName" label="产品" min-width="140" show-overflow-tooltip />
|
|
|
- <el-table-column prop="siteFeedbackTime" label="保存时间" min-width="160" align="center" />
|
|
|
- <el-table-column label="业务类型" min-width="100" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag
|
|
|
- v-if="row.businessType && row.businessType !== '—'"
|
|
|
- :type="businessTypeTagType(row.businessType)"
|
|
|
- size="small"
|
|
|
- >
|
|
|
- {{ row.businessType }}
|
|
|
- </el-tag>
|
|
|
- <span v-else class="pl-plan-feedback-table__empty-cell">—</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="siteFeedbackRemark" label="现场备注" min-width="160" show-overflow-tooltip />
|
|
|
- <el-table-column label="现场附件" min-width="140" align="center">
|
|
|
- <template #default="{ row }">
|
|
|
- <div v-if="row.siteFeedbackImages" class="pl-plan-feedback-table__attachment">
|
|
|
- <AttachmentUploadField :model-value="row.siteFeedbackImages" disabled compact />
|
|
|
- </div>
|
|
|
- <span v-else class="pl-plan-feedback-table__empty-cell">—</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" min-width="240" align="center" fixed="right">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-button
|
|
|
- v-if="feedbackEditable"
|
|
|
- class="btn-op-item"
|
|
|
- @click="openSiteFeedbackDialog(row)"
|
|
|
- >
|
|
|
- <el-icon><Edit /></el-icon>
|
|
|
- {{ rowHasSiteFeedback(row) ? '编辑' : '补充' }}
|
|
|
- </el-button>
|
|
|
- <el-button class="btn-op-item" @click="copySiteSummaryForRow(row.sourceRow)">
|
|
|
- <el-icon><CopyDocument /></el-icon>
|
|
|
- 复制给现场
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </section>
|
|
|
-
|
|
|
- <section class="pl-plan-form-section pl-plan-form-section--plain">
|
|
|
- <h3 class="pl-plan-form-section-title">地磅单信息</h3>
|
|
|
- <p v-if="!dibangPlanRows.length && !loading" class="pl-plan-form-empty-hint"
|
|
|
- >暂无关联地磅单</p
|
|
|
- >
|
|
|
- <div
|
|
|
- v-for="(row, idx) in dibangPlanRows"
|
|
|
- :key="row.link.dbInventoryId || idx"
|
|
|
- class="pl-plan-dibang-block"
|
|
|
- >
|
|
|
- <div class="pl-plan-dibang-head">
|
|
|
- <span class="pl-plan-dibang-product">{{
|
|
|
- row.link.productName || row.inventory?.product || '—'
|
|
|
- }}</span>
|
|
|
- <div class="pl-plan-dibang-head-actions">
|
|
|
- <el-tag
|
|
|
- v-if="row.inventory?.status"
|
|
|
- :type="inventoryStatusTagType(row.inventory.status)"
|
|
|
- size="small"
|
|
|
- >
|
|
|
- {{ row.inventory.status }}
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <ViewKvTable :fields="dibangBasicViewFields(row)" mode="hex" />
|
|
|
-
|
|
|
- <template v-if="row.weighing">
|
|
|
- <h5 class="pl-plan-dibang-weighing-title">地磅单</h5>
|
|
|
- <div class="view-subtable-wrap">
|
|
|
- <el-table :data="[row.weighing]" class="view-subtable list-table" stripe>
|
|
|
- <el-table-column type="index" label="序号" min-width="68" align="center" header-align="center" />
|
|
|
- <el-table-column
|
|
|
- prop="batchNo"
|
|
|
- label="出入库单号"
|
|
|
- min-width="130"
|
|
|
- align="center"
|
|
|
- />
|
|
|
- <el-table-column prop="vehicleNo" label="车牌号" min-width="110" align="center" />
|
|
|
- <el-table-column
|
|
|
- prop="cargoName"
|
|
|
- label="货物名称"
|
|
|
- min-width="120"
|
|
|
- align="center"
|
|
|
- show-overflow-tooltip
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="businessType"
|
|
|
- label="业务类型"
|
|
|
- min-width="100"
|
|
|
- align="center"
|
|
|
- >
|
|
|
- <template #default="{ row: weighingRow }">
|
|
|
- <el-tag :type="businessTypeTagType(weighingRow.businessType)" size="small">
|
|
|
- {{ weighingRow.businessType }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column
|
|
|
- prop="supplier"
|
|
|
- label="供货单位"
|
|
|
- min-width="140"
|
|
|
- align="center"
|
|
|
- show-overflow-tooltip
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="consignee"
|
|
|
- label="收货单位"
|
|
|
- min-width="140"
|
|
|
- align="center"
|
|
|
- show-overflow-tooltip
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="grossWeight"
|
|
|
- label="毛重(kg)"
|
|
|
- min-width="100"
|
|
|
- align="right"
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="grossWeightTime"
|
|
|
- label="毛重时间"
|
|
|
- min-width="160"
|
|
|
- align="center"
|
|
|
- >
|
|
|
- <template #default="{ row: weighingRow }">
|
|
|
- {{ formatDateTime(weighingRow.grossWeightTime) || '—' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column
|
|
|
- prop="tareWeight"
|
|
|
- label="皮重(kg)"
|
|
|
- min-width="100"
|
|
|
- align="right"
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="tareWeightTime"
|
|
|
- label="皮重时间"
|
|
|
- min-width="160"
|
|
|
- align="center"
|
|
|
- >
|
|
|
- <template #default="{ row: weighingRow }">
|
|
|
- {{ formatDateTime(weighingRow.tareWeightTime) || '—' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="deduction" label="扣杂(kg)" min-width="100" align="right">
|
|
|
- <template #default="{ row: weighingRow }">
|
|
|
- {{ weighingRow.deduction ?? 0 }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column
|
|
|
- prop="netWeight"
|
|
|
- label="净重(kg)"
|
|
|
- min-width="100"
|
|
|
- align="right"
|
|
|
- />
|
|
|
- <el-table-column
|
|
|
- prop="remarks"
|
|
|
- label="备注"
|
|
|
- min-width="140"
|
|
|
- align="center"
|
|
|
- show-overflow-tooltip
|
|
|
- />
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <p v-else class="pl-plan-form-empty-hint pl-plan-dibang-pending">尚未关联地磅单</p>
|
|
|
- </div>
|
|
|
- </section>
|
|
|
+ <PlPlanTransportFeedbackBlocks
|
|
|
+ v-if="!feedbackMirrorTo"
|
|
|
+ v-bind="feedbackBlocksBind"
|
|
|
+ v-on="feedbackBlocksOn"
|
|
|
+ />
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="合同信息" name="contract">
|
|
|
@@ -498,6 +178,11 @@
|
|
|
</el-tab-pane>
|
|
|
</el-tabs>
|
|
|
|
|
|
+ <!-- defer:目标在同次渲染的兄弟节点(归档左侧)时,等 flush 后再解析,避免 emitsOptions 空引用 -->
|
|
|
+ <Teleport v-if="feedbackMirrorTo" :to="feedbackMirrorTo" defer>
|
|
|
+ <PlPlanTransportFeedbackBlocks v-bind="feedbackBlocksBind" v-on="feedbackBlocksOn" />
|
|
|
+ </Teleport>
|
|
|
+
|
|
|
<ContractDetailDialog v-if="contractDetailDialogActive" ref="contractDetailRef" hide-product-price />
|
|
|
<PlPlanDriverFeedbackDialog
|
|
|
v-model:visible="driverFeedbackDialogVisible"
|
|
|
@@ -525,56 +210,100 @@
|
|
|
<el-dialog
|
|
|
v-model="ioOrderEditVisible"
|
|
|
:title="ioOrder?.kind === 'inbound' ? '编辑入库单回传' : '编辑出库单回传'"
|
|
|
- width="1080px"
|
|
|
+ width="1280px"
|
|
|
align-center
|
|
|
destroy-on-close
|
|
|
- class="dialog-form-layout"
|
|
|
+ append-to-body
|
|
|
+ class="dialog-form-layout pl-io-order-edit-dialog"
|
|
|
+ modal-class="order-module-dialog-mask"
|
|
|
:close-on-click-modal="false"
|
|
|
+ style="backdrop-filter: none"
|
|
|
>
|
|
|
- <el-form v-if="ioOrderEditForm" label-position="top">
|
|
|
- <el-form-item label="产品实际数量">
|
|
|
- <div
|
|
|
- v-for="(row, idx) in ioOrderEditForm.items || []"
|
|
|
- :key="row.id"
|
|
|
- class="pl-io-order-edit-item"
|
|
|
- >
|
|
|
- <div class="pl-io-order-edit-item__head">
|
|
|
- <span class="pl-io-order-edit-item__name">{{ row.productName || '产品' }}</span>
|
|
|
- <span class="pl-io-order-edit-item__badge">
|
|
|
- 产品 {{ idx + 1 }}/{{ ioOrderEditForm.items?.length || 0 }}
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <el-table :data="[row]" size="small" border class="pl-io-order-edit-item__table">
|
|
|
- <el-table-column prop="productNo" label="产品编码" min-width="110" show-overflow-tooltip />
|
|
|
- <el-table-column prop="specification" label="型号/规格" min-width="100" show-overflow-tooltip />
|
|
|
- <el-table-column label="单位" min-width="72" align="center">
|
|
|
- <template #default="{ row: itemRow }">{{ itemRow.unitLabel || itemRow.unit || '—' }}</template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="planQty" label="计划数量" min-width="100" align="right" />
|
|
|
- <el-table-column label="实际数量" min-width="120">
|
|
|
- <template #default="{ row: itemRow }">
|
|
|
+ <el-form v-if="ioOrderEditForm" label-position="top" class="pl-io-order-edit-form">
|
|
|
+ <el-form-item label="产品实际数量" class="pl-io-order-edit-products">
|
|
|
+ <div class="pl-io-order-edit-products__list">
|
|
|
+ <div
|
|
|
+ v-for="(row, idx) in ioOrderEditForm.items || []"
|
|
|
+ :key="row.id"
|
|
|
+ class="pl-io-order-edit-item"
|
|
|
+ >
|
|
|
+ <div class="pl-io-order-edit-item__head">
|
|
|
+ <div class="pl-io-order-edit-item__title">
|
|
|
+ <span class="pl-io-order-edit-item__name">{{ row.productName || '产品' }}</span>
|
|
|
+ <span v-if="row.productNo" class="pl-io-order-edit-item__no">{{
|
|
|
+ row.productNo
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <span class="pl-io-order-edit-item__badge">
|
|
|
+ 产品 {{ idx + 1 }}/{{ ioOrderEditForm.items?.length || 0 }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__meta">
|
|
|
+ <div class="pl-io-order-edit-item__meta-item">
|
|
|
+ <span class="pl-io-order-edit-item__meta-label">规格型号</span>
|
|
|
+ <span class="pl-io-order-edit-item__meta-value">{{
|
|
|
+ row.specification || '—'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__meta-item">
|
|
|
+ <span class="pl-io-order-edit-item__meta-label">产品分类</span>
|
|
|
+ <span class="pl-io-order-edit-item__meta-value">{{
|
|
|
+ row.categoryName || '—'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__meta-item">
|
|
|
+ <span class="pl-io-order-edit-item__meta-label">单位</span>
|
|
|
+ <span class="pl-io-order-edit-item__meta-value">{{
|
|
|
+ row.unitLabel || row.unit || '—'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__meta-item">
|
|
|
+ <span class="pl-io-order-edit-item__meta-label">包装规格</span>
|
|
|
+ <span class="pl-io-order-edit-item__meta-value">{{
|
|
|
+ row.packageSpec || '—'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__meta-item">
|
|
|
+ <span class="pl-io-order-edit-item__meta-label">送货方式</span>
|
|
|
+ <span class="pl-io-order-edit-item__meta-value">{{
|
|
|
+ getDeliveryMethodLabel(row.deliveryMethod)
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__qty-row">
|
|
|
+ <div class="pl-io-order-edit-item__qty">
|
|
|
+ <span class="pl-io-order-edit-item__qty-label">计划数量</span>
|
|
|
+ <span class="pl-io-order-edit-item__qty-value">{{
|
|
|
+ row.planQty != null ? row.planQty : '—'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="pl-io-order-edit-item__qty pl-io-order-edit-item__qty--actual">
|
|
|
+ <span class="pl-io-order-edit-item__qty-label">实际数量</span>
|
|
|
<el-input-number
|
|
|
- v-model="itemRow.actualQty"
|
|
|
+ v-model="row.actualQty"
|
|
|
:min="0"
|
|
|
:precision="3"
|
|
|
:controls="false"
|
|
|
- :disabled="Boolean(itemRow.brandMode)"
|
|
|
+ :disabled="Boolean(row.brandMode)"
|
|
|
+ class="pl-io-order-edit-item__qty-input"
|
|
|
/>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="packageSpec" label="包装规格" min-width="110" show-overflow-tooltip />
|
|
|
- </el-table>
|
|
|
- <BrandQtyChipsField
|
|
|
- v-if="row.brandMode"
|
|
|
- v-model="row.brandQtyRows"
|
|
|
- :brand-mode="ioBrandModeForField(row)"
|
|
|
- :required-brand-id="row.brandId"
|
|
|
- :brand-options="row.brandOptions || []"
|
|
|
- :unit="row.unitLabel || row.unit || '吨'"
|
|
|
- variant="admin"
|
|
|
- :actual-qty="Number(row.actualQty ?? 0)"
|
|
|
- @update:actual-qty="(val) => syncIoRowActualQty(row, val)"
|
|
|
- />
|
|
|
+ <span class="pl-io-order-edit-item__unit">{{
|
|
|
+ row.unitLabel || row.unit || '吨'
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <BrandQtyChipsField
|
|
|
+ v-if="row.brandMode"
|
|
|
+ v-model="row.brandQtyRows"
|
|
|
+ :brand-mode="ioBrandModeForField(row)"
|
|
|
+ :required-brand-id="row.brandId"
|
|
|
+ :brand-options="row.brandOptions || []"
|
|
|
+ :unit="row.unitLabel || row.unit || '吨'"
|
|
|
+ variant="admin"
|
|
|
+ :actual-qty="Number(row.actualQty ?? 0)"
|
|
|
+ @update:actual-qty="(val) => syncIoRowActualQty(row, val)"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="现场备注">
|
|
|
@@ -585,8 +314,12 @@
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
- <el-button @click="ioOrderEditVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" :loading="ioOrderSaving" @click="saveIoOrderEdit">确定</el-button>
|
|
|
+ <el-button @click="ioOrderEditVisible = false">
|
|
|
+ <el-icon><Close /></el-icon>取消
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" :loading="ioOrderSaving" @click="saveIoOrderEdit">
|
|
|
+ <el-icon><Check /></el-icon>确定
|
|
|
+ </el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
<PlVehicleViewDialog
|
|
|
@@ -598,7 +331,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
- import { CopyDocument, Edit, View } from '@element-plus/icons-vue'
|
|
|
+ import { Check, Close, CopyDocument, View } from '@element-plus/icons-vue'
|
|
|
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
|
|
|
import BrandQtyChipsField, {
|
|
|
type BrandQtyRow
|
|
|
@@ -607,14 +340,14 @@
|
|
|
import PlPlanFormProductTable from '@/components/business/pl-plan-form-dialog/PlPlanFormProductTable.vue'
|
|
|
import PlPlanHazardCertViewTable from '@/components/business/pl-plan-hazard-cert-view-table/index.vue'
|
|
|
import PlPlanSiteFeedbackDialog from '@/components/business/pl-plan-site-feedback-dialog/index.vue'
|
|
|
+ import PlPlanTransportFeedbackBlocks, {
|
|
|
+ type SiteFeedbackTableRow
|
|
|
+ } from '@/components/business/pl-plan-transport-feedback-blocks/index.vue'
|
|
|
import PlVehicleSummaryStrip from '@/components/business/pl-vehicle-summary-strip/index.vue'
|
|
|
import PlVehicleViewDialog from '@/components/business/pl-vehicle-view-dialog/index.vue'
|
|
|
import ViewKvTable from '@/components/business/system-view-dialog/ViewKvTable.vue'
|
|
|
import type { ViewField } from '@/components/business/system-view-dialog/types'
|
|
|
- import {
|
|
|
- businessTypeTagType,
|
|
|
- inventoryStatusTagType
|
|
|
- } from '@/components/business/db/db-related-view'
|
|
|
+ import { businessTypeTagType } from '@/components/business/db/db-related-view'
|
|
|
import type { ContractVO } from '@/api/crm/contract/types'
|
|
|
import { inventoryApi } from '@/api/db/inventory'
|
|
|
import {
|
|
|
@@ -623,7 +356,12 @@
|
|
|
type PlIoOrder,
|
|
|
type TransportSiteLink
|
|
|
} from '@/api/pl'
|
|
|
- import { mapContractProducts, type PlanFormProductRow } from '@/composables/usePlContractToPlan'
|
|
|
+ import {
|
|
|
+ enrichPlanProductLinesAlias,
|
|
|
+ getDeliveryMethodLabel,
|
|
|
+ mapContractProducts,
|
|
|
+ type PlanFormProductRow
|
|
|
+ } from '@/composables/usePlContractToPlan'
|
|
|
import { mapPlanProductToFormRow, usePlPlanViewDetail, type PlPlanTransportDibangRow, type PlPlanTransportSiteRow } from '@/composables/usePlPlanViewDetail'
|
|
|
import { resolveFreightUnitLabel } from '@/composables/usePlFreightCalc'
|
|
|
import { isHazardProduct } from '@/composables/usePlToxicProductCertCheck'
|
|
|
@@ -641,8 +379,7 @@
|
|
|
getPlPlanCompanyLabel,
|
|
|
getPlPlanTypeLabel,
|
|
|
getPlYesNoLabel,
|
|
|
- parsePlFeedbackImages,
|
|
|
- resolveSiteBrandSummary
|
|
|
+ parsePlFeedbackImages
|
|
|
} from '@/utils/pl-display'
|
|
|
import { formatMoneyDisplay } from '@/utils/money-format'
|
|
|
import type { Transport, TransportDriverProductFeedback } from '@/api/pl'
|
|
|
@@ -661,13 +398,16 @@
|
|
|
arrangeDriverUrl?: string
|
|
|
arrangeSiteRecords?: TransportSiteLink[]
|
|
|
feedbackEditable?: boolean
|
|
|
+ /** 将司机/现场/地磅回传区块镜像渲染到归档左侧等目标节点 */
|
|
|
+ feedbackMirrorTo?: string | HTMLElement | null
|
|
|
}>(),
|
|
|
{
|
|
|
embedded: false,
|
|
|
showArrangeLinks: false,
|
|
|
arrangeDriverUrl: '',
|
|
|
arrangeSiteRecords: () => [],
|
|
|
- feedbackEditable: false
|
|
|
+ feedbackEditable: false,
|
|
|
+ feedbackMirrorTo: null
|
|
|
}
|
|
|
)
|
|
|
|
|
|
@@ -755,6 +495,21 @@
|
|
|
row.actualQty = Number(val)
|
|
|
}
|
|
|
|
|
|
+ function resolveIoBrandOptions(it: PlIoOrder['items'][number]) {
|
|
|
+ let options = [...(it.brandOptions || [])]
|
|
|
+ if (
|
|
|
+ it.brandMode === 'required' &&
|
|
|
+ it.brandId &&
|
|
|
+ !options.some((o) => o.brandId === it.brandId)
|
|
|
+ ) {
|
|
|
+ options = [
|
|
|
+ { brandId: it.brandId, brandName: it.brandName || '指定品牌' },
|
|
|
+ ...options
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ return options
|
|
|
+ }
|
|
|
+
|
|
|
function mapIoItemBrandQtyRows(it: PlIoOrder['items'][number]): BrandQtyRow[] {
|
|
|
const saved = (it.brandQtys || [])
|
|
|
.filter(r => Number(r.qty) > 0)
|
|
|
@@ -765,10 +520,14 @@
|
|
|
}))
|
|
|
if (saved.length) return saved
|
|
|
if (it.brandMode === 'required' && it.brandId) {
|
|
|
+ const brandName =
|
|
|
+ it.brandName ||
|
|
|
+ (it.brandOptions || []).find((o) => o.brandId === it.brandId)?.brandName ||
|
|
|
+ '指定品牌'
|
|
|
return [
|
|
|
{
|
|
|
brandId: it.brandId,
|
|
|
- brandName: it.brandName || '指定品牌',
|
|
|
+ brandName,
|
|
|
qty: Number(it.actualQty ?? 0)
|
|
|
}
|
|
|
]
|
|
|
@@ -785,17 +544,25 @@
|
|
|
|
|
|
function openIoOrderEditDialog() {
|
|
|
if (!ioOrder.value) return
|
|
|
+ const planProductById = new Map((products.value || []).map(p => [p.id, p]))
|
|
|
ioOrderEditForm.value = {
|
|
|
...ioOrder.value,
|
|
|
items: (ioOrder.value.items || []).map(it => {
|
|
|
- const brandQtyRows = mapIoItemBrandQtyRows(it)
|
|
|
+ const brandOptions = resolveIoBrandOptions(it)
|
|
|
+ const brandQtyRows = mapIoItemBrandQtyRows({ ...it, brandOptions })
|
|
|
const actualQty = it.brandMode
|
|
|
? Number(
|
|
|
brandQtyRows.reduce((sum, r) => sum + Number(r.qty || 0), 0).toFixed(3)
|
|
|
)
|
|
|
: it.actualQty
|
|
|
+ const planProduct = planProductById.get(it.planProductId)
|
|
|
return {
|
|
|
...it,
|
|
|
+ categoryName: it.categoryName || planProduct?.categoryName || '',
|
|
|
+ specification: it.specification || planProduct?.model || '',
|
|
|
+ packageSpec: it.packageSpec || planProduct?.packageSpec || '',
|
|
|
+ deliveryMethod: it.deliveryMethod || planProduct?.deliveryMethod || '',
|
|
|
+ brandOptions,
|
|
|
brandQtyRows,
|
|
|
actualQty
|
|
|
}
|
|
|
@@ -874,26 +641,237 @@
|
|
|
return Array.isArray(imgs) ? imgs.filter(Boolean).join(',') : ''
|
|
|
}
|
|
|
|
|
|
+ function mergeTaxRatesFromContract(
|
|
|
+ rows: PlanFormProductRow[],
|
|
|
+ contract: ContractVO | null
|
|
|
+ ): PlanFormProductRow[] {
|
|
|
+ if (!contract?.products?.length) return rows
|
|
|
+ const rateByProductId = new Map<string, number>()
|
|
|
+ for (const item of contract.products) {
|
|
|
+ const key = String(item.productId ?? item.id ?? '')
|
|
|
+ if (key && item.taxRate != null) rateByProductId.set(key, Number(item.taxRate))
|
|
|
+ }
|
|
|
+ return rows.map((row) => ({
|
|
|
+ ...row,
|
|
|
+ taxRate: row.taxRate ?? rateByProductId.get(row.productId)
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ const productRows = ref<PlanFormProductRow[]>([])
|
|
|
+ let productRowsEnrichSeq = 0
|
|
|
+
|
|
|
+ async function refreshProductRows() {
|
|
|
+ const seq = ++productRowsEnrichSeq
|
|
|
+ const rows = products.value.map((item, index) => mapPlanProductToFormRow(item, index))
|
|
|
+ const withTax = mergeTaxRatesFromContract(rows, contractSnapshot.value)
|
|
|
+ // 先同步写入计划产品字段,避免别名异步回填完成前表格全是 —
|
|
|
+ productRows.value = withTax
|
|
|
+ const enriched = await enrichPlanProductLinesAlias(withTax.map((row) => ({ ...row })))
|
|
|
+ if (seq !== productRowsEnrichSeq) return
|
|
|
+ productRows.value = enriched
|
|
|
+ }
|
|
|
+
|
|
|
+ watch([products, contractSnapshot], () => {
|
|
|
+ void refreshProductRows()
|
|
|
+ }, { immediate: true, deep: true })
|
|
|
+
|
|
|
+ const planProductById = computed(() => {
|
|
|
+ const map = new Map<string, (typeof products.value)[number]>()
|
|
|
+ for (const row of products.value) {
|
|
|
+ if (row.id) map.set(String(row.id), row)
|
|
|
+ }
|
|
|
+ return map
|
|
|
+ })
|
|
|
+
|
|
|
+ const planProductFormById = computed(() => {
|
|
|
+ const map = new Map<string, PlanFormProductRow>()
|
|
|
+ for (const row of productRows.value) {
|
|
|
+ if (row.id) map.set(String(row.id), row)
|
|
|
+ }
|
|
|
+ return map
|
|
|
+ })
|
|
|
+
|
|
|
+ function resolveFeedbackPlanProductId(row: TransportDriverProductFeedback | Record<string, unknown>) {
|
|
|
+ const raw =
|
|
|
+ (row as TransportDriverProductFeedback).planProductId ||
|
|
|
+ (row as { plan_product_id?: string }).plan_product_id ||
|
|
|
+ ''
|
|
|
+ return String(raw || '').trim()
|
|
|
+ }
|
|
|
+
|
|
|
+ function resolveDisplayBrandLabel(options: {
|
|
|
+ brandMode?: string | null
|
|
|
+ brandName?: string | null
|
|
|
+ }) {
|
|
|
+ const mode = (options.brandMode || '').trim()
|
|
|
+ const name = (options.brandName || '').trim()
|
|
|
+ if (mode === 'required') return name || '指定品牌'
|
|
|
+ if (mode === 'any') return name || '不限品牌'
|
|
|
+ return name
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildDriverFeedbackDisplayRow(
|
|
|
+ pp: (typeof products.value)[number] | undefined,
|
|
|
+ fb: TransportDriverProductFeedback | undefined,
|
|
|
+ formRow?: PlanFormProductRow
|
|
|
+ ) {
|
|
|
+ const unitCode = String(fb?.unit || pp?.unit || formRow?.unit || '').trim()
|
|
|
+ const unitLabel = dictStore.getLabel('crm_product_unit', unitCode) || unitCode
|
|
|
+ const brandLabel = resolveDisplayBrandLabel({
|
|
|
+ brandMode: fb?.brandMode || pp?.brandMode || formRow?.brandMode,
|
|
|
+ brandName: fb?.brandName || pp?.brandName || formRow?.brandName
|
|
|
+ })
|
|
|
+ return {
|
|
|
+ productName:
|
|
|
+ formRow?.standardProductName ||
|
|
|
+ pp?.productName ||
|
|
|
+ formRow?.productName ||
|
|
|
+ fb?.productName ||
|
|
|
+ '—',
|
|
|
+ aliasName: formRow?.aliasName || '',
|
|
|
+ model: fb?.model || pp?.model || formRow?.model || '',
|
|
|
+ unit: unitCode,
|
|
|
+ unitLabel,
|
|
|
+ packageSpec: fb?.packageSpec || pp?.packageSpec || formRow?.packageSpec || '',
|
|
|
+ brandName: brandLabel,
|
|
|
+ actualQty: fb?.actualQty,
|
|
|
+ brandSummary: fb?.brandSummary || formatBrandQtySummary(fb?.brandQtys) || '—'
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const driverFeedbackTableRows = computed(() => {
|
|
|
- const rows = transport.value?.driverProductFeedbacks || []
|
|
|
- if (rows.length) {
|
|
|
- return rows.map((row: TransportDriverProductFeedback) => ({
|
|
|
- productName: row.productName || '—',
|
|
|
- actualQty: row.actualQty,
|
|
|
- brandSummary: row.brandSummary || formatBrandQtySummary(row.brandQtys) || '—'
|
|
|
- }))
|
|
|
+ const feedbacks = transport.value?.driverProductFeedbacks || []
|
|
|
+ const planList = products.value || []
|
|
|
+
|
|
|
+ if (feedbacks.length) {
|
|
|
+ const feedbackById = new Map(
|
|
|
+ feedbacks.map((fb) => [resolveFeedbackPlanProductId(fb), fb] as const)
|
|
|
+ )
|
|
|
+ const usedIds = new Set<string>()
|
|
|
+ const rows: ReturnType<typeof buildDriverFeedbackDisplayRow>[] = []
|
|
|
+
|
|
|
+ // 以计划产品为主键拼接,保证名称/规格/单位等一定能带出
|
|
|
+ for (const pp of planList) {
|
|
|
+ const fb = feedbackById.get(String(pp.id))
|
|
|
+ if (!fb) continue
|
|
|
+ usedIds.add(String(pp.id))
|
|
|
+ rows.push(buildDriverFeedbackDisplayRow(pp, fb, planProductFormById.value.get(String(pp.id))))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 兼容:planProductId 对不上时按序号兜底
|
|
|
+ if (!rows.length && feedbacks.length === planList.length) {
|
|
|
+ return feedbacks.map((fb, index) =>
|
|
|
+ buildDriverFeedbackDisplayRow(
|
|
|
+ planList[index],
|
|
|
+ fb,
|
|
|
+ planProductFormById.value.get(String(planList[index]?.id || ''))
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const fb of feedbacks) {
|
|
|
+ const id = resolveFeedbackPlanProductId(fb)
|
|
|
+ if (id && usedIds.has(id)) continue
|
|
|
+ const pp = id ? planProductById.value.get(id) : undefined
|
|
|
+ rows.push(buildDriverFeedbackDisplayRow(pp, fb, id ? planProductFormById.value.get(id) : undefined))
|
|
|
+ }
|
|
|
+ return rows
|
|
|
}
|
|
|
+
|
|
|
if (!hasDriverFeedback.value) return []
|
|
|
// 兼容旧数据:仅有运输单总量、无按产品明细
|
|
|
return [
|
|
|
{
|
|
|
productName: '(合计)',
|
|
|
+ aliasName: '',
|
|
|
+ model: '',
|
|
|
+ unit: '',
|
|
|
+ unitLabel: '',
|
|
|
+ packageSpec: '',
|
|
|
+ brandName: '',
|
|
|
actualQty: transport.value?.feedbackQty,
|
|
|
brandSummary: '—'
|
|
|
}
|
|
|
]
|
|
|
})
|
|
|
|
|
|
+ function resolveIoLinkedPlanProduct(
|
|
|
+ it: {
|
|
|
+ planProductId?: string
|
|
|
+ productId?: string
|
|
|
+ productName?: string
|
|
|
+ plan_product_id?: string
|
|
|
+ product_id?: string
|
|
|
+ },
|
|
|
+ index: number
|
|
|
+ ) {
|
|
|
+ const planList = products.value || []
|
|
|
+ const planProductId = String(it.planProductId || it.plan_product_id || '').trim()
|
|
|
+ if (planProductId) {
|
|
|
+ const byId = planProductById.value.get(planProductId)
|
|
|
+ if (byId) return byId
|
|
|
+ }
|
|
|
+ const crmId = String(it.productId || it.product_id || '').trim()
|
|
|
+ if (crmId) {
|
|
|
+ const byCrm = planList.find((p) => String(p.productId || '') === crmId)
|
|
|
+ if (byCrm) return byCrm
|
|
|
+ }
|
|
|
+ if (planList.length === (ioOrder.value?.items || []).length) {
|
|
|
+ const byIndex = planList[index]
|
|
|
+ if (byIndex) return byIndex
|
|
|
+ }
|
|
|
+ const name = String(it.productName || '').trim()
|
|
|
+ if (name) {
|
|
|
+ const byName = planList.find((p) => String(p.productName || '').trim() === name)
|
|
|
+ if (byName) return byName
|
|
|
+ }
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ const ioOrderDisplayItems = computed(() => {
|
|
|
+ const items = ioOrder.value?.items || []
|
|
|
+ return items.map((it, index) => {
|
|
|
+ const pp = resolveIoLinkedPlanProduct(it, index)
|
|
|
+ const formRow = pp?.id
|
|
|
+ ? planProductFormById.value.get(String(pp.id))
|
|
|
+ : planProductFormById.value.get(String(it.planProductId || '').trim())
|
|
|
+ const productId = String(formRow?.productId || pp?.productId || it.productId || '').trim()
|
|
|
+ const candidates = [
|
|
|
+ String(it.productNo || '').trim(),
|
|
|
+ String(formRow?.productNo || '').trim(),
|
|
|
+ String(pp?.productNo || '').trim()
|
|
|
+ ].filter(Boolean)
|
|
|
+ const productNo =
|
|
|
+ candidates.find((no) => no && no !== productId) || candidates[0] || ''
|
|
|
+ const brandLabel = resolveDisplayBrandLabel({
|
|
|
+ brandMode: it.brandMode || pp?.brandMode || formRow?.brandMode,
|
|
|
+ brandName: it.brandName || pp?.brandName || formRow?.brandName
|
|
|
+ })
|
|
|
+ const unitCode = String(it.unit || pp?.unit || formRow?.unit || '').trim()
|
|
|
+ return {
|
|
|
+ ...it,
|
|
|
+ productNo: productNo || undefined,
|
|
|
+ productName:
|
|
|
+ formRow?.standardProductName ||
|
|
|
+ pp?.productName ||
|
|
|
+ it.productName ||
|
|
|
+ formRow?.productName ||
|
|
|
+ '',
|
|
|
+ aliasName: formRow?.aliasName || '',
|
|
|
+ brandName: brandLabel,
|
|
|
+ specification: it.specification || pp?.model || formRow?.model || '',
|
|
|
+ packageSpec: it.packageSpec || pp?.packageSpec || formRow?.packageSpec || '',
|
|
|
+ remark: (it.remark || pp?.remark || formRow?.remark || '').trim(),
|
|
|
+ unit: unitCode || it.unit,
|
|
|
+ unitLabel:
|
|
|
+ it.unitLabel ||
|
|
|
+ dictStore.getLabel('crm_product_unit', unitCode) ||
|
|
|
+ unitCode ||
|
|
|
+ ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
const siteFeedbackTableRows = computed(() =>
|
|
|
siteInventoryRows.value.map((row) => ({
|
|
|
sourceRow: row,
|
|
|
@@ -1036,6 +1014,41 @@
|
|
|
|
|
|
const displayFeedbackTime = computed(() => formatDateTime(transport.value?.feedbackTime) || '—')
|
|
|
|
|
|
+ const feedbackBlocksBind = computed(() => ({
|
|
|
+ feedbackEditable: props.feedbackEditable,
|
|
|
+ loading: loading.value,
|
|
|
+ canEditDriverFeedback: canEditDriverFeedback.value,
|
|
|
+ hasDriverFeedback: hasDriverFeedback.value,
|
|
|
+ arrangeDriverUrl: props.arrangeDriverUrl,
|
|
|
+ driverFeedbackTableRows: driverFeedbackTableRows.value,
|
|
|
+ displayFeedbackTime: displayFeedbackTime.value,
|
|
|
+ feedbackRemark: transport.value?.feedbackRemark,
|
|
|
+ feedbackImageCsv: feedbackImageCsv.value,
|
|
|
+ ioOrder: ioOrder.value,
|
|
|
+ ioOrderDisplayItems: ioOrderDisplayItems.value,
|
|
|
+ planProducts: products.value,
|
|
|
+ planProductRows: productRows.value,
|
|
|
+ ioOrderHasFeedback: ioOrderHasFeedback.value,
|
|
|
+ ioOrderViewFields: ioOrderViewFields.value,
|
|
|
+ ioOrderImageCsv: ioOrderImageCsv.value,
|
|
|
+ siteFeedbackTableRows: siteFeedbackTableRows.value,
|
|
|
+ dibangPlanRows: dibangPlanRows.value,
|
|
|
+ dibangBasicViewFields
|
|
|
+ }))
|
|
|
+
|
|
|
+ const feedbackBlocksOn = {
|
|
|
+ 'edit-driver': openDriverFeedbackDialog,
|
|
|
+ 'copy-driver-url': copyArrangeDriverUrl,
|
|
|
+ 'edit-io-order': openIoOrderEditDialog,
|
|
|
+ 'copy-site-url': (url?: string) => {
|
|
|
+ void copyArrangeSiteUrl(url)
|
|
|
+ },
|
|
|
+ 'edit-site-feedback': openSiteFeedbackDialog,
|
|
|
+ 'copy-site-summary': (row: SiteFeedbackTableRow) => {
|
|
|
+ void copySiteSummaryForRow(row.sourceRow)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const displayCarrierName = computed(() => {
|
|
|
if (transport.value?.carrierName) return transport.value.carrierName
|
|
|
const vehicle = vehicleBundle.value?.detail
|
|
|
@@ -1083,22 +1096,6 @@
|
|
|
return getPlHasInvoiceLabelFromSettlement(freightSettlement.value)
|
|
|
})
|
|
|
|
|
|
- function mergeTaxRatesFromContract(
|
|
|
- rows: PlanFormProductRow[],
|
|
|
- contract: ContractVO | null
|
|
|
- ): PlanFormProductRow[] {
|
|
|
- if (!contract?.products?.length) return rows
|
|
|
- const rateByProductId = new Map<string, number>()
|
|
|
- for (const item of contract.products) {
|
|
|
- const key = String(item.productId ?? item.id ?? '')
|
|
|
- if (key && item.taxRate != null) rateByProductId.set(key, Number(item.taxRate))
|
|
|
- }
|
|
|
- return rows.map((row) => ({
|
|
|
- ...row,
|
|
|
- taxRate: row.taxRate ?? rateByProductId.get(row.productId)
|
|
|
- }))
|
|
|
- }
|
|
|
-
|
|
|
const overviewBasicViewFields = computed<ViewField[]>(() => [
|
|
|
{ label: '计划编号', value: plan.value?.planNo },
|
|
|
{ label: '计划日期', value: plan.value?.planDate },
|
|
|
@@ -1211,11 +1208,6 @@
|
|
|
mapContractProducts(contractSnapshot.value?.products)
|
|
|
)
|
|
|
|
|
|
- const productRows = computed<PlanFormProductRow[]>(() => {
|
|
|
- const rows = products.value.map((item, index) => mapPlanProductToFormRow(item, index))
|
|
|
- return mergeTaxRatesFromContract(rows, contractSnapshot.value)
|
|
|
- })
|
|
|
-
|
|
|
const hazardProducts = computed(() => products.value.filter((item) => isHazardProduct(item)))
|
|
|
|
|
|
async function handleViewContract() {
|
|
|
@@ -1292,6 +1284,11 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /* 回传区块有外包层,无法命中 section + section,补与上方运费表格的间距 */
|
|
|
+ .pl-plan-form-section + :deep(.pl-plan-transport-feedback-blocks) {
|
|
|
+ margin-top: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
.pl-plan-form-section-head {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
@@ -1637,4 +1634,148 @@
|
|
|
.pl-plan-dibang-pending {
|
|
|
margin: 8px 0 0;
|
|
|
}
|
|
|
+
|
|
|
+ .pl-io-order-edit-form {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-products {
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ :deep(.el-form-item__content) {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-products__list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item {
|
|
|
+ width: 100%;
|
|
|
+ padding: 12px;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #f8fafc;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__head {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 8px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__title {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1e293b;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__no {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #64748b;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__badge {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #64748b;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__meta {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
|
+ gap: 8px 12px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ padding: 10px 12px;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__meta-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__meta-label {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #94a3b8;
|
|
|
+ line-height: 1.2;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__meta-value {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #334155;
|
|
|
+ line-height: 1.4;
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty-row {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: minmax(140px, 1fr) minmax(240px, 2fr);
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty--actual {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 72px 1fr auto;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #64748b;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty-value {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1e293b;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty-input {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__unit {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #64748b;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 900px) {
|
|
|
+ .pl-io-order-edit-item__meta {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .pl-io-order-edit-item__qty-row {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+ }
|
|
|
</style>
|