From f2485e01843a569549bef74aa65ac44df3cfaa3b Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期二, 03 二月 2026 17:58:21 +0800
Subject: [PATCH] 库存管理-合格库存 增加条形码和二维码
---
src/views/basicData/product/index.vue | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 354 insertions(+), 26 deletions(-)
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index 3f0921a..4b36793 100644
--- a/src/views/basicData/product/index.vue
+++ b/src/views/basicData/product/index.vue
@@ -30,11 +30,8 @@
:props="{ children: 'children', label: 'label' }"
highlight-current
node-key="id"
- style="
- height: calc(100vh - 190px);
- overflow-y: scroll;
- scrollbar-width: none;
- "
+ class="product-tree-scroll"
+ style="height: calc(100vh - 190px); overflow-y: auto"
>
<template #default="{ node, data }">
<div class="custom-tree-node">
@@ -43,7 +40,7 @@
<component :is="data.children && data.children.length > 0
? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
</el-icon>
- {{ data.label }}
+ <span class="tree-node-label">{{ data.label }}</span>
</span>
<div>
<el-button
@@ -95,7 +92,18 @@
@selection-change="handleSelectionChange"
:tableLoading="tableLoading"
@pagination="pagination"
- ></PIMTable>
+ >
+ <template #productImage="{ row }">
+ <img
+ v-if="row.url"
+ class="upload-img"
+ :src="javaApiUrl + row.url"
+ @click="previewImage(row.url)"
+ style="cursor: pointer"
+ />
+ <span v-else style="color: #909399">鏆傛棤鍥剧墖</span>
+ </template>
+ </PIMTable>
</div>
<el-dialog v-model="productDia" title="浜у搧" width="400px" @keydown.enter.prevent>
<el-form
@@ -111,6 +119,8 @@
<el-input
v-model="form.productName"
placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+ maxlength="20"
+ show-word-limit
clearable
@keydown.enter.prevent
/>
@@ -128,7 +138,7 @@
<el-dialog
v-model="modelDia"
title="瑙勬牸鍨嬪彿"
- width="400px"
+ width="600px"
@close="closeModelDia"
@keydown.enter.prevent
>
@@ -139,8 +149,8 @@
:rules="modelRules"
ref="modelFormRef"
>
- <el-row>
- <el-col :span="24">
+ <el-row :gutter="20">
+ <el-col :span="12">
<el-form-item label="瑙勬牸鍨嬪彿锛�" prop="model">
<el-input
v-model="modelForm.model"
@@ -150,9 +160,7 @@
/>
</el-form-item>
</el-col>
- </el-row>
- <el-row>
- <el-col :span="24">
+ <el-col :span="12">
<el-form-item label="鍗曚綅锛�" prop="unit">
<el-input
v-model="modelForm.unit"
@@ -160,6 +168,79 @@
clearable
@keydown.enter.prevent
/>
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="楂樺害锛�" prop="height">
+ <el-input
+ v-model="modelForm.height"
+ placeholder="璇疯緭鍏ラ珮搴�"
+ clearable
+ @keydown.enter.prevent
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="姣忎欢鏁伴噺/鏀細" prop="boxNum">
+ <el-input-number
+ :step="1"
+ :min="0"
+ style="width: 100%"
+ v-model="modelForm.boxNum"
+ @change="calculateTotalPrice"
+ placeholder="璇疯緭鍏ユ瘡浠舵暟閲�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <el-form-item label="鍗曚环(鍏�)/浠讹細" prop="taxInclusiveUnitPrice">
+ <el-input-number
+ :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="modelForm.taxInclusiveUnitPrice"
+ @change="calculateTotalPrice"
+ placeholder="璇疯緭鍏ュ崟浠�"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鍗曚环(缇庡厓)/浠讹細" prop="dollarPrice">
+ <el-input-number
+ :step="0.01"
+ :min="0"
+ style="width: 100%"
+ v-model="modelForm.dollarPrice"
+ placeholder="璇疯緭鍏ョ編鍏冨崟浠�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="浜у搧鍥剧墖锛�" prop="url">
+ <el-upload
+ :action="uploadUrl"
+ :before-upload="handleBeforeUpload"
+ :on-success="(res, file) => handleUploadSuccess(res, file)"
+ :on-error="handleUploadError"
+ name="file"
+ :show-file-list="false"
+ :headers="headers"
+ accept="image/*"
+ :data="{ type: 13 }"
+ >
+ <img
+ v-if="modelForm.url"
+ class="upload-img-dialog"
+ :src="javaApiUrl + modelForm.url"
+ />
+ <el-icon v-else class="avatar-uploader-icon-dialog"><Plus /></el-icon>
+ </el-upload>
</el-form-item>
</el-col>
</el-row>
@@ -175,8 +256,10 @@
</template>
<script setup>
-import { ref } from "vue";
+import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import { ElMessageBox } from "element-plus";
+import { Plus } from "@element-plus/icons-vue";
+import { getToken } from "@/utils/auth";
import {
addOrEditProduct,
addOrEditProductModel,
@@ -186,6 +269,7 @@
productTreeList,
} from "@/api/basicData/product.js";
import ImportExcel from "./ImportExcel/index.vue";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
const { proxy } = getCurrentInstance();
const tree = ref(null);
@@ -203,12 +287,40 @@
const expandedKeys = ref([]);
const tableColumn = ref([
{
+ label: "浜у搧鍥剧墖",
+ prop: "url",
+ dataType: "slot",
+ slot: "productImage",
+ align: "center",
+ width: 100,
+ },
+ {
label: "瑙勬牸鍨嬪彿",
prop: "model",
},
{
label: "鍗曚綅",
prop: "unit",
+ },
+ {
+ label: "楂樺害",
+ prop: "height",
+ width: 120,
+ },
+ {
+ label: "姣忎欢鏁伴噺/鏀�",
+ prop: "boxNum",
+ width: 120,
+ },
+ {
+ label: "鍗曚环(鍏�)/浠�",
+ prop: "taxInclusiveUnitPrice",
+ width: 120,
+ },
+ {
+ label: "鍗曚环(缇庡厓)/浠�",
+ prop: "dollarPrice",
+ width: 130,
},
{
dataType: "action",
@@ -229,6 +341,11 @@
const tableLoading = ref(false);
const isShowButton = ref(false);
const selectedRows = ref([]);
+
+// 涓婁紶閰嶇疆
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const javaApiUrl = proxy.javaApi || import.meta.env.VITE_APP_BASE_API;
const page = reactive({
current: 1,
size: 10,
@@ -239,15 +356,28 @@
productName: "",
},
rules: {
- productName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ productName: [
+ { required: true, message: "璇疯緭鍏�", trigger: "blur" },
+ { max: 20, message: "浜у搧鍚嶇О涓嶈兘瓒呰繃20涓瓧绗�", trigger: "blur" },
+ ],
},
modelForm: {
model: "",
unit: "",
+ url: "",
+ height: "",
+ boxNum: null,
+ taxInclusiveUnitPrice: null,
+ dollarPrice: null,
},
modelRules: {
- model: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
- unit: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+ model: [{ required: true, message: "璇疯緭鍏ヨ鏍煎瀷鍙�", trigger: "blur" }],
+ unit: [{ required: true, message: "璇疯緭鍏ュ崟浣�", trigger: "blur" }],
+ url: [{ required: true, message: "璇蜂笂浼犱骇鍝佸浘鐗�", trigger: "change" }],
+ height: [{ required: true, message: "璇疯緭鍏ラ珮搴�", trigger: "blur" }],
+ boxNum: [{ required: true, message: "璇疯緭鍏ユ瘡浠舵暟閲�/鏀�", trigger: "change" }],
+ taxInclusiveUnitPrice: [{ required: true, message: "璇疯緭鍏ュ崟浠�(鍏�)/浠�", trigger: "change" }],
+ dollarPrice: [{ required: true, message: "璇疯緭鍏ュ崟浠�(缇庡厓)/浠�", trigger: "change" }],
},
});
const { form, rules, modelForm, modelRules } = toRefs(data);
@@ -283,11 +413,30 @@
const openModelDia = (type, data) => {
modelOperationType.value = type;
modelDia.value = true;
- modelForm.value.model = "";
- modelForm.value.model = "";
- modelForm.value.id = "";
- if (type === "edit") {
- modelForm.value = { ...data };
+ // 閲嶇疆琛ㄥ崟
+ modelForm.value = {
+ model: "",
+ unit: "",
+ url: "",
+ height: "",
+ boxNum: null,
+ taxInclusiveUnitPrice: null,
+ dollarPrice: null,
+ id: "",
+ };
+ if (type === "edit" && data) {
+ // 濡傛灉 url 鏄� Windows 璺緞锛岄渶瑕佽浆鎹�
+ let url = data.url || "";
+ if (url && url.indexOf("\\") > -1) {
+ url = processFileUrl(url);
+ }
+ modelForm.value = {
+ ...data,
+ url: url,
+ boxNum: data.boxNum || null,
+ taxInclusiveUnitPrice: data.taxInclusiveUnitPrice || null,
+ dollarPrice: data.dollarPrice || null,
+ };
}
};
// 鎻愪氦浜у搧鍚嶇О淇敼
@@ -389,10 +538,107 @@
size: page.size,
}).then((res) => {
console.log("res", res);
- tableData.value = res.records;
+ // 澶勭悊杩斿洖鐨勬暟鎹紝杞崲 Windows 璺緞涓哄彲璁块棶鐨� URL
+ tableData.value = (res.records || []).map((item) => {
+ if (item.url && item.url.indexOf("\\") > -1) {
+ item.url = processFileUrl(item.url);
+ }
+ return item;
+ });
page.total = res.total;
tableLoading.value = false;
});
+};
+
+// 涓婁紶鍓嶆牎楠�
+const handleBeforeUpload = (file) => {
+ const isImage = file.type.startsWith("image/");
+ const isLt5M = file.size / 1024 / 1024 < 5;
+
+ if (!isImage) {
+ proxy.$modal.msgError("涓婁紶鏂囦欢鍙兘鏄浘鐗囨牸寮�!");
+ return false;
+ }
+ if (!isLt5M) {
+ proxy.$modal.msgError("涓婁紶鍥剧墖澶у皬涓嶈兘瓒呰繃 5MB!");
+ return false;
+ }
+ return true;
+};
+
+// 澶勭悊鏂囦欢璺緞锛氬皢 Windows 璺緞杞崲涓哄彲璁块棶鐨� URL
+const processFileUrl = (filePath) => {
+ if (!filePath) return "";
+
+ // 濡傛灉璺緞鏄� Windows 璺緞鏍煎紡锛堝寘鍚弽鏂滄潬锛夛紝闇�瑕佽浆鎹�
+ if (filePath && filePath.indexOf("\\") > -1) {
+ // 鏌ユ壘 temp 鎴� uploads 鍏抽敭瀛楃殑浣嶇疆
+ const tempIndex = filePath.toLowerCase().indexOf("temp");
+ const uploadsIndex = filePath.toLowerCase().indexOf("uploads");
+
+ if (tempIndex > -1) {
+ // 浠� temp 寮�濮嬫彁鍙栫浉瀵硅矾寰勶紝骞跺皢鍙嶆枩鏉犳浛鎹负姝f枩鏉�
+ const relativePath = filePath.substring(tempIndex).replace(/\\/g, "/");
+ filePath = "/" + relativePath;
+ } else if (uploadsIndex > -1) {
+ // 浠� uploads 寮�濮嬫彁鍙栫浉瀵硅矾寰�
+ const relativePath = filePath.substring(uploadsIndex).replace(/\\/g, "/");
+ filePath = "/" + relativePath;
+ } else {
+ // 濡傛灉娌℃湁鎵惧埌鍏抽敭瀛楋紝鎻愬彇鏂囦欢鍚�
+ const parts = filePath.split("\\");
+ const fileName = parts[parts.length - 1];
+ filePath = "/temp/uploads/" + fileName;
+ }
+ }
+
+ // 纭繚璺緞浠� / 寮�澶�
+ if (filePath && !filePath.startsWith("/")) {
+ filePath = "/" + filePath;
+ }
+
+ return filePath;
+};
+
+// 涓婁紶鎴愬姛
+const handleUploadSuccess = (res, file) => {
+ if (res.code === 200) {
+ // 浠� res.data 涓幏鍙� tempPath锛屽苟杞崲涓哄彲璁块棶鐨� URL
+ const tempPath = res.data?.tempPath || res.tempPath;
+ if (tempPath) {
+ const relativePath = processFileUrl(tempPath);
+ modelForm.value.url = relativePath;
+ proxy.$modal.msgSuccess("涓婁紶鎴愬姛");
+ // 瑙﹀彂琛ㄥ崟楠岃瘉
+ nextTick(() => {
+ proxy.$refs.modelFormRef?.validateField("url");
+ });
+ } else {
+ proxy.$modal.msgError("涓婁紶鎴愬姛浣嗘湭杩斿洖鏂囦欢璺緞");
+ }
+ } else {
+ proxy.$modal.msgError(res.msg || "涓婁紶澶辫触");
+ }
+};
+
+// 涓婁紶澶辫触
+const handleUploadError = () => {
+ proxy.$modal.msgError("涓婁紶澶辫触锛岃閲嶈瘯");
+};
+
+// 璁$畻鎬讳环
+const calculateTotalPrice = () => {
+ // 濡傛灉闇�瑕佽绠楁�讳环锛屽彲浠ュ湪杩欓噷娣诲姞閫昏緫
+ // if (modelForm.value.boxNum && modelForm.value.taxInclusiveUnitPrice) {
+ // modelForm.value.totalPrice = modelForm.value.boxNum * modelForm.value.taxInclusiveUnitPrice;
+ // }
+};
+
+// 棰勮鍥剧墖
+const previewImage = (url) => {
+ if (url) {
+ window.open(javaApiUrl + url, "_blank");
+ }
};
// 鍒犻櫎瑙勬牸鍨嬪彿
const handleDelete = () => {
@@ -467,18 +713,21 @@
display: flex;
}
.left {
- width: 380px;
+ width: 450px;
+ min-width: 450px;
padding: 16px;
background: #ffffff;
}
.right {
- width: calc(100% - 380px);
+ flex: 1;
+ min-width: 0;
padding: 16px;
margin-left: 20px;
background: #ffffff;
}
.custom-tree-node {
flex: 1;
+ min-width: 0;
display: flex;
align-items: center;
justify-content: space-between;
@@ -486,13 +735,92 @@
padding-right: 8px;
}
.tree-node-content {
+ flex: 1;
+ min-width: 0;
display: flex;
- align-items: center; /* 鍨傜洿灞呬腑 */
+ align-items: center;
height: 100%;
+ overflow: hidden;
+}
+.tree-node-content .orange-icon {
+ flex-shrink: 0;
+}
+.tree-node-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.orange-icon {
color: orange;
font-size: 18px;
margin-right: 8px; /* 鍥炬爣涓庢枃瀛椾箣闂村姞鐐归棿璺� */
}
+.product-tree-scroll {
+ scrollbar-width: thin;
+ scrollbar-color: #c0c4cc #f5f7fa;
+}
+.product-tree-scroll::-webkit-scrollbar {
+ width: 8px;
+}
+.product-tree-scroll::-webkit-scrollbar-track {
+ background: #f5f7fa;
+ border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb {
+ background: #c0c4cc;
+ border-radius: 4px;
+}
+.product-tree-scroll::-webkit-scrollbar-thumb:hover {
+ background: #909399;
+}
+
+.upload-img {
+ width: 50px;
+ height: 50px;
+ object-fit: cover;
+ cursor: pointer;
+ border-radius: 4px;
+}
+
+.avatar-uploader-icon {
+ font-size: 28px;
+ color: #8c939d;
+ width: 50px;
+ height: 50px;
+ line-height: 50px;
+ text-align: center;
+ border: 1px dashed #d9d9d9;
+ border-radius: 4px;
+ cursor: pointer;
+}
+
+.avatar-uploader-icon:hover {
+ border-color: #409eff;
+}
+
+.upload-img-dialog {
+ width: 100px;
+ height: 100px;
+ object-fit: cover;
+ cursor: pointer;
+ border-radius: 4px;
+ border: 1px solid #dcdfe6;
+}
+
+.avatar-uploader-icon-dialog {
+ font-size: 28px;
+ color: #8c939d;
+ width: 100px;
+ height: 100px;
+ line-height: 100px;
+ text-align: center;
+ border: 1px dashed #d9d9d9;
+ border-radius: 4px;
+ cursor: pointer;
+ display: block;
+}
+
+.avatar-uploader-icon-dialog:hover {
+ border-color: #409eff;
+}
</style>
--
Gitblit v1.9.3