From cc87985576a7caf844db38c3c5a1e0ee5d0738ca Mon Sep 17 00:00:00 2001 From: c Date: Sat, 21 Mar 2026 15:56:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=AC=AC=E4=B8=80=E7=89=88=E6=99=AE?= =?UTF-8?q?=E9=81=8D=E4=BF=AE=E5=A4=8D=E5=AE=8C=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.production | 2 +- package.json | 2 +- src/common/api/user.ts | 6 +- src/common/http/request.ts | 18 +++--- src/common/languages/locales/en-US.ts | 21 ++++++- src/common/languages/locales/zh-CN.ts | 21 +++++++ .../base/base-qrcode/QrCodeDialog.vue | 1 + src/pinia/stores/core/user.ts | 51 ++++++++-------- src/views/core/login/loginForm.vue | 20 +++---- src/views/production/bom/BomView.vue | 2 +- .../FinishedProductShipmentView.vue | 2 + .../purchase-order/PurchaseOrderView.vue | 59 +++++++++++++++++-- .../purchase-plan/PurchasePlanView.vue | 33 ++++++++--- .../StockTransferOrderView.vue | 14 +++-- .../warehouse-item/WarehouseItemView.vue | 32 ++++++---- .../WarehouseReceiptView.vue | 11 ++-- vite.config.mts | 16 ++--- 17 files changed, 218 insertions(+), 93 deletions(-) diff --git a/.env.production b/.env.production index e097007..b8c26cc 100644 --- a/.env.production +++ b/.env.production @@ -1,5 +1,5 @@ # 线上环境接口地址 -VITE_API_URL = "/pro" +VITE_API_URL = "/api" # 静态文件获取根路径 VITE_PUBLIC_PATH = "/" diff --git a/package.json b/package.json index 20d2f29..8aa0324 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "teek-design-vue3-template", + "name": "erp-frontend", "version": "2.0.0", "private": true, "description": "牛安后台管理系统", diff --git a/src/common/api/user.ts b/src/common/api/user.ts index ab6c24e..d86b611 100644 --- a/src/common/api/user.ts +++ b/src/common/api/user.ts @@ -21,7 +21,7 @@ export const UserService = { }, checkIsLogined() { - return get("/open/check-login"); + return get("/auth/check-login"); }, /** @@ -29,12 +29,12 @@ export const UserService = { * 用于前端页面刷新后检测当前token是否有效 */ checkLogin() { - return get("/open/check-login"); + return get("/auth/check-login"); }, // 获取用户信息 getUserInfo() { - return get("/auth/getUserInfo"); + return get("/auth/check-login"); }, getDynamicRouter() { diff --git a/src/common/http/request.ts b/src/common/http/request.ts index b661ef5..275f6bd 100644 --- a/src/common/http/request.ts +++ b/src/common/http/request.ts @@ -2,6 +2,7 @@ import { useUserStore } from "@/pinia"; import axios from "axios"; import type { AxiosInstance, AxiosResponse, InternalAxiosRequestConfig } from "axios"; import { ElMessage } from "element-plus"; +import { $t } from "@/common/languages"; // 创建 axios 实例 const service: AxiosInstance = axios.create({ @@ -24,7 +25,7 @@ service.interceptors.request.use( return config; }, error => { - ElMessage.error("请求发送失败"); + ElMessage.error($t("_http.request_failed")); return Promise.reject(error); } ); @@ -38,31 +39,34 @@ service.interceptors.response.use( // 通常 code === 0 表示成功 if (res.code === 0) { return res as any; // 直接返回业务数据 + } else if (res.code === 3) { + ElMessage.error(res.msg || $t("_http.invalid_credentials")); + return Promise.reject(new Error(res.msg || $t("_http.invalid_credentials"))); } else { - ElMessage.error(res.msg || "请求失败"); + ElMessage.error(res.msg || $t("_http.request_error")); return Promise.reject(new Error(res.msg || "Error")); } }, error => { // 处理网络错误或 HTTP 状态码非 2xx 的情况 - let message = "网络异常"; + let message = $t("_http.network_error"); const { isLogined } = storeToRefs(useUserStore()); if (error.response) { const status = error.response.status; switch (status) { case 401: - message = "未登录或会话已过期,请重新登录"; + message = $t("_http.unauthorized"); isLogined.value = false; useRouter().push("/login"); break; case 403: - message = "权限不足"; + message = $t("_http.forbidden"); break; case 500: - message = "服务器内部错误"; + message = $t("_http.server_error"); break; default: - message = `请求失败 ( $ {status})`; + message = $t("_http.request_failed_status", { status }); } } ElMessage.error(message); diff --git a/src/common/languages/locales/en-US.ts b/src/common/languages/locales/en-US.ts index 1d2ade0..6dc766e 100644 --- a/src/common/languages/locales/en-US.ts +++ b/src/common/languages/locales/en-US.ts @@ -729,6 +729,12 @@ export default { generate_order_error: "Generate Failed", vendor_priority_tip: "Priority", }, + purchase_plan_item: { + input_partNumber: "Please enter part number", + input_purchaseCount: "Please enter plan count", + input_price: "Please enter price", + input_currentCount: "Please enter current count", + }, purchaseorder: { delete_message: "Delete Order", part_number_not_exists: "Part number does not exist", @@ -917,9 +923,11 @@ export default { tableTitle: "Purchase Details", templateFileName: "Purchase Plan Template", }, - purchaseorder: { + purchase_order: { add: "Add Purchase Order", edit: "Edit Purchase Order", + inbound: "Purchase Inbound", + qrcode: "Print QR Code", showItem: "Order Items", baseTitle: "Purchase Order Basic Info", tableTitle: "Order Details", @@ -1235,5 +1243,16 @@ export default { _status: { in_progress: "In Progress", completed: "Completed", + receipting: "Receipting", + }, + _http: { + request_failed: "Request failed", + invalid_credentials: "Invalid username or password", + request_error: "Request error", + network_error: "Network error", + unauthorized: "Not logged in or session expired, please log in again", + forbidden: "Permission denied", + server_error: "Server internal error", + request_failed_status: "Request failed ({status})", }, }; diff --git a/src/common/languages/locales/zh-CN.ts b/src/common/languages/locales/zh-CN.ts index ad0bf9d..01fbb85 100644 --- a/src/common/languages/locales/zh-CN.ts +++ b/src/common/languages/locales/zh-CN.ts @@ -760,6 +760,12 @@ export default { generate_order_error: "生成采购订单失败", vendor_priority_tip: "优先", }, + purchase_plan_item: { + input_partNumber: "请输入物料编号", + input_purchaseCount: "请输入计划数量", + input_price: "请输入单价", + input_currentCount: "请输入本次采购数量", + }, purchase_order: { delete_message: "删除采购订单", input_vendorName: "请选择供应商", @@ -1008,6 +1014,9 @@ export default { add: "新建采购计划", edit: "编辑采购计划", generateOrder: "生成采购订单", + showItem: "采购明细", + baseTitle: "采购计划基本信息", + tableTitle: "采购明细", model: "型号", defaultVendor: "默认供应商", demandQuantity: "需求量", @@ -1023,6 +1032,7 @@ export default { edit: "编辑采购订单", inbound: "采购入库", qrcode: "二维码打印", + showItem: "采购明细", baseTitle: "采购订单基本信息", tableTitle: "采购明细", templateFileName: "采购订单模板", @@ -1337,5 +1347,16 @@ export default { _status: { in_progress: "入库中", completed: "已完成", + receipting: "入库中", + }, + _http: { + request_failed: "请求发送失败", + invalid_credentials: "用户名或密码错误", + request_error: "请求失败", + network_error: "网络异常", + unauthorized: "未登录或会话已过期,请重新登录", + forbidden: "权限不足", + server_error: "服务器内部错误", + request_failed_status: "请求失败 ({status})", }, }; diff --git a/src/components/base/base-qrcode/QrCodeDialog.vue b/src/components/base/base-qrcode/QrCodeDialog.vue index c63ca76..98f5a4e 100644 --- a/src/components/base/base-qrcode/QrCodeDialog.vue +++ b/src/components/base/base-qrcode/QrCodeDialog.vue @@ -218,6 +218,7 @@ watch(visible, newVal => { width="450px" :close-on-click-modal="false" :close-on-press-escape="false" + :lock-scroll="false" >
diff --git a/src/pinia/stores/core/user.ts b/src/pinia/stores/core/user.ts index 1f9a75a..be133ce 100644 --- a/src/pinia/stores/core/user.ts +++ b/src/pinia/stores/core/user.ts @@ -59,33 +59,34 @@ export const useUserStore = defineStore( }; const getUserInfo = async () => { - // 模拟获取用户信息 - // return await UserService.getUserInfo().then(res => { - // setRoles(res.data.roles); - // setUserInfo(res.data); - // return res.data; - // }); - - const userInfo = await new Promise(resolve => { - setTimeout(() => { - resolve({ - userId: "v10001", - username: "Admin", - sex: "保密", - signature: "这个人很懒,什么都没有写", - email: "1234567890@qq.com", - phone: "1234567890", - avatar: "https://cdn.jsdelivr.net/gh/Kele-Bingtang/static/user/avatar1.png", - roles: ["admin"], - job: "开发工程师", - dept: "Teek 云科技技术部 - 智能全栈科", - registerTime: "2022-10-01 19:07:27", - }); - }, 500); + return await UserService.getUserInfo().then(res => { + setRoles(res.data.roles); + setUserInfo(res.data.userInfo); + return res.data.userInfo; }); - setUserInfo(userInfo); - return userInfo; + // return {}; + + // const userInfo = await new Promise(resolve => { + // setTimeout(() => { + // resolve({ + // userId: "v10001", + // username: "Admin", + // sex: "保密", + // signature: "这个人很懒,什么都没有写", + // email: "1234567890@qq.com", + // phone: "1234567890", + // avatar: "https://cdn.jsdelivr.net/gh/Kele-Bingtang/static/user/avatar1.png", + // roles: ["admin"], + // job: "开发工程师", + // dept: "Teek 云科技技术部 - 智能全栈科", + // registerTime: "2022-10-01 19:07:27", + // }); + // }, 500); + // }); + + // setUserInfo(userInfo); + // return userInfo; }; /** diff --git a/src/views/core/login/loginForm.vue b/src/views/core/login/loginForm.vue index adb6d9d..dd4de70 100644 --- a/src/views/core/login/loginForm.vue +++ b/src/views/core/login/loginForm.vue @@ -53,16 +53,16 @@ const login = () => { loading.value = true; try { // 执行登录 - await userStore.login({ ...loginForm }); - // if (!result) { - // ElNotification({ - // title: getTimeState(), - // message: "登录失败,用户名或密码错误", - // type: "success", - // duration: 3000, - // }); - // return; - // } + const result = await userStore.login({ ...loginForm }); + if (!result) { + ElNotification({ + title: getTimeState(), + message: "登录失败,用户名或密码错误", + type: "success", + duration: 3000, + }); + return; + } // 跳转到首页或者 URL 携带的 redirect 页(优先级高) let path = HOME_URL; diff --git a/src/views/production/bom/BomView.vue b/src/views/production/bom/BomView.vue index 984f3e0..fba575e 100644 --- a/src/views/production/bom/BomView.vue +++ b/src/views/production/bom/BomView.vue @@ -130,7 +130,7 @@ const operateButtonClick = (eventName: string, row: any) => { const mappingConfig: FieldMappingConfig = { partNumber: { - sourceKey: ["商品编号", "partNumber"], + sourceKey: ["编号", "物料编号", "商品编号", "partNumber"], header: $t("_prop.production.bom_item.partNumber"), width: 20, defaultValue: "", diff --git a/src/views/production/finished-product-shipment/FinishedProductShipmentView.vue b/src/views/production/finished-product-shipment/FinishedProductShipmentView.vue index 1f7f0c2..d296a20 100644 --- a/src/views/production/finished-product-shipment/FinishedProductShipmentView.vue +++ b/src/views/production/finished-product-shipment/FinishedProductShipmentView.vue @@ -238,6 +238,8 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => { if (row.status === 1 && button.eventName === "approve") return false; // 已审核后不能编辑和删除 if (row.status === 1 && (button.eventName === "edit" || button.eventName === "remove")) return false; + // 退料按钮只在已审核状态下展示 + if (button.eventName === "productionReturn" && row.status !== 1) return false; return true; }; diff --git a/src/views/purchase/purchase-order/PurchaseOrderView.vue b/src/views/purchase/purchase-order/PurchaseOrderView.vue index 15eb475..d39ceea 100644 --- a/src/views/purchase/purchase-order/PurchaseOrderView.vue +++ b/src/views/purchase/purchase-order/PurchaseOrderView.vue @@ -7,6 +7,7 @@ import { $t } from "@/common/languages"; import { ElMessage, ElMessageBox, type FormInstance, type FormItemRule, type FormRules } from "element-plus"; import BaseFormWithTable from "@/components/base/base-form-with-table/BaseFormWithTable.vue"; import BaseTableForm from "@/components/base/base-table-form/BaseTableForm.vue"; +import BaseItemDialog from "@/components/base/base-item-dialog/BaseItemDialog.vue"; import ExpandablePageableTable from "@/components/base/expandable-pageable-table/ExpandablePageableTable.vue"; import BaseSelect from "@/components/base/base-select/BaseSelect.vue"; import QrCodeDialog from "@/components/base/base-qrcode/QrCodeDialog.vue"; @@ -29,12 +30,14 @@ const partNumberExistsUrl = "/warehouse/warehouseitem/existsWarehouseItem"; const searchers = [ { name: "searchCode", type: "text" as const, placeholder: $t("_prop.purchase.purchase_order.searchCode") }, + { name: "partNumber", type: "text" as const, placeholder: $t("_prop.purchase.purchase_order_item.partNumber") }, ]; const getFormStatusLabel = (status: number) => { const labels: Record = { 0: $t("_status.in_progress"), 4: $t("_status.completed"), + 5: $t("_status.receipting"), }; return labels[status] ?? String(status); }; @@ -60,7 +63,7 @@ const validatePartNumber = (rule: any, value: string, callback: (error?: Error) }; const rules = reactive({ - vendorName: [{ required: true, message: $t("_message.purchase.purchase_order.input_vendorName"), trigger: "blur" }], + vendorNo: [{ required: true, message: $t("_message.purchase.purchase_order.select_vendor"), trigger: "change" }], partNumber: [ { required: true, @@ -97,7 +100,7 @@ const inboundForm = reactive({ orderCode: "", vendorName: "", formCode: "", - storeNo: null as number, + storeNo: null as number | null, storeName: "", formMark: "", items: [] as any[], @@ -106,6 +109,10 @@ const inboundForm = reactive({ const qrCodeVisible = ref(false); const qrCodeData = ref([]); +const showItemVisible = ref(false); +const showItemData = ref([]); +const showItemRow = ref(null); + const mappingConfig: FieldMappingConfig = { partNumber: { sourceKey: "物料编号", @@ -266,6 +273,11 @@ const showQrCode = async (row: any) => { } }; +const showItem = (row: any) => { + showItemRow.value = row; + showItemVisible.value = true; +}; + const submit = (formData: any, formRef: FormInstance | undefined) => { if (formRef !== undefined) { formRef.validate(valid => { @@ -307,6 +319,9 @@ const operateButtonClick = (eventName: string, row: any) => { case "printQrCode": showQrCode(row); break; + case "showItem": + showItem(row); + break; } }; @@ -368,6 +383,7 @@ const getFormStatusTagType = (code: number | null): string => { :item-url="getItemsUrl" item-id-key="id" item-id-name="orderId" + item-field-name="items" > diff --git a/src/views/purchase/purchase-plan/PurchasePlanView.vue b/src/views/purchase/purchase-plan/PurchasePlanView.vue index 1ad448e..5b91603 100644 --- a/src/views/purchase/purchase-plan/PurchasePlanView.vue +++ b/src/views/purchase/purchase-plan/PurchasePlanView.vue @@ -30,6 +30,7 @@ const removeUrl = "/purchase/purchaseplan/deletePurchasePlan"; const getItemsUrl = "/purchase/purchaseplan/getPurchasePlanItemsWithVendorSuggestions"; const generateOrderUrl = "/purchase/purchaseplan/generatePurchaseOrder"; const getItemUrl = "/purchase/purchaseplan/getPurchasePlanItems"; +const partNumberExistsUrl = "/warehouse/warehouseitem/existsWarehouseItem"; const searchers = [ { name: "searchCode", type: "text" as const, placeholder: $t("_prop.purchase.purchase_plan.searchCode") }, ]; @@ -53,6 +54,28 @@ const rules = reactive({ ], }); const itemArrayName = "planItems"; + +/** + * 验证物料编号是否存在 + */ +const validatePartNumber = (rule: any, value: string, callback: (error?: Error) => void) => { + if (!value) { + callback(new Error($t("_message.purchase.purchase_plan_item.input_partNumber"))); + return; + } + get(partNumberExistsUrl, { partNumber: value }) + .then(res => { + if (res.data === true) { + callback(); + } else { + callback(new Error($t("_message.warehouse.warehouse_item.not_exist_partNumber"))); + } + }) + .catch(() => { + callback(); + }); +}; + /** * 基本不变通用变量 */ @@ -445,8 +468,6 @@ const mappingConfig: FieldMappingConfig = { @@ -455,9 +476,7 @@ const mappingConfig: FieldMappingConfig = { @@ -477,9 +496,7 @@ const mappingConfig: FieldMappingConfig = { @@ -325,7 +324,14 @@ const cancelVendorEdit = (row: any, originalRow: any) => { - + diff --git a/src/views/warehouse/warehouse-receipt/WarehouseReceiptView.vue b/src/views/warehouse/warehouse-receipt/WarehouseReceiptView.vue index d65b7dd..efca874 100644 --- a/src/views/warehouse/warehouse-receipt/WarehouseReceiptView.vue +++ b/src/views/warehouse/warehouse-receipt/WarehouseReceiptView.vue @@ -161,6 +161,7 @@ const authShowFunc = (row: any, button: globalThis.ButtonProp) => { if (row.status === 0 && button.eventName === "reject") return false; if (row.status === 1 && button.eventName === "approve") return false; if (row.status === 1 && button.eventName === "edit") return false; + if (row.status === 1 && button.eventName === "remove") return false; return true; }; @@ -212,11 +213,11 @@ const submit = (form: any, formRef: FormInstance | undefined) => {