|
|
@@ -0,0 +1,449 @@
|
|
|
+<template>
|
|
|
+ <AidopDemoShell :title="pageTitle" subtitle="未匹配到供应商的初始(p)工单物料明细,确认后写入货源清单基础数据">
|
|
|
+ <!-- 顶部信息 + 返回 -->
|
|
|
+ <div class="bom-header">
|
|
|
+ <el-button text @click="goBack">
|
|
|
+ <el-icon><ArrowLeft /></el-icon> 返回工单下达列表
|
|
|
+ </el-button>
|
|
|
+ <el-button text type="primary" :loading="loading" @click="loadList">
|
|
|
+ <el-icon><Refresh /></el-icon> 刷新
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 条件筛选区(参照采购在途演示工程) -->
|
|
|
+ <AidopFilterPane
|
|
|
+ v-if="rows.length > 0"
|
|
|
+ :rows="rows"
|
|
|
+ :fields="AIDOP_FILTER_FIELDS"
|
|
|
+ @filter-change="onFilterChange"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 统计摘要 -->
|
|
|
+ <div class="bom-summary">
|
|
|
+ 共 <b>{{ filteredRows.length }}</b> 行未匹配供应商,涉及 <b>{{ itemKindCount }}</b> 种物料
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 物料明细表:可编辑列与上传BOM一致 -->
|
|
|
+ <el-table
|
|
|
+ :data="pageRows"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ style="width: 100%; margin-top: 8px"
|
|
|
+ max-height="520"
|
|
|
+ size="small"
|
|
|
+ v-loading="loading"
|
|
|
+ >
|
|
|
+ <el-table-column type="index" label="#" width="60" align="center" fixed :index="pageIndex" />
|
|
|
+ <el-table-column
|
|
|
+ v-for="col in TABLE_COLUMNS"
|
|
|
+ :key="col.key"
|
|
|
+ :prop="col.key"
|
|
|
+ :label="col.label"
|
|
|
+ :width="col.width"
|
|
|
+ :min-width="col.minWidth"
|
|
|
+ show-overflow-tooltip
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <template v-if="col.editable">
|
|
|
+ <div>
|
|
|
+ <!-- 下拉选择列:决策采购 / 供应商 / 决策阶段 / 状态 -->
|
|
|
+ <el-select
|
|
|
+ v-if="['purchaser', 'supplier', 'decisionStage', 'status'].includes(col.key)"
|
|
|
+ v-model="row[col.key]"
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ default-first-option
|
|
|
+ :remote="col.key === 'supplier'"
|
|
|
+ :remote-method="col.key === 'supplier' ? searchSuppliers : undefined"
|
|
|
+ :loading="col.key === 'purchaser' ? purchaserLoading : col.key === 'supplier' ? supplierLoading : false"
|
|
|
+ placeholder=""
|
|
|
+ size="small"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onCellChange(row, col.key)"
|
|
|
+ >
|
|
|
+ <el-option v-if="col.key === 'purchaser'" v-for="u in purchaserOptions" :key="'p-' + u" :label="u" :value="u" />
|
|
|
+ <el-option v-else-if="col.key === 'supplier'" v-for="s in supplierOptions" :key="'s-' + s.value" :label="s.label" :value="s.value" />
|
|
|
+ <el-option v-else-if="col.key === 'decisionStage'" v-for="ds in DECISION_STAGE_OPTIONS" :key="'ds-' + ds" :label="ds" :value="ds" />
|
|
|
+ <el-option v-else-if="col.key === 'status'" v-for="st in STATUS_OPTIONS" :key="'st-' + st" :label="st" :value="st" />
|
|
|
+ </el-select>
|
|
|
+ <!-- 数值列:含税单价 / 总价 -->
|
|
|
+ <el-input-number
|
|
|
+ v-else-if="['unitPrice', 'totalPrice'].includes(col.key)"
|
|
|
+ v-model="row[col.key]"
|
|
|
+ size="small"
|
|
|
+ controls-position="right"
|
|
|
+ :precision="2"
|
|
|
+ style="width: 100%"
|
|
|
+ @change="onCellChange(row, col.key)"
|
|
|
+ />
|
|
|
+ <!-- 文本输入:供应商联系人 -->
|
|
|
+ <el-input v-else v-model="row[col.key]" size="small" @change="onCellChange(row, col.key)" />
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <span v-else>{{ formatCell(row[col.key], col.type) }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <div v-if="filteredRows.length > PAGE_SIZE" style="margin-top: 8px; display: flex; justify-content: flex-end">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="currentPage"
|
|
|
+ :page-size="PAGE_SIZE"
|
|
|
+ :total="filteredRows.length"
|
|
|
+ layout="total, prev, pager, next, jumper"
|
|
|
+ background
|
|
|
+ small
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 操作按钮 -->
|
|
|
+ <div class="bom-actions">
|
|
|
+ <el-button type="primary" @click="onConfirm" :loading="confirming">确认</el-button>
|
|
|
+ <el-button @click="goBack">取消</el-button>
|
|
|
+ </div>
|
|
|
+ </AidopDemoShell>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts" name="aidopBusinessWorkOrderPurchaseConfirm">
|
|
|
+import { computed, onMounted, ref } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { ArrowLeft, Refresh } from '@element-plus/icons-vue';
|
|
|
+import AidopDemoShell from '../components/AidopDemoShell.vue';
|
|
|
+import AidopFilterPane from '../components/AidopFilterPane.vue';
|
|
|
+import { AIDOP_FILTER_FIELDS } from '../components/aidopFilterFields';
|
|
|
+import {
|
|
|
+ confirmBatchPurchase,
|
|
|
+ fetchUnmatchedPurchaseList,
|
|
|
+ type PurchaseConfirmItem,
|
|
|
+ type UnmatchedPurchaseRow,
|
|
|
+} from '../api/workOrderDispatch';
|
|
|
+import { getAPI } from '/@/utils/axios-utils';
|
|
|
+import { SysUserApi } from '/@/api-services/api';
|
|
|
+import { s0SuppliersApi } from '../s0/api/s0SupplyApi';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+const pageTitle = '批量采购确认';
|
|
|
+
|
|
|
+// ── 列定义:与上传BOM页 38 列完全一致 ──
|
|
|
+interface ColDef {
|
|
|
+ key: string;
|
|
|
+ label: string;
|
|
|
+ width?: number;
|
|
|
+ minWidth?: number;
|
|
|
+ type: 'id' | 'text' | 'enum' | 'number' | 'date';
|
|
|
+ editable?: boolean;
|
|
|
+}
|
|
|
+const TABLE_COLUMNS: ColDef[] = [
|
|
|
+ { key: 'salesOrderNo', label: '销售订单号', width: 130, type: 'id' },
|
|
|
+ { key: 'contractNo', label: '合同号', width: 130, type: 'id' },
|
|
|
+ { key: 'contractName', label: '合同名称', minWidth: 150, type: 'text' },
|
|
|
+ { key: 'productDept', label: '产品部', width: 110, type: 'enum' },
|
|
|
+ { key: 'parentItemNum', label: '父物料号', width: 120, type: 'id' },
|
|
|
+ { key: 'parentDescr', label: '父物料描述', minWidth: 140, type: 'text' },
|
|
|
+ { key: 'factory', label: '工厂', width: 70, type: 'enum' },
|
|
|
+ { key: 'productionPerson', label: '投产人', width: 80, type: 'enum' },
|
|
|
+ { key: 'materialBillNo', label: '物料单号', width: 100, type: 'id' },
|
|
|
+ { key: 'itemNum', label: '子物料号', width: 140, type: 'id' },
|
|
|
+ { key: 'descr', label: '子物料描述', minWidth: 150, type: 'text' },
|
|
|
+ { key: 'descr1', label: '子件长描述', minWidth: 120, type: 'text' },
|
|
|
+ { key: 'qtyRequired', label: '需求数量', width: 90, type: 'number' },
|
|
|
+ { key: 'um', label: '单位', width: 60, type: 'enum' },
|
|
|
+ { key: 'projectText', label: '项目文本', width: 100, type: 'text' },
|
|
|
+ { key: 'electricalDesigner', label: '电气设计', width: 90, type: 'enum' },
|
|
|
+ { key: 'structuralDesigner', label: '结构设计', width: 90, type: 'enum' },
|
|
|
+ { key: 'pushDate', label: '推送日期', width: 100, type: 'date' },
|
|
|
+ { key: 'purchaser', label: '决策采购', width: 100, type: 'enum', editable: true },
|
|
|
+ { key: 'supplier', label: '供应商', minWidth: 160, type: 'enum', editable: true },
|
|
|
+ { key: 'supplierContact', label: '供应商联系人', width: 120, type: 'enum', editable: true },
|
|
|
+ { key: 'decisionStage', label: '决策阶段', width: 110, type: 'enum', editable: true },
|
|
|
+ { key: 'unitPrice', label: '含税单价', width: 90, type: 'number', editable: true },
|
|
|
+ { key: 'totalPrice', label: '总价', width: 90, type: 'number', editable: true },
|
|
|
+ { key: 'sapInfoNo', label: 'SAP信息记录编号', width: 150, type: 'id' },
|
|
|
+ { key: 'decisionDoneDate', label: '决策完成日期', width: 110, type: 'date' },
|
|
|
+ { key: 'execPurchaser', label: '执行采购', width: 90, type: 'enum' },
|
|
|
+ { key: 'purchaseReqNo', label: '采购申请号', width: 110, type: 'number' },
|
|
|
+ { key: 'purchaseOrderNo', label: '采购订单号', width: 110, type: 'number' },
|
|
|
+ { key: 'orderQty', label: '订单数量', width: 90, type: 'number' },
|
|
|
+ { key: 'orderDate', label: '订单日期', width: 100, type: 'date' },
|
|
|
+ { key: 'deliveryDate', label: '交货日期', width: 100, type: 'date' },
|
|
|
+ { key: 'requiredDeliveryDate', label: '要求到货日期', width: 110, type: 'date' },
|
|
|
+ { key: 'lastReceivedQty', label: '最近到货数量', width: 110, type: 'number' },
|
|
|
+ { key: 'lastReceivedDate', label: '最近到货日期', width: 110, type: 'date' },
|
|
|
+ { key: 'status', label: '状态', width: 100, type: 'enum', editable: true },
|
|
|
+ { key: 'cumulativeReceivedQty', label: '累计入库数量', width: 110, type: 'number' },
|
|
|
+ { key: 'issue', label: '异常问题', minWidth: 120, type: 'text' },
|
|
|
+];
|
|
|
+
|
|
|
+const DECISION_STAGE_OPTIONS = ref(['待决策', '已发资料', '集采再询价', '竞价或扩额', '替代采购', '已维护价格']);
|
|
|
+const STATUS_OPTIONS = ref(['待决策', '已下订单', '已通知送货', '已部分到货', '已完成', '已删除', '被替代']);
|
|
|
+
|
|
|
+// ── 数据状态 ──
|
|
|
+interface RowItem {
|
|
|
+ [key: string]: any;
|
|
|
+}
|
|
|
+const loading = ref(false);
|
|
|
+const confirming = ref(false);
|
|
|
+const rows = ref<RowItem[]>([]);
|
|
|
+/** 筛选后的行(由条件筛选区驱动;确认写入仍用全量 rows) */
|
|
|
+const filteredRows = ref<RowItem[]>([]);
|
|
|
+
|
|
|
+const itemKindCount = computed(() => new Set(filteredRows.value.map((r) => String(r.itemNum ?? '')).filter(Boolean)).size);
|
|
|
+
|
|
|
+// ── 分页 ──
|
|
|
+const PAGE_SIZE = 200;
|
|
|
+const currentPage = ref(1);
|
|
|
+const pageRows = computed(() => {
|
|
|
+ const start = (currentPage.value - 1) * PAGE_SIZE;
|
|
|
+ return filteredRows.value.slice(start, start + PAGE_SIZE);
|
|
|
+});
|
|
|
+function pageIndex(i: number): number {
|
|
|
+ return (currentPage.value - 1) * PAGE_SIZE + i + 1;
|
|
|
+}
|
|
|
+function onFilterChange(list: RowItem[]) {
|
|
|
+ filteredRows.value = list;
|
|
|
+ currentPage.value = 1;
|
|
|
+}
|
|
|
+
|
|
|
+/** 格式化单元格显示 */
|
|
|
+function formatCell(val: any, type: string): string {
|
|
|
+ if (val == null || val === '') return '—';
|
|
|
+ return String(val);
|
|
|
+}
|
|
|
+
|
|
|
+// ── 货源清单规则(物料→决策采购→供应商 映射,localStorage 持久化,与上传BOM页共享)──
|
|
|
+const RULES_KEY = 'aidop_supply_source_rules_v1';
|
|
|
+interface SupplySourceRule {
|
|
|
+ itemNum: string;
|
|
|
+ purchaser: string;
|
|
|
+ supplier: string;
|
|
|
+ supplierContact?: string;
|
|
|
+ updatedAt: string;
|
|
|
+}
|
|
|
+const supplySourceRules = ref<SupplySourceRule[]>([]);
|
|
|
+
|
|
|
+function loadSupplySourceRules() {
|
|
|
+ try {
|
|
|
+ const raw = localStorage.getItem(RULES_KEY);
|
|
|
+ if (raw) supplySourceRules.value = JSON.parse(raw);
|
|
|
+ } catch { /* ignore */ }
|
|
|
+}
|
|
|
+function saveSupplySourceRule(itemNum: string, purchaser: string, supplier: string, supplierContact?: string) {
|
|
|
+ if (!itemNum || !supplier) return;
|
|
|
+ const rules = supplySourceRules.value;
|
|
|
+ const existIdx = rules.findIndex((r) => r.itemNum === itemNum && r.purchaser === purchaser);
|
|
|
+ const rule: SupplySourceRule = { itemNum, purchaser, supplier, supplierContact, updatedAt: new Date().toISOString() };
|
|
|
+ if (existIdx >= 0) rules[existIdx] = rule;
|
|
|
+ else rules.push(rule);
|
|
|
+ supplySourceRules.value = rules;
|
|
|
+ try { localStorage.setItem(RULES_KEY, JSON.stringify(rules)); } catch { /* ignore */ }
|
|
|
+}
|
|
|
+/** 自动匹配供应商(本地货源清单规则,同步) */
|
|
|
+function autoMatchSupplierLocal(row: RowItem) {
|
|
|
+ if (!row.itemNum) return;
|
|
|
+ const rules = supplySourceRules.value;
|
|
|
+ const exact = row.purchaser ? rules.find((r) => r.itemNum === row.itemNum && r.purchaser === row.purchaser) : null;
|
|
|
+ if (exact) {
|
|
|
+ if (!row.supplier) row.supplier = exact.supplier;
|
|
|
+ if (!row.supplierContact && exact.supplierContact) row.supplierContact = exact.supplierContact;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const fallback = rules.find((r) => r.itemNum === row.itemNum);
|
|
|
+ if (fallback && !row.supplier) {
|
|
|
+ row.supplier = fallback.supplier;
|
|
|
+ if (!row.supplierContact && fallback.supplierContact) row.supplierContact = fallback.supplierContact;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ── 可编辑列变更处理 ──
|
|
|
+function onCellChange(row: RowItem, key: string) {
|
|
|
+ if (key === 'purchaser') {
|
|
|
+ const val = String(row.purchaser ?? '').trim();
|
|
|
+ if (val && !purchaserOptions.value.includes(val)) {
|
|
|
+ purchaserOptions.value.push(val);
|
|
|
+ }
|
|
|
+ autoMatchSupplierLocal(row);
|
|
|
+ }
|
|
|
+ if (key === 'decisionStage') {
|
|
|
+ const val = String(row.decisionStage ?? '').trim();
|
|
|
+ if (val && !DECISION_STAGE_OPTIONS.value.includes(val)) {
|
|
|
+ DECISION_STAGE_OPTIONS.value.push(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (key === 'status') {
|
|
|
+ const val = String(row.status ?? '').trim();
|
|
|
+ if (val && !STATUS_OPTIONS.value.includes(val)) {
|
|
|
+ STATUS_OPTIONS.value.push(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (key === 'supplier') {
|
|
|
+ const val = String(row.supplier ?? '').trim();
|
|
|
+ if (val && !supplierOptions.value.some((s) => s.value === val)) {
|
|
|
+ supplierOptions.value.unshift({ value: val, label: val });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (key === 'supplier' && row.supplier && row.itemNum) {
|
|
|
+ saveSupplySourceRule(
|
|
|
+ String(row.itemNum),
|
|
|
+ String(row.purchaser ?? ''),
|
|
|
+ String(row.supplier),
|
|
|
+ row.supplierContact ? String(row.supplierContact) : undefined
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (key === 'unitPrice' && row.unitPrice != null && row.qtyRequired != null) {
|
|
|
+ row.totalPrice = Number(row.unitPrice) * Number(row.qtyRequired);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ── 采购人员选项 ──
|
|
|
+const purchaserOptions = ref<string[]>([]);
|
|
|
+const purchaserLoading = ref(false);
|
|
|
+async function loadPurchaserOptions() {
|
|
|
+ purchaserLoading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await getAPI(SysUserApi).apiSysUserPagePost({ page: 1, pageSize: 500 } as any);
|
|
|
+ purchaserOptions.value = (res.data.result?.items ?? [])
|
|
|
+ .map((u: any) => u.realName)
|
|
|
+ .filter(Boolean);
|
|
|
+ } catch { /* 静默失败 */ }
|
|
|
+ finally { purchaserLoading.value = false; }
|
|
|
+}
|
|
|
+
|
|
|
+// ── 供应商搜索选项(供应商主数据 SuppMaster)──
|
|
|
+const supplierOptions = ref<{ value: string; label: string }[]>([]);
|
|
|
+const supplierLoading = ref(false);
|
|
|
+async function searchSuppliers(keyword: string) {
|
|
|
+ supplierLoading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await s0SuppliersApi.list({ keyword: keyword || undefined, page: 1, pageSize: 20 });
|
|
|
+ supplierOptions.value = (res?.list ?? [])
|
|
|
+ .filter((s) => s.sortName)
|
|
|
+ .map((s) => ({ value: s.sortName!, label: `${s.sortName}${s.supp ? ' | ' + s.supp : ''}` }));
|
|
|
+ } catch {
|
|
|
+ supplierOptions.value = [];
|
|
|
+ } finally { supplierLoading.value = false; }
|
|
|
+}
|
|
|
+
|
|
|
+// ── 列表加载 ──
|
|
|
+async function loadList() {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const data = await fetchUnmatchedPurchaseList();
|
|
|
+ rows.value = (data.list ?? []).map((r: UnmatchedPurchaseRow) => {
|
|
|
+ const row: RowItem = {
|
|
|
+ workOrd: r.workOrd ?? null,
|
|
|
+ salesOrderNo: r.salesOrderNo ?? null,
|
|
|
+ contractNo: r.contractNo ?? null,
|
|
|
+ parentItemNum: r.parentItemNum ?? null,
|
|
|
+ parentDescr: r.parentDescr ?? null,
|
|
|
+ factory: r.factory ?? null,
|
|
|
+ itemNum: r.itemNum ?? null,
|
|
|
+ descr: r.descr ?? null,
|
|
|
+ descr1: r.descr1 ?? null,
|
|
|
+ qtyRequired: r.qtyRequired ?? null,
|
|
|
+ um: r.um ?? null,
|
|
|
+ projectText: r.projectText ?? null,
|
|
|
+ purchaser: r.purchaser ?? '',
|
|
|
+ supplier: null,
|
|
|
+ supplierContact: null,
|
|
|
+ decisionStage: '待决策',
|
|
|
+ unitPrice: null,
|
|
|
+ totalPrice: null,
|
|
|
+ status: '待决策',
|
|
|
+ };
|
|
|
+ autoMatchSupplierLocal(row);
|
|
|
+ return row;
|
|
|
+ });
|
|
|
+ currentPage.value = 1;
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message || '加载未匹配列表失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ── 确认写入货源清单 ──
|
|
|
+async function onConfirm() {
|
|
|
+ if (!rows.value.length) return;
|
|
|
+
|
|
|
+ const items: PurchaseConfirmItem[] = rows.value
|
|
|
+ .filter((r) => r.supplier && r.itemNum)
|
|
|
+ .map((r) => ({
|
|
|
+ itemNum: String(r.itemNum),
|
|
|
+ descr: r.descr ? String(r.descr) : undefined,
|
|
|
+ um: r.um ? String(r.um) : undefined,
|
|
|
+ purchaser: r.purchaser ? String(r.purchaser) : undefined,
|
|
|
+ supplier: String(r.supplier),
|
|
|
+ supplierContact: r.supplierContact ? String(r.supplierContact) : undefined,
|
|
|
+ unitPrice: r.unitPrice != null ? Number(r.unitPrice) : null,
|
|
|
+ }));
|
|
|
+
|
|
|
+ if (items.length === 0) {
|
|
|
+ ElMessage.warning('请先为明细填写供应商');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const emptyCount = rows.value.length - items.length;
|
|
|
+ if (emptyCount > 0) {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm(
|
|
|
+ `有 ${emptyCount} 行未填写供应商,将跳过不写入。是否继续?`,
|
|
|
+ '批量采购确认',
|
|
|
+ { confirmButtonText: '继续', cancelButtonText: '取消', type: 'warning' }
|
|
|
+ );
|
|
|
+ } catch {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ confirming.value = true;
|
|
|
+ try {
|
|
|
+ const result = await confirmBatchPurchase(items);
|
|
|
+ const msg =
|
|
|
+ `已写入货源清单:新增 ${result.inserted} 条、更新 ${result.updated} 条` +
|
|
|
+ (result.skipped > 0 ? `、跳过 ${result.skipped} 行` : '');
|
|
|
+ ElMessage.success(msg);
|
|
|
+ await loadList();
|
|
|
+ } catch (e: any) {
|
|
|
+ ElMessage.error(e?.message || '确认写入失败');
|
|
|
+ } finally {
|
|
|
+ confirming.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function goBack() {
|
|
|
+ router.push({ path: '/aidop/s1/workorder-mgmt/dispatch' });
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadSupplySourceRules();
|
|
|
+ loadPurchaserOptions();
|
|
|
+ loadList();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+@import '../styles/aidop-demo.scss';
|
|
|
+
|
|
|
+.bom-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.bom-summary {
|
|
|
+ font-size: 13px;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ margin-bottom: 4px;
|
|
|
+ b { color: var(--el-color-primary); }
|
|
|
+}
|
|
|
+
|
|
|
+.bom-actions {
|
|
|
+ margin-top: 16px;
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+</style>
|