| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- <script setup lang="ts">
- import { ref, onMounted } from "vue";
- import { useRoute, useRouter } from "vue-router";
- import { getOrderDetail, getOrderItems, updateOrder, submitOrder, syncToErp } from "@/api/replenishmentOrder";
- import { message } from "@/utils/message";
- import { useRenderIcon } from "@/components/ReIcon/src/hooks";
- import DeleteIcon from "~icons/ep/delete";
- import dayjs from "dayjs";
- defineOptions({
- name: "ReplenishmentOrderDetail"
- });
- const route = useRoute();
- const router = useRouter();
- const loading = ref(true);
- const saving = ref(false);
- const submitting = ref(false);
- const order = ref<any>({});
- const items = ref<any[]>([]);
- const statusMap: Record<number, { text: string; type: string }> = {
- 0: { text: "草稿", type: "info" },
- 1: { text: "已提交", type: "warning" },
- 2: { text: "已同步ERP", type: "primary" },
- 3: { text: "已完成", type: "success" },
- 4: { text: "已取消", type: "danger" }
- };
- async function loadData() {
- loading.value = true;
- try {
- const id = route.query.id as string;
- if (!id) return;
- const [orderRes, itemsRes] = await Promise.all([
- getOrderDetail(id),
- getOrderItems(id)
- ]);
- if (orderRes.data) {
- order.value = orderRes.data;
- }
- if (itemsRes.data) {
- // 深拷贝以便编辑,plannedQuantity 绑定到 v-model
- items.value = (itemsRes.data || []).map((item: any) => ({ ...item }));
- }
- } finally {
- loading.value = false;
- }
- }
- function addItem() {
- items.value.push({
- orderId: order.value.id,
- deviceId: order.value.deviceId,
- productId: null,
- productCode: "",
- productName: "",
- plannedQuantity: 1,
- unitPrice: undefined,
- shelfNum: undefined,
- position: "",
- beforeStock: undefined,
- afterStock: undefined
- });
- }
- function removeItem(index: number) {
- if (items.value.length <= 1) return;
- items.value.splice(index, 1);
- }
- async function handleSave() {
- saving.value = true;
- try {
- const payload = {
- id: Number(order.value.id),
- supplierName: order.value.supplierName || undefined,
- warehouseName: order.value.warehouseName || undefined,
- expectedArrivalTime: order.value.expectedArrivalTime,
- remark: order.value.remark || undefined,
- items: items.value.map((item: any) => ({
- productId: item.productId ? Number(item.productId) : undefined,
- productCode: item.productCode || undefined,
- productName: item.productName || undefined,
- plannedQuantity: item.plannedQuantity,
- unitPrice: item.unitPrice,
- shelfNum: item.shelfNum,
- position: item.position || undefined
- }))
- };
- await updateOrder(payload);
- message("保存成功", { type: "success" });
- await loadData();
- } catch (e) {
- message("保存失败", { type: "error" });
- } finally {
- saving.value = false;
- }
- }
- async function handleSubmitToErp() {
- submitting.value = true;
- try {
- // 先提交
- await submitOrder(order.value.id);
- // 再同步 ERP
- try {
- await syncToErp(order.value.id);
- message("已提交并同步ERP", { type: "success" });
- } catch {
- message("已提交,但ERP同步失败", { type: "warning" });
- }
- await loadData();
- } catch (e) {
- message("提交失败", { type: "error" });
- } finally {
- submitting.value = false;
- }
- }
- function goBack() {
- router.push({ path: "/inventory/replenishment-order/list" });
- }
- const isDraft = () => order.value.status === 0;
- const canEdit = () => isDraft();
- function itemSubtotal(item: any) {
- return ((item.plannedQuantity || 0) * (item.unitPrice || 0)).toFixed(2);
- }
- onMounted(() => {
- loadData();
- });
- </script>
- <template>
- <div class="main p-6">
- <div class="mb-4 flex items-center justify-between">
- <div class="flex items-center gap-4">
- <el-button :icon="'ep:arrow-left'" @click="goBack">返回列表</el-button>
- <h2 class="text-lg font-bold">补货单详情</h2>
- </div>
- <div v-if="!loading && isDraft()" class="flex gap-2">
- <el-button type="primary" :loading="submitting" @click="handleSubmitToErp">
- 提交ERP
- </el-button>
- <el-button type="success" :loading="saving" @click="handleSave">
- 保存修改
- </el-button>
- </div>
- </div>
- <el-card v-loading="loading" class="mb-4">
- <template #header>
- <div class="flex items-center justify-between">
- <span class="font-bold">基本信息</span>
- <el-tag
- v-if="order.status != null"
- :type="(statusMap[order.status]?.type as any) || 'info'"
- >
- {{ (statusMap[order.status] as any)?.text || "未知" }}
- </el-tag>
- </div>
- </template>
- <el-descriptions :column="3" border>
- <el-descriptions-item label="补货单号">
- {{ order.orderNo }}
- </el-descriptions-item>
- <el-descriptions-item label="设备ID">
- {{ order.deviceId }}
- </el-descriptions-item>
- <el-descriptions-item label="门店">
- {{ order.shopName || order.shopId || "-" }}
- </el-descriptions-item>
- <el-descriptions-item v-if="canEdit()" label="供应商">
- <el-input v-model="order.supplierName" placeholder="供应商" size="small" style="width:160px;" />
- </el-descriptions-item>
- <el-descriptions-item v-else label="供应商">
- {{ order.supplierName || "-" }}
- </el-descriptions-item>
- <el-descriptions-item v-if="canEdit()" label="仓库">
- <el-input v-model="order.warehouseName" placeholder="仓库" size="small" style="width:160px;" />
- </el-descriptions-item>
- <el-descriptions-item v-else label="仓库">
- {{ order.warehouseName || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="ERP采购单号">
- {{ order.erpOrderNo || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="总数量">
- {{ items.reduce((s: number, i: any) => s + (i.plannedQuantity || 0), 0) }}
- </el-descriptions-item>
- <el-descriptions-item label="总金额">
- ¥{{ items.reduce((s: number, i: any) => s + (i.plannedQuantity || 0) * (i.unitPrice || 0), 0).toFixed(2) }}
- </el-descriptions-item>
- <el-descriptions-item label="预计到货时间">
- {{ order.expectedArrivalTime || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="创建人">
- {{ order.creatorName || "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="创建时间">
- {{ order.createTime ? dayjs(order.createTime).format("YYYY-MM-DD HH:mm:ss") : "-" }}
- </el-descriptions-item>
- <el-descriptions-item v-if="canEdit()" label="备注" :span="2">
- <el-input v-model="order.remark" placeholder="备注" size="small" />
- </el-descriptions-item>
- <el-descriptions-item v-else label="备注" :span="2">
- {{ order.remark || "-" }}
- </el-descriptions-item>
- </el-descriptions>
- </el-card>
- <el-card v-loading="loading">
- <template #header>
- <div class="flex items-center justify-between">
- <span class="font-bold">补货明细</span>
- <el-button v-if="canEdit()" type="primary" size="small" :icon="'ep:plus'" @click="addItem">
- 添加商品
- </el-button>
- </div>
- </template>
- <el-table :data="items" border stripe>
- <el-table-column label="图片" width="70">
- <template #default="{ row }">
- <img
- v-if="row.productImage"
- :src="row.productImage"
- style="width:40px;height:40px;object-fit:cover;border-radius:4px;"
- @error="$event.target.style.display='none'"
- />
- <span v-else style="color:#ccc;">-</span>
- </template>
- </el-table-column>
- <el-table-column label="商品编码" min-width="120">
- <template #default="{ row }">
- <el-input v-if="canEdit()" v-model="row.productCode" placeholder="商品编码" size="small" />
- <span v-else>{{ row.productCode || "-" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="商品名称" min-width="140">
- <template #default="{ row }">
- <el-input v-if="canEdit()" v-model="row.productName" placeholder="商品名称" size="small" />
- <span v-else>{{ row.productName || "-" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="计划数量" width="110">
- <template #default="{ row }">
- <el-input-number
- v-if="canEdit()"
- v-model="row.plannedQuantity"
- :min="1"
- :max="99999"
- size="small"
- controls-position="right"
- />
- <span v-else>{{ row.plannedQuantity }}</span>
- </template>
- </el-table-column>
- <el-table-column label="单价" width="110">
- <template #default="{ row }">
- <el-input-number
- v-if="canEdit()"
- v-model="row.unitPrice"
- :min="0"
- :precision="2"
- placeholder="单价"
- size="small"
- controls-position="right"
- />
- <span v-else>{{ row.unitPrice != null ? `¥${Number(row.unitPrice).toFixed(2)}` : "-" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="小计" width="100">
- <template #default="{ row }">
- <span class="font-medium">¥{{ itemSubtotal(row) }}</span>
- </template>
- </el-table-column>
- <el-table-column label="层号" width="70">
- <template #default="{ row }">
- <el-input-number
- v-if="canEdit()"
- v-model="row.shelfNum"
- :min="1"
- placeholder="层"
- size="small"
- controls-position="right"
- />
- <span v-else>{{ row.shelfNum != null ? row.shelfNum : "-" }}</span>
- </template>
- </el-table-column>
- <el-table-column label="货道" width="90">
- <template #default="{ row }">
- <el-input v-if="canEdit()" v-model="row.position" placeholder="位置" size="small" />
- <span v-else>{{ row.position || "-" }}</span>
- </template>
- </el-table-column>
- <el-table-column v-if="canEdit()" label="操作" width="70" fixed="right">
- <template #default="{ $index }">
- <el-button
- type="danger"
- size="small"
- :icon="useRenderIcon(DeleteIcon)"
- :disabled="items.length <= 1"
- @click="removeItem($index)"
- />
- </template>
- </el-table-column>
- </el-table>
- </el-card>
- </div>
- </template>
|