mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-08-06 10:57:26 +08:00
feat (router): 新增采购详情路由,优化预警数量管理功能
在管理员模块与商家模块中新增采购详情页面路由。 升级预警数量管理组件,优化交互体验与功能逻辑。
This commit is contained in:
38
manager/src/api/procurement.js
Normal file
38
manager/src/api/procurement.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
getRequest,
|
||||
postRequest,
|
||||
putRequest,
|
||||
deleteRequest,
|
||||
} from "@/libs/axios";
|
||||
|
||||
// ========== 采购单(只读) ==========
|
||||
export const getProcurementOrderPage = params => getRequest("/procurement/order", params);
|
||||
export const getProcurementOrderDetail = id => getRequest(`/procurement/order/${id}`);
|
||||
|
||||
// ========== 采购入库单(只读) ==========
|
||||
export const getProcurementInboundPage = params => getRequest("/procurement/inbound", params);
|
||||
export const getProcurementInboundDetail = id => getRequest(`/procurement/inbound/${id}`);
|
||||
export const getProcurementInboundItems = id => getRequest(`/procurement/inbound/${id}/items`);
|
||||
|
||||
// ========== 盘点单(只读) ==========
|
||||
export const getInventoryCountPage = params => getRequest("/procurement/inventory-count/page", params);
|
||||
export const getInventoryCountDetail = id => getRequest(`/procurement/inventory-count/${id}`);
|
||||
export const getInventoryCountItemsPage = (id, params) =>
|
||||
getRequest(`/procurement/inventory-count/${id}/items/page`, params);
|
||||
export const downloadInventoryCountItems = id =>
|
||||
getRequest(`/procurement/inventory-count/${id}/download`);
|
||||
export const exportInventoryCountItems = id =>
|
||||
getRequest(`/procurement/inventory-count/${id}/export`, {}, "blob");
|
||||
|
||||
// ========== 报损单(只读) ==========
|
||||
export const getDamageReportPage = params => getRequest("/procurement/damage-report/page", params);
|
||||
export const getDamageReportDetail = id => getRequest(`/procurement/damage-report/${id}`);
|
||||
export const getDamageReportItems = id => getRequest(`/procurement/damage-report/${id}/items`);
|
||||
|
||||
// ========== 出入库原因 CRUD ==========
|
||||
export const getStockReasonPage = params => getRequest("/procurement/reason", params);
|
||||
export const addStockReason = params =>
|
||||
postRequest("/procurement/reason", params, { "Content-Type": "application/json" });
|
||||
export const updateStockReason = params =>
|
||||
putRequest("/procurement/reason", params, { "Content-Type": "application/json" });
|
||||
export const deleteStockReason = id => deleteRequest(`/procurement/reason/${id}`);
|
||||
@@ -454,6 +454,30 @@ export const otherRouter = {
|
||||
name: "goodsSalesSummaryReport",
|
||||
meta: { title: "商品销售汇总报表" },
|
||||
component: () => import("@/views/statistics/goods-sales-summary.vue")
|
||||
},
|
||||
{
|
||||
path: "procurement/purchase-order/detail",
|
||||
title: "采购单详情",
|
||||
name: "manager-procurement-order-detail",
|
||||
component: () => import("@/views/procurement/purchase-order/detail.vue")
|
||||
},
|
||||
{
|
||||
path: "procurement/inbound/detail",
|
||||
title: "入库单详情",
|
||||
name: "manager-procurement-inbound-detail",
|
||||
component: () => import("@/views/procurement/inbound/detail.vue")
|
||||
},
|
||||
{
|
||||
path: "procurement/inventory-count/detail",
|
||||
title: "盘点单详情",
|
||||
name: "manager-inventory-count-detail",
|
||||
component: () => import("@/views/procurement/inventory-count/detail.vue")
|
||||
},
|
||||
{
|
||||
path: "procurement/damage-report/detail",
|
||||
title: "报损单详情",
|
||||
name: "manager-damage-report-detail",
|
||||
component: () => import("@/views/procurement/damage-report/detail.vue")
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
91
manager/src/views/procurement/constants.js
Normal file
91
manager/src/views/procurement/constants.js
Normal file
@@ -0,0 +1,91 @@
|
||||
export const PROCUREMENT_STATUS = {
|
||||
DRAFT: "待提交",
|
||||
SUBMITTED: "已提交",
|
||||
PENDING_INBOUND: "待入库",
|
||||
PARTIAL_INBOUND: "部分入库",
|
||||
CLOSED: "已关闭",
|
||||
COMPLETED: "已完成",
|
||||
REJECTED: "已拒绝",
|
||||
};
|
||||
|
||||
export const PROCUREMENT_STATUS_TAG = {
|
||||
DRAFT: "info",
|
||||
SUBMITTED: "warning",
|
||||
PENDING_INBOUND: "primary",
|
||||
PARTIAL_INBOUND: "warning",
|
||||
CLOSED: "info",
|
||||
COMPLETED: "success",
|
||||
REJECTED: "danger",
|
||||
};
|
||||
|
||||
export const DAMAGE_STATUS = {
|
||||
DRAFT: "待提交",
|
||||
SUBMITTED: "待审核",
|
||||
APPROVED: "已通过",
|
||||
REJECTED: "已驳回",
|
||||
CANCELLED: "已作废",
|
||||
COMPLETED: "已完成",
|
||||
};
|
||||
|
||||
export const DAMAGE_STATUS_TAG = {
|
||||
DRAFT: "info",
|
||||
SUBMITTED: "warning",
|
||||
APPROVED: "primary",
|
||||
REJECTED: "danger",
|
||||
CANCELLED: "info",
|
||||
COMPLETED: "success",
|
||||
};
|
||||
|
||||
export const MARKET_ENABLE = {
|
||||
UPPER: "上架",
|
||||
DOWN: "下架",
|
||||
};
|
||||
|
||||
export const STOCK_REASON_CATEGORY = {
|
||||
INBOUND: "入库",
|
||||
OUTBOUND: "出库",
|
||||
DAMAGE: "报损",
|
||||
};
|
||||
|
||||
export function stockReasonCategoryText(category) {
|
||||
return STOCK_REASON_CATEGORY[category] || category || "-";
|
||||
}
|
||||
|
||||
export function procurementStatusText(status) {
|
||||
return PROCUREMENT_STATUS[status] || status || "-";
|
||||
}
|
||||
|
||||
export function procurementStatusTag(status) {
|
||||
return PROCUREMENT_STATUS_TAG[status] || "info";
|
||||
}
|
||||
|
||||
export function damageStatusText(status) {
|
||||
return DAMAGE_STATUS[status] || status || "-";
|
||||
}
|
||||
|
||||
export function damageStatusTag(status) {
|
||||
return DAMAGE_STATUS_TAG[status] || "info";
|
||||
}
|
||||
|
||||
export function formatMoney(val) {
|
||||
if (val === null || val === undefined || val === "") return "0.00";
|
||||
return Number(val).toFixed(2);
|
||||
}
|
||||
|
||||
export function exportCsv(filename, headers, rows) {
|
||||
const escape = (v) => {
|
||||
const s = v == null ? "" : String(v);
|
||||
if (s.includes(",") || s.includes('"') || s.includes("\n")) {
|
||||
return `"${s.replace(/"/g, '""')}"`;
|
||||
}
|
||||
return s;
|
||||
};
|
||||
const lines = [headers.map(escape).join(",")];
|
||||
rows.forEach((row) => lines.push(row.map(escape).join(",")));
|
||||
const blob = new Blob(["\uFEFF" + lines.join("\n")], { type: "text/csv;charset=utf-8;" });
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
}
|
||||
83
manager/src/views/procurement/damage-report/detail.vue
Normal file
83
manager/src/views/procurement/damage-report/detail.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>报损单详情</span>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions v-if="detail.id" :column="2" border>
|
||||
<el-descriptions-item label="报损单号">{{ detail.sn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="damageStatusTag(detail.status)">{{ damageStatusText(detail.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建人">{{ detail.makerName || detail.createBy || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ detail.createTime || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核人">{{ detail.auditorName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">{{ detail.auditTime || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报损日期">{{ detail.damageDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总数量">{{ detail.totalQuantity }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总金额">¥{{ formatMoney(detail.totalAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detail.remark || "-" }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-table :data="items" border class="mt_10 item-table" style="width: 100%">
|
||||
<el-table-column prop="goodsName" label="商品名称" class-name="goods-name-col" width="200">
|
||||
<template #default="{ row }">{{ row.goodsName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="skuId" label="SKU ID" class-name="sku-id-col" width="200">
|
||||
<template #default="{ row }">{{ row.skuId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="报损数量" width="90" align="center" />
|
||||
<el-table-column label="单价" width="110" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.unitPrice) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" width="100" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.amount) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDamageReportDetail, getDamageReportItems } from "@/api/procurement";
|
||||
import { damageStatusText, damageStatusTag, formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-damage-report-detail",
|
||||
data() {
|
||||
return { detail: {}, items: [] };
|
||||
},
|
||||
mounted() {
|
||||
const id = this.$route.query.id;
|
||||
if (id) {
|
||||
getDamageReportDetail(id).then((res) => {
|
||||
if (res.success) this.detail = res.result || {};
|
||||
});
|
||||
getDamageReportItems(id).then((res) => {
|
||||
if (res.success) this.items = res.result || [];
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: { damageStatusText, damageStatusTag, formatMoney },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.item-table :deep(.sku-id-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.item-table :deep(.goods-name-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
95
manager/src/views/procurement/damage-report/list.vue
Normal file
95
manager/src/views/procurement/damage-report/list.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form :model="searchForm" inline label-width="80px">
|
||||
<el-form-item label="店铺ID">
|
||||
<el-input v-model="searchForm.storeId" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" clearable style="width: 160px">
|
||||
<el-option v-for="(label, value) in DAMAGE_STATUS" :key="value" :label="label" :value="value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="data" border style="width: 100%">
|
||||
<el-table-column prop="sn" label="报损单号" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="storeId" label="店铺ID" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="totalQuantity" label="数量" width="90" align="center" />
|
||||
<el-table-column label="金额" width="110" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.totalAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="damageStatusTag(row.status)">{{ damageStatusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goDetail(row)">详情</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getDamageReportPage } from "@/api/procurement";
|
||||
import { DAMAGE_STATUS, damageStatusText, damageStatusTag, formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-damage-report-list",
|
||||
data() {
|
||||
return {
|
||||
DAMAGE_STATUS,
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
searchForm: { pageNumber: 1, pageSize: 10, storeId: "", status: "" },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
damageStatusText,
|
||||
damageStatusTag,
|
||||
formatMoney,
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDamageReportPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
goDetail(row) {
|
||||
this.$router.push({ name: "manager-damage-report-detail", query: { id: row.id } });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
78
manager/src/views/procurement/inbound/detail.vue
Normal file
78
manager/src/views/procurement/inbound/detail.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>入库单详情</span>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions v-if="detail.id" :column="2" border>
|
||||
<el-descriptions-item label="入库单号">{{ detail.inboundSn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="采购单ID">{{ detail.procurementOrderId }}</el-descriptions-item>
|
||||
<el-descriptions-item label="本次入库数量">{{ totalInboundQuantity }}</el-descriptions-item>
|
||||
<el-descriptions-item label="入库成本">¥{{ formatMoney(detail.totalCost) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="操作人">{{ detail.operatorName }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-table :data="items" border class="mt_10 item-table" style="width: 100%">
|
||||
<el-table-column prop="goodsName" label="商品名称" class-name="goods-name-col" width="200">
|
||||
<template #default="{ row }">{{ row.goodsName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="skuId" label="SKU ID" class-name="sku-id-col" width="200">
|
||||
<template #default="{ row }">{{ row.skuId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="inboundQuantity" label="入库数量" width="90" align="center" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProcurementInboundDetail, getProcurementInboundItems } from "@/api/procurement";
|
||||
import { formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-procurement-inbound-detail",
|
||||
data() {
|
||||
return { detail: {}, items: [] };
|
||||
},
|
||||
computed: {
|
||||
totalInboundQuantity() {
|
||||
return (this.items || []).reduce(
|
||||
(sum, item) => sum + (Number(item.inboundQuantity) || 0),
|
||||
0
|
||||
);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
const id = this.$route.query.id;
|
||||
if (id) {
|
||||
getProcurementInboundDetail(id).then((res) => {
|
||||
if (res.success) this.detail = res.result || {};
|
||||
});
|
||||
getProcurementInboundItems(id).then((res) => {
|
||||
if (res.success) this.items = res.result || [];
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: { formatMoney },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.item-table :deep(.sku-id-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.item-table :deep(.goods-name-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
83
manager/src/views/procurement/inbound/list.vue
Normal file
83
manager/src/views/procurement/inbound/list.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form :model="searchForm" inline label-width="80px">
|
||||
<el-form-item label="入库单号">
|
||||
<el-input v-model="searchForm.inboundSn" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="data" border style="width: 100%">
|
||||
<el-table-column prop="inboundSn" label="入库单号" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="storeId" label="店铺ID" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="confirmedQuantity" label="已入库" width="90" align="center" />
|
||||
<el-table-column label="成本" width="110" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.totalCost) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operatorName" label="操作人" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="inboundTime" label="入库时间" width="170" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goDetail(row)">详情</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProcurementInboundPage } from "@/api/procurement";
|
||||
import { formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-procurement-inbound-list",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
searchForm: { pageNumber: 1, pageSize: 10, inboundSn: "" },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
formatMoney,
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getProcurementInboundPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
goDetail(row) {
|
||||
this.$router.push({ name: "manager-procurement-inbound-detail", query: { id: row.id } });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
106
manager/src/views/procurement/inventory-count/detail.vue
Normal file
106
manager/src/views/procurement/inventory-count/detail.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>盘点单明细 - {{ sn }}</span>
|
||||
<div>
|
||||
<el-button type="primary" :loading="exporting" @click="handleExport">导出 Excel</el-button>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table v-loading="loading" :data="data" border class="item-table" style="width: 100%">
|
||||
<el-table-column prop="goodsName" label="商品名称" class-name="goods-name-col" width="200">
|
||||
<template #default="{ row }">{{ row.goodsName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="skuName" label="规格" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="skuId" label="SKU ID" class-name="sku-id-col" width="200">
|
||||
<template #default="{ row }">{{ row.skuId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="库存数量" width="100" align="center" />
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInventoryCountItemsPage, exportInventoryCountItems } from "@/api/procurement";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "manager-inventory-count-detail",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
exporting: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
sn: "",
|
||||
searchForm: { pageNumber: 1, pageSize: 20 },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.sn = this.$route.query.sn || "";
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
getDataList() {
|
||||
const id = this.$route.query.id;
|
||||
if (!id) return;
|
||||
this.loading = true;
|
||||
getInventoryCountItemsPage(id, this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
const id = this.$route.query.id;
|
||||
if (!id) return;
|
||||
this.exporting = true;
|
||||
exportInventoryCountItems(id)
|
||||
.then((blob) => {
|
||||
downloadBlob(blob, `盘点单_${this.sn || id}.xlsx`);
|
||||
})
|
||||
.finally(() => {
|
||||
this.exporting = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.item-table :deep(.sku-id-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.item-table :deep(.goods-name-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
66
manager/src/views/procurement/inventory-count/list.vue
Normal file
66
manager/src/views/procurement/inventory-count/list.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-table v-loading="loading" :data="data" border style="width: 100%">
|
||||
<el-table-column prop="sn" label="盘点单号" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="storeId" label="店铺ID" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="itemTotal" label="商品数" width="90" align="center" />
|
||||
<el-table-column prop="makerName" label="制单人" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="countTime" label="盘点时间" width="170" />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goDetail(row)">详情</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getInventoryCountPage } from "@/api/procurement";
|
||||
|
||||
export default {
|
||||
name: "manager-inventory-count-list",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
searchForm: { pageNumber: 1, pageSize: 10 },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getInventoryCountPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
goDetail(row) {
|
||||
this.$router.push({ name: "manager-inventory-count-detail", query: { id: row.id, sn: row.sn } });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
92
manager/src/views/procurement/purchase-order/detail.vue
Normal file
92
manager/src/views/procurement/purchase-order/detail.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>采购单详情</span>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions v-if="detail.id" :column="2" border>
|
||||
<el-descriptions-item label="采购单号">{{ detail.orderSn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="店铺">{{ detail.storeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag :type="procurementStatusTag(detail.status)">{{ procurementStatusText(detail.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="制单人">{{ detail.makerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核人">{{ detail.auditorName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">{{ detail.auditTime || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总数量">{{ detail.totalQuantity }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总金额">¥{{ formatMoney(detail.totalAmount) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出入库原因">{{ stockReasonText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-table :data="detail.items || []" border class="mt_10 item-table" style="width: 100%">
|
||||
<el-table-column prop="goodsName" label="商品名称" class-name="goods-name-col" width="200">
|
||||
<template #default="{ row }">{{ row.goodsName }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="skuId" label="SKU ID" class-name="sku-id-col" width="200">
|
||||
<template #default="{ row }">{{ row.skuId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quantity" label="采购数量" width="90" align="center" />
|
||||
<el-table-column prop="receivedQuantity" label="已入库" width="80" align="center" />
|
||||
<el-table-column label="含税单价" width="110" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.unitPriceWithTax) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="含税小计" width="100" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.subtotalWithTax) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProcurementOrderDetail, getStockReasonPage } from "@/api/procurement";
|
||||
import { procurementStatusText, procurementStatusTag, formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-procurement-order-detail",
|
||||
data() {
|
||||
return { detail: {}, reasonOptions: [] };
|
||||
},
|
||||
computed: {
|
||||
stockReasonText() {
|
||||
const reason = this.reasonOptions.find((item) => item.id === this.detail.stockReasonId);
|
||||
return reason ? reason.reason : "-";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
getStockReasonPage({ category: "INBOUND", pageNumber: 1, pageSize: 100 }).then((res) => {
|
||||
if (res.success) {
|
||||
this.reasonOptions = res.result.records || [];
|
||||
}
|
||||
});
|
||||
const id = this.$route.query.id;
|
||||
if (id) {
|
||||
getProcurementOrderDetail(id).then((res) => {
|
||||
if (res.success) this.detail = res.result || {};
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: { procurementStatusText, procurementStatusTag, formatMoney },
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.item-table :deep(.sku-id-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.item-table :deep(.goods-name-col .cell) {
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
100
manager/src/views/procurement/purchase-order/list.vue
Normal file
100
manager/src/views/procurement/purchase-order/list.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form :model="searchForm" inline label-width="80px">
|
||||
<el-form-item label="单据编号">
|
||||
<el-input v-model="searchForm.orderSn" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品名称">
|
||||
<el-input v-model="searchForm.goodsName" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" clearable style="width: 160px">
|
||||
<el-option v-for="(label, value) in PROCUREMENT_STATUS" :key="value" :label="label" :value="value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="data" border style="width: 100%">
|
||||
<el-table-column prop="orderSn" label="采购单号" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="storeName" label="店铺" width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="totalQuantity" label="数量" width="90" align="center" />
|
||||
<el-table-column label="金额" width="110" align="right">
|
||||
<template #default="{ row }">¥{{ formatMoney(row.totalAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="procurementStatusTag(row.status)">{{ procurementStatusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" />
|
||||
<el-table-column prop="auditTime" label="审核时间" width="170" />
|
||||
<el-table-column prop="auditorName" label="审核人" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="80" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goDetail(row)">详情</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getProcurementOrderPage } from "@/api/procurement";
|
||||
import { PROCUREMENT_STATUS, procurementStatusText, procurementStatusTag, formatMoney } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-procurement-order-list",
|
||||
data() {
|
||||
return {
|
||||
PROCUREMENT_STATUS,
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
searchForm: { pageNumber: 1, pageSize: 10, orderSn: "", goodsName: "", status: "" },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
procurementStatusText,
|
||||
procurementStatusTag,
|
||||
formatMoney,
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getProcurementOrderPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
goDetail(row) {
|
||||
this.$router.push({ name: "manager-procurement-order-detail", query: { id: row.id } });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
169
manager/src/views/procurement/reason/reasonManage.vue
Normal file
169
manager/src/views/procurement/reason/reasonManage.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form :model="searchForm" inline label-width="80px">
|
||||
<el-form-item label="原因">
|
||||
<el-input v-model="searchForm.reason" clearable style="width: 200px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类别">
|
||||
<el-select v-model="searchForm.category" clearable style="width: 160px">
|
||||
<el-option label="入库" value="INBOUND" />
|
||||
<el-option label="出库" value="OUTBOUND" />
|
||||
<el-option label="报损" value="DAMAGE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="operation padding-row">
|
||||
<el-button type="primary" @click="openAdd">添加</el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="data" border class="mt_10" style="width: 100%">
|
||||
<el-table-column prop="reason" label="原因" width="240" show-overflow-tooltip />
|
||||
<el-table-column label="类别" width="120" align="center">
|
||||
<template #default="{ row }">{{ stockReasonCategoryText(row.category) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170" />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="openEdit(row)">编辑</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="handleDelete(row)">删除</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
size="small"
|
||||
@current-change="getDataList"
|
||||
@size-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="modalVisible" :title="modalTitle" width="480px" append-to-body>
|
||||
<el-form ref="formRef" :model="form" label-width="80px">
|
||||
<el-form-item label="原因" required>
|
||||
<el-input v-model="form.reason" placeholder="出入库原因" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类别" required>
|
||||
<el-select v-model="form.category" style="width: 100%">
|
||||
<el-option label="入库" value="INBOUND" />
|
||||
<el-option label="出库" value="OUTBOUND" />
|
||||
<el-option label="报损" value="DAMAGE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="modalVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getStockReasonPage,
|
||||
addStockReason,
|
||||
updateStockReason,
|
||||
deleteStockReason,
|
||||
} from "@/api/procurement";
|
||||
import { stockReasonCategoryText } from "../constants";
|
||||
|
||||
export default {
|
||||
name: "manager-stock-reason-manage",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
submitLoading: false,
|
||||
modalVisible: false,
|
||||
modalTitle: "添加原因",
|
||||
data: [],
|
||||
total: 0,
|
||||
searchForm: { pageNumber: 1, pageSize: 10, reason: "", category: "" },
|
||||
form: { id: "", reason: "", category: "INBOUND" },
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
stockReasonCategoryText,
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getStockReasonPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
openAdd() {
|
||||
this.modalTitle = "添加原因";
|
||||
this.form = { id: "", reason: "", category: "INBOUND" };
|
||||
this.modalVisible = true;
|
||||
},
|
||||
openEdit(row) {
|
||||
this.modalTitle = "编辑原因";
|
||||
this.form = { id: row.id, reason: row.reason, category: row.category };
|
||||
this.modalVisible = true;
|
||||
},
|
||||
handleSubmit() {
|
||||
if (!this.form.reason || !this.form.category) {
|
||||
this.$Message.warning("请填写完整信息");
|
||||
return;
|
||||
}
|
||||
this.submitLoading = true;
|
||||
const req = this.form.id
|
||||
? updateStockReason(this.form)
|
||||
: addStockReason({ reason: this.form.reason, category: this.form.category });
|
||||
req
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("保存成功");
|
||||
this.modalVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
handleDelete(row) {
|
||||
this.$Modal.confirm({
|
||||
title: "删除确认",
|
||||
content: `确认删除原因「${row.reason}」?`,
|
||||
onOk: () => {
|
||||
deleteStockReason(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("删除成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.op-split {
|
||||
margin: 0 6px;
|
||||
color: #dcdfe6;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user