| | |
| | | "env": { |
| | | "VITE_APP_TITLE": "芯导云(管理信息系统)" |
| | | }, |
| | | "screen": "screen/JZYJView.png", |
| | | "logo": "logo/HYSNLogo.png", |
| | | "favicon": "favicon/HYSNico.ico" |
| | | "screen": "screen/SQDView.png", |
| | | "logo": "logo/SQDLogo.png", |
| | | "favicon": "favicon/SQDico.ico" |
| | | }, |
| | | "TEST": { |
| | | "SQD": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "中小企业数字化转型二级套餐包", |
| | | "VITE_BASE_API": "http://1.15.17.182:9003", |
| | | "VITE_JAVA_API": "http://1.15.17.182:9002" |
| | | "VITE_APP_TITLE": "天津双奇点管理系统", |
| | | "VITE_BASE_API": "http://114.132.189.42:9042", |
| | | "VITE_JAVA_API": "http://114.132.189.42:9044" |
| | | }, |
| | | "screen": "screen/HYSNView.png", |
| | | "logo": "logo/ZGLTLogo.png", |
| | | "favicon": "favicon/favicon.ico" |
| | | "screen": "screen/SQDView.png", |
| | | "logo": "logo/SQDLogo.png", |
| | | "favicon": "favicon/SQDico.ico" |
| | | }, |
| | | "screen": "/src/assets/images/login-background.png", |
| | | "logo": "/src/assets/logo/logo.png", |
| | |
| | | "fuse.js": "6.6.2", |
| | | "js-beautify": "1.14.11", |
| | | "js-cookie": "3.0.5", |
| | | "jsbarcode": "^3.12.3", |
| | | "jsencrypt": "3.3.2", |
| | | "nprogress": "0.2.0", |
| | | "pinia": "2.1.7", |
| | |
| | | }); |
| | | }; |
| | | |
| | | |
| | | // 根据ID查询库存记录详情 |
| | | export const getStockInventoryById = (params) => { |
| | | return request({ |
| | | url: "/stockInventory/getById", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | | |
| | | |
| | | // 创建库存记录 |
| | | export const createStockInventory = (params) => { |
| | | return request({ |
| | |
| | | }); |
| | | }; |
| | | |
| | | // 获取产品大类的select列表 |
| | | // /basic/product/listProduct |
| | | export const getProductList = (params) => { |
| | | return request({ |
| | | url: "/basic/product/listProduct", |
| | | method: "get", |
| | | params, |
| | | }); |
| | | }; |
| | |
| | | // 首页接口 |
| | | import request from "@/utils/request"; |
| | | |
| | | // 工单执行效率分析 |
| | | export const workOrderEfficiencyAnalysis = (query) => { |
| | | return request({ |
| | | url: "/home/workOrderEfficiencyAnalysis", |
| | | method: "get", |
| | | params: query, |
| | | }); |
| | | }; |
| | | |
| | | // 原材料检测 |
| | | export const rawMaterialDetection = (query) => { |
| | | return request({ |
| | |
| | | 导入 |
| | | </el-button> |
| | | <el-dialog v-model="upload.open" :title="upload.title"> |
| | | <FileUpload |
| | | ref="fileUploadRef" |
| | | <el-upload |
| | | ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | | :action="upload.url + '?updateSupport=' + upload.updateSupport" |
| | | :disabled="upload.isUploading" |
| | | :showTip="false" |
| | | @success="handleFileSuccess" |
| | | /> |
| | | :before-upload="upload.beforeUpload" |
| | | :on-progress="upload.onProgress" |
| | | :on-success="upload.onSuccess" |
| | | :on-error="upload.onError" |
| | | :on-change="upload.onChange" |
| | | :auto-upload="false" |
| | | drag |
| | | > |
| | | <el-icon class="el-icon--upload"><UploadFilled /></el-icon> |
| | | <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip text-center"> |
| | | <span>仅允许导入xls、xlsx格式文件。</span> |
| | | <el-link |
| | | type="primary" |
| | | :underline="false" |
| | | style="font-size: 12px; vertical-align: baseline" |
| | | @click="handleDownloadTemplate" |
| | | >下载模板</el-link |
| | | > |
| | | </div> |
| | | </template> |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitFileForm">确 定</el-button> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { reactive } from "vue"; |
| | | import { reactive, ref, getCurrentInstance } from "vue"; |
| | | import { getToken } from "@/utils/auth.js"; |
| | | import { FileUpload } from "@/components/Upload"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { UploadFilled } from "@element-plus/icons-vue"; |
| | | |
| | | defineOptions({ |
| | | name: "产品维护导入", |
| | | }); |
| | | |
| | | const emits = defineEmits(["uploadSuccess"]); |
| | | const fileUploadRef = ref(); |
| | | const uploadRef = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const upload = reactive({ |
| | | // 是否显示弹出层(供应商导入) |
| | | open: false, |
| | |
| | | headers: { Authorization: "Bearer " + getToken() }, |
| | | // 上传的地址 |
| | | url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import", |
| | | updateSupport: false, |
| | | beforeUpload: () => { |
| | | upload.isUploading = true; |
| | | }, |
| | | onProgress: () => {}, |
| | | onChange: () => {}, |
| | | onError: () => { |
| | | upload.isUploading = false; |
| | | ElMessage({ message: "上传失败", type: "error" }); |
| | | }, |
| | | onSuccess: (response) => { |
| | | upload.isUploading = false; |
| | | handleFileSuccess(response); |
| | | }, |
| | | }); |
| | | // 点击导入 |
| | | const handleImport = () => { |
| | |
| | | upload.title = "产品导入"; |
| | | }; |
| | | |
| | | // 下载导入模板 |
| | | const handleDownloadTemplate = () => { |
| | | proxy.download("/basic/product/downloadTemplate", {}, "产品导入模板.xlsx"); |
| | | }; |
| | | |
| | | const submitFileForm = () => { |
| | | fileUploadRef.value.uploadApi(); |
| | | uploadRef.value.submit(); |
| | | }; |
| | | |
| | | const handleFileSuccess = (response) => { |
| | |
| | | :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"> |
| | |
| | | <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 |
| | |
| | | @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 |
| | |
| | | <el-input |
| | | v-model="form.productName" |
| | | placeholder="请输入产品名称" |
| | | maxlength="20" |
| | | show-word-limit |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | |
| | | <el-dialog |
| | | v-model="modelDia" |
| | | title="规格型号" |
| | | width="400px" |
| | | width="600px" |
| | | @close="closeModelDia" |
| | | @keydown.enter.prevent |
| | | > |
| | |
| | | :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" |
| | |
| | | /> |
| | | </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" |
| | |
| | | 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> |
| | |
| | | </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, |
| | |
| | | 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); |
| | |
| | | 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", |
| | |
| | | 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, |
| | |
| | | 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); |
| | |
| | | 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, |
| | | }; |
| | | } |
| | | }; |
| | | // 提交产品名称修改 |
| | |
| | | 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 开始提取相对路径,并将反斜杠替换为正斜杠 |
| | | 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 = () => { |
| | |
| | | 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; |
| | |
| | | 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> |
| | |
| | | listKnowledgeBase({...page.value, ...searchForm.value}) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | page.value.total = res.data.total; |
| | | // 如果当前页数超过总页数,重置到第1页并重新查询 |
| | | const maxPage = Math.ceil(res.data.total / page.value.size) || 1; |
| | | if (page.value.current > maxPage && maxPage > 0) { |
| | | page.value.current = 1; |
| | | // 重新查询第1页数据 |
| | | return getList(); |
| | | } |
| | | tableData.value = res.data.records; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | |
| | | |
| | | // 分页处理 |
| | | const pagination = (obj) => { |
| | | const oldSize = page.value.size; |
| | | page.value.current = obj.page; |
| | | page.value.size = obj.limit; |
| | | handleQuery(); |
| | | // 如果 size 改变了,重置到第1页,避免当前页超出范围 |
| | | if (oldSize !== obj.limit) { |
| | | page.value.current = 1; |
| | | } |
| | | getList(); |
| | | }; |
| | | |
| | | // 选择变化处理 |
| | |
| | | </el-button> |
| | | </el-col> |
| | | </el-row> |
| | | <el-table :data="regulations" |
| | | border |
| | | v-loading="tableLoading" |
| | | style="width: 100%"> |
| | | <el-table-column prop="regulationNum" |
| | | label="制度编号" |
| | | width="120" /> |
| | | <el-table-column prop="title" |
| | | label="制度标题" |
| | | min-width="150" /> |
| | | <el-table-column prop="category" |
| | | label="分类" |
| | | width="120"> |
| | | <template #default="scope"> |
| | | <el-tag>{{ getCategoryText(scope.row.category) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="version" |
| | | label="版本" |
| | | width="120" /> |
| | | <el-table-column prop="createUserName" |
| | | label="发布人" |
| | | width="120" /> |
| | | <el-table-column prop="createTime" |
| | | label="发布时间" |
| | | width="180" /> |
| | | <el-table-column prop="status" |
| | | label="状态" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '生效中' : '已废止' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="readCount" |
| | | label="已读人数" |
| | | width="100" /> |
| | | <el-table-column label="操作" |
| | | width="320" |
| | | fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | @click="viewRegulation(scope.row)">查看</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link |
| | | type="danger" |
| | | @click="repealEdit(scope.row)">废弃</el-button> |
| | | <el-button link |
| | | type="success" |
| | | @click="viewVersionHistory(scope.row)">版本历史</el-button> |
| | | <!-- <el-button link type="warning" @click="viewReadStatus(scope.row)">阅读状态</el-button> --> |
| | | <el-button link |
| | | type="primary" |
| | | @click="openFileDialog(scope.row)">附件</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="page.total > 0" :total="page.total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="regulationTableColumn" |
| | | :tableData="regulations" |
| | | :tableLoading="tableLoading" |
| | | :page="page" |
| | | :isShowPagination="true" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | <!-- 用印申请对话框(已移除) --> |
| | |
| | | delRuleFile, |
| | | addRuleFile, |
| | | } from "@/api/collaborativeApproval/rulesRegulationsManagementFile.js"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | |
| | | // 响应式数据 |
| | | const operationType = ref("add"); |
| | |
| | | |
| | | const regulations = ref([]); |
| | | |
| | | // 表格列配置 |
| | | const regulationTableColumn = ref([ |
| | | { label: "制度编号", prop: "regulationNum"}, |
| | | { label: "制度标题", prop: "title" }, |
| | | { |
| | | label: "分类", |
| | | prop: "category", |
| | | dataType: "tag", |
| | | formatData: (v) => getCategoryText(v), |
| | | formatType: () => "info", |
| | | }, |
| | | { label: "版本", prop: "version", width: 120 }, |
| | | { label: "发布人", prop: "createUserName", width: 120 }, |
| | | { label: "发布时间", prop: "createTime", width: 180 }, |
| | | { |
| | | label: "状态", |
| | | prop: "status", |
| | | width: 100, |
| | | dataType: "tag", |
| | | formatData: (v) => (v === "active" ? "生效中" : "已废止"), |
| | | formatType: (v) => (v === "active" ? "success" : "info"), |
| | | }, |
| | | { label: "已读人数", prop: "readCount", width: 100 }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | width: 320, |
| | | fixed: "right", |
| | | align: "center", |
| | | operation: [ |
| | | { name: "查看", clickFun: (row) => viewRegulation(row) }, |
| | | { name: "编辑", clickFun: (row) => handleEdit(row) }, |
| | | { name: "废弃", clickFun: (row) => repealEdit(row) }, |
| | | { name: "版本历史", clickFun: (row) => viewVersionHistory(row) }, |
| | | { name: "附件", clickFun: (row) => openFileDialog(row) }, |
| | | ], |
| | | }, |
| | | ]); |
| | | |
| | | const versionHistory = ref([]); |
| | | |
| | | const readStatusList = ref([]); |
| | |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-table :data="sealApplications" border v-loading="tableLoading" style="width: 100%"> |
| | | <el-table-column prop="applicationNum" label="申请编号" width="120" /> |
| | | <el-table-column prop="title" label="申请标题" min-width="200" /> |
| | | <el-table-column prop="createUserName" label="申请人" width="120" /> |
| | | <el-table-column prop="department" label="所属部门" width="150" /> |
| | | <el-table-column prop="sealType" label="用印类型" width="120"> |
| | | <template #default="scope"> |
| | | {{ getSealTypeText(scope.row.sealType) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createTime" label="申请时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link @click="viewSealDetail(scope.row)">查看</el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 'pending'" |
| | | link |
| | | type="primary" |
| | | @click="approveSeal(scope.row)" |
| | | > |
| | | 审批 |
| | | </el-button> |
| | | <el-button |
| | | v-if="scope.row.status === 'pending'" |
| | | link |
| | | type="danger" |
| | | @click="rejectSeal(scope.row)" |
| | | > |
| | | 拒绝 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <pagination v-show="page.total > 0" :total="page.total" layout="total, sizes, prev, pager, next, jumper" |
| | | :page="page.current" :limit="page.size" @pagination="paginationChange" /> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="sealTableColumn" |
| | | :tableData="sealApplications" |
| | | :tableLoading="tableLoading" |
| | | :page="page" |
| | | :isShowPagination="true" |
| | | @pagination="paginationChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | |
| | | </el-form> |
| | | </FormDialog> |
| | | |
| | | <!-- 规章制度发布对话框 --> |
| | | <!-- <el-dialog v-model="showRegulationDialog" :title="operationType === 'add' ? '发布制度' : '编辑制度'" width="800px"> |
| | | <el-form :model="regulationForm" :rules="regulationRules" ref="regulationFormRef" label-width="100px"> |
| | | <el-form-item label="制度编号" prop="regulationNum"> |
| | | <el-input v-model="regulationForm.regulationNum" placeholder="请输入制度编号" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度标题" prop="title"> |
| | | <el-input v-model="regulationForm.title" placeholder="请输入制度标题" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度分类" prop="category"> |
| | | <el-select v-model="regulationForm.category" placeholder="请选择制度分类" style="width: 100%"> |
| | | <el-option label="人事制度" value="hr" /> |
| | | <el-option label="财务制度" value="finance" /> |
| | | <el-option label="安全制度" value="safety" /> |
| | | <el-option label="技术制度" value="tech" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="制度内容" prop="content"> |
| | | <el-input v-model="regulationForm.content" type="textarea" :rows="10" placeholder="请输入制度详细内容" /> |
| | | </el-form-item> |
| | | <el-form-item label="制度版本" prop="version"> |
| | | <el-input v-model="regulationForm.version" placeholder="请输入制度版本" /> |
| | | </el-form-item> |
| | | <el-form-item label="生效时间" prop="effectiveTime"> |
| | | <el-date-picker v-model="regulationForm.effectiveTime" type="datetime" format="YYYY-MM-DD HH:mm:ss" |
| | | value-format="YYYY-MM-DD HH:mm:ss" placeholder="选择生效时间" style="width: 100%" /> |
| | | </el-form-item> |
| | | <el-form-item label="适用范围" prop="scope"> |
| | | <el-checkbox-group v-model="regulationForm.scope"> |
| | | <el-checkbox label="all">全体员工</el-checkbox> |
| | | <el-checkbox label="manager">管理层</el-checkbox> |
| | | <el-checkbox label="hr">人事部门</el-checkbox> |
| | | <el-checkbox label="finance">财务部门</el-checkbox> |
| | | <el-checkbox label="tech">技术部门</el-checkbox> |
| | | </el-checkbox-group> |
| | | </el-form-item> |
| | | <el-form-item label="是否需要确认" prop="requireConfirm"> |
| | | <el-switch v-model="regulationForm.requireConfirm" /> |
| | | <span class="ml-10">开启后员工需要阅读确认</span> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="showRegulationDialog = false">取消</el-button> |
| | | <el-button type="primary" @click="submitRegulation">发布制度</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> --> |
| | | |
| | | <!-- 用印详情对话框 --> |
| | | <FormDialog |
| | | v-model="showSealDetailDialog" |
| | |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- 规章制度详情对话框 --> |
| | | <FormDialog |
| | | v-model="showRegulationDetailDialog" |
| | | title="规章制度详情" |
| | | :width="'800px'" |
| | | @close="closeRegulationDetailDialog" |
| | | @confirm="handleRegulationDetailConfirm" |
| | | @cancel="closeRegulationDetailDialog" |
| | | > |
| | | <div v-if="currentRegulationDetail"> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="制度编号">{{ currentRegulationDetail.id }}</el-descriptions-item> |
| | | <el-descriptions-item label="制度标题">{{ currentRegulationDetail.title }}</el-descriptions-item> |
| | | <el-descriptions-item label="分类">{{ getCategoryText(currentRegulationDetail.category) }}</el-descriptions-item> |
| | | <el-descriptions-item label="版本">{{ currentRegulationDetail.version }}</el-descriptions-item> |
| | | <el-descriptions-item label="发布人">{{ currentRegulationDetail.createUserName }}</el-descriptions-item> |
| | | <el-descriptions-item label="发布时间">{{ currentRegulationDetail.createTime }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <div class="mt-20"> |
| | | <h4>制度内容</h4> |
| | | <div class="regulation-content">{{ currentRegulationDetail.content }}</div> |
| | | </div> |
| | | <!-- 如果tableData>0 显示 --> |
| | | <div style="margin: 10px 0;" v-if="tableData && tableData.length > 0" > |
| | | <el-button type="success" @click="resetForm(currentRegulationDetail)">确认查看</el-button> |
| | | </div> |
| | | </div> |
| | | </FormDialog> |
| | | |
| | | <!-- 版本历史对话框 --> |
| | | <FormDialog |
| | | v-model="showVersionHistoryDialog" |
| | | title="版本历史" |
| | | :width="'800px'" |
| | | @close="closeVersionHistoryDialog" |
| | | @confirm="closeVersionHistoryDialog" |
| | | @cancel="closeVersionHistoryDialog" |
| | | > |
| | | <el-table :data="versionHistory" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="version" label="版本号" width="100" /> |
| | | <el-table-column prop="updateTime" label="更新时间" width="180" /> |
| | | <el-table-column prop="createUserName" label="更新人" width="120" /> |
| | | <el-table-column prop="changeLog" label="变更说明"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'active' ? 'success' : 'info'"> |
| | | {{ scope.row.status === 'active' ? '生效中' : '已废止' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </FormDialog> |
| | | |
| | | <!-- 阅读状态对话框 --> |
| | | <FormDialog |
| | | v-model="showReadStatusDialog" |
| | | title="阅读状态" |
| | | :width="'800px'" |
| | | @close="closeReadStatusDialog" |
| | | @confirm="closeReadStatusDialog" |
| | | @cancel="closeReadStatusDialog" |
| | | > |
| | | <el-table :data="readStatusList" style="width: 100%;margin-bottom: 10px"> |
| | | <el-table-column prop="employee" label="员工姓名" width="120" /> |
| | | <el-table-column prop="department" label="所属部门" width="150" /> |
| | | <el-table-column prop="createTime" label="阅读时间" width="180" /> |
| | | <el-table-column prop="confirmTime" label="确认时间" width="180" /> |
| | | <el-table-column prop="status" label="状态" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.status === 'confirmed' ? 'success' : 'warning'"> |
| | | {{ scope.row.status === 'confirmed' ? '已确认' : '未确认' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue' |
| | | import { useRoute } from 'vue-router' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Plus } from '@element-plus/icons-vue' |
| | | import { listSealApplication, addSealApplication, updateSealApplication,listRuleManagement,addRuleManagement,updateRuleManagement,delRuleManagement,getReadingStatusByRuleId,getReadingStatusList,addReadingStatus,updateReadingStatus } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import { el } from 'element-plus/es/locales.mjs' |
| | | import { getUserProfile, userListNoPageByTenantId } from '@/api/system/user.js' |
| | | import { listSealApplication, addSealApplication, updateSealApplication } from '@/api/collaborativeApproval/sealManagement.js' |
| | | import { userListNoPageByTenantId } from '@/api/system/user.js' |
| | | import useUserStore from '@/store/modules/user' |
| | | import { userLoginFacotryList } from "@/api/system/user.js" |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js" |
| | | import FormDialog from '@/components/Dialog/FormDialog.vue' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | |
| | | // 响应式数据 |
| | | const currentUser = ref(null) |
| | | const activeTab = ref('seal') |
| | | const operationType = ref('add') |
| | | const tableData = ref([]) |
| | | // 用印申请相关 |
| | | const userStore = useUserStore() |
| | | const route = useRoute() |
| | |
| | | // 分页参数 |
| | | const page = reactive({ |
| | | current: 1, |
| | | size: 100, |
| | | size: 10, |
| | | total: 0 |
| | | }) |
| | | // 规章制度相关 |
| | | const showRegulationDialog = ref(false) |
| | | const showRegulationDetailDialog = ref(false) |
| | | const showVersionHistoryDialog = ref(false) |
| | | const showReadStatusDialog = ref(false) |
| | | const currentRegulationDetail = ref(null) |
| | | const regulationFormRef = ref() |
| | | const regulationForm = reactive({ |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | |
| | | const readStatus = ref({ |
| | | id: '', |
| | | ruleId: '', |
| | | employee: '', |
| | | department: '', |
| | | createTime: '', |
| | | confirmTime: '', |
| | | status: 'unconfirmed' |
| | | }) |
| | | |
| | | const regulationRules = { |
| | | title: [{ required: true, message: '请输入制度标题', trigger: 'blur' }], |
| | | category: [{ required: true, message: '请选择制度分类', trigger: 'change' }], |
| | | content: [{ required: true, message: '请输入制度内容', trigger: 'blur' }], |
| | | effectiveTime: [{ required: true, message: '请选择生效时间', trigger: 'change' }], |
| | | scope: [{ required: true, message: '请选择适用范围', trigger: 'change' }] |
| | | } |
| | | |
| | | const regulationSearchForm = reactive({ |
| | | title: '', |
| | | category: '' |
| | | }) |
| | | |
| | | // 假数据 |
| | | const sealApplications = ref([]) |
| | | |
| | | const regulations = ref([]) |
| | | |
| | | const versionHistory = ref([]) |
| | | |
| | | const readStatusList = ref([]) |
| | | // { employee: '陈志强', department: '销售部', readTime: '2025-01-11 10:30:00', confirmTime: '2025-01-11 10:35:00', status: 'confirmed' }, |
| | | // { employee: '刘雅婷', department: '技术部', readTime: '2025-01-11 14:20:00', confirmTime: '', status: 'unconfirmed' }, |
| | | // { employee: '王建国', department: '财务部', readTime: '2025-01-12 09:15:00', confirmTime: '2025-01-12 09:20:00', status: 'confirmed' } |
| | | |
| | | // 用印申请状态 |
| | | const getStatusType = (status) => { |
| | |
| | | } |
| | | return statusMap[status] || 'info' |
| | | } |
| | | // 制度状态 |
| | | // 用印申请状态文本 |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | pending: '待审批', |
| | |
| | | official: '公章', |
| | | contract: '合同专用章', |
| | | finance: '财务专用章', |
| | | legal: '法人章', |
| | | tegal: '技术专用章' |
| | | } |
| | | return sealTypeMap[sealType] || '未知' |
| | | } |
| | | // 制度分类 |
| | | const getCategoryText = (category) => { |
| | | const categoryMap = { |
| | | hr: '人事制度', |
| | | finance: '财务制度', |
| | | safety: '安全制度', |
| | | tech: '技术制度' |
| | | |
| | | // 用印申请表格列配置(需在 getStatusText/getSealTypeText 等之后定义) |
| | | const sealTableColumn = ref([ |
| | | { label: '申请编号', prop: 'applicationNum',}, |
| | | { label: '申请标题', prop: 'title', showOverflowTooltip: true }, |
| | | { label: '申请人', prop: 'createUserName', }, |
| | | { label: '所属部门', prop: 'department', width: 150 }, |
| | | { |
| | | label: '用印类型', |
| | | prop: 'sealType', |
| | | dataType: 'tag', |
| | | formatData: (v) => getSealTypeText(v), |
| | | formatType: () => 'info' |
| | | }, |
| | | { label: '申请时间', prop: 'createTime', width: 180 }, |
| | | { |
| | | label: '状态', |
| | | prop: 'status', |
| | | width: 100, |
| | | dataType: 'tag', |
| | | formatData: (v) => getStatusText(v), |
| | | formatType: (v) => getStatusType(v) |
| | | }, |
| | | { |
| | | dataType: 'action', |
| | | label: '操作', |
| | | width: 200, |
| | | fixed: 'right', |
| | | align: 'center', |
| | | operation: [ |
| | | { name: '查看', clickFun: (row) => viewSealDetail(row) }, |
| | | { |
| | | name: '审批', |
| | | clickFun: (row) => approveSeal(row), |
| | | showHide: (row) => row.status === 'pending' |
| | | }, |
| | | { |
| | | name: '拒绝', |
| | | clickFun: (row) => rejectSeal(row), |
| | | showHide: (row) => row.status === 'pending' |
| | | } |
| | | ] |
| | | } |
| | | return categoryMap[category] || '未知' |
| | | } |
| | | ]) |
| | | |
| | | // 搜索印章申请 |
| | | const searchSealApplications = () => { |
| | | page.current=1 |
| | |
| | | sealSearchForm.status = '' |
| | | sealSearchForm.applicationNum = '' |
| | | searchSealApplications() |
| | | } |
| | | // 搜索制度 |
| | | const searchRegulations = () => { |
| | | page.current=1 |
| | | getRegulationList() |
| | | } |
| | | // 重置制度搜索 |
| | | const resetRegulationSearch = () => { |
| | | regulationSearchForm.title = '' |
| | | regulationSearchForm.category = '' |
| | | searchRegulations() |
| | | } |
| | | // 提交用印申请 |
| | | const submitSealApplication = async () => { |
| | |
| | | const closeSealDetailDialog = () => { |
| | | showSealDetailDialog.value = false |
| | | } |
| | | // 关闭规章制度详情对话框 |
| | | const closeRegulationDetailDialog = () => { |
| | | showRegulationDetailDialog.value = false |
| | | } |
| | | // 处理规章制度详情确认 |
| | | const handleRegulationDetailConfirm = () => { |
| | | // 如果tableData>0,执行确认查看操作 |
| | | if (currentRegulationDetail.value && tableData.value && tableData.value.length > 0) { |
| | | resetForm(currentRegulationDetail.value) |
| | | } |
| | | closeRegulationDetailDialog() |
| | | } |
| | | // 关闭版本历史对话框 |
| | | const closeVersionHistoryDialog = () => { |
| | | showVersionHistoryDialog.value = false |
| | | } |
| | | // 关闭阅读状态对话框 |
| | | const closeReadStatusDialog = () => { |
| | | showReadStatusDialog.value = false |
| | | } |
| | | // 新增 |
| | | const handleAdd = () => { |
| | | operationType.value = 'add' |
| | | resetRegulationForm() |
| | | showRegulationDialog.value = true |
| | | } |
| | | |
| | | // 编辑 |
| | | const handleEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | showRegulationDialog.value = true |
| | | } |
| | | // 废弃 |
| | | const repealEdit = (row) => { |
| | | operationType.value = 'edit' |
| | | Object.assign(regulationForm, row) |
| | | regulationForm.status = 'repealed' |
| | | ElMessageBox.confirm('确认废弃该制度?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度废弃成功') |
| | | // showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | } |
| | | }) |
| | | }).catch(() => { |
| | | ElMessage({ |
| | | type: 'info', |
| | | message: '已取消废弃' |
| | | }) |
| | | }) |
| | | } |
| | | // 发布制度 |
| | | const submitRegulation = async () => { |
| | | try { |
| | | await regulationFormRef.value.validate() |
| | | if(operationType.value == 'add'){ |
| | | addRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度发布成功') |
| | | showRegulationDialog.value = false |
| | | getRegulationList() |
| | | resetRegulationForm() |
| | | } |
| | | }) |
| | | }else{ |
| | | updateRuleManagement(regulationForm).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度编辑成功') |
| | | showRegulationDialog.value = false |
| | | resetRegulationForm() |
| | | getRegulationList() |
| | | }})} |
| | | }catch(err){ |
| | | ElMessage.error(err.msg) |
| | | } |
| | | } |
| | | //重置制度表单 |
| | | const resetRegulationForm = () => { |
| | | Object.assign(regulationForm, { |
| | | id: '', |
| | | regulationNum: '', |
| | | title: '', |
| | | category: '', |
| | | content: '', |
| | | version: '', |
| | | status: 'active', |
| | | readCount: 0, |
| | | effectiveTime: '', |
| | | scope: [], |
| | | requireConfirm: false |
| | | }) |
| | | } |
| | | |
| | | |
| | | // 查看用印申请详情 |
| | | const viewSealDetail = (row) => { |
| | |
| | | } |
| | | // 审批用印申请 |
| | | const approveSeal = (row) => { |
| | | console.log(row) |
| | | ElMessageBox.confirm('确认通过该用印申请?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批通过') |
| | | getSealApplicationList() |
| | | } |
| | | }) |
| | | }) |
| | |
| | | row.status = 'rejected' |
| | | updateSealApplication(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('审批拒绝') |
| | | ElMessage.success('已拒绝申请') |
| | | getSealApplicationList() |
| | | } |
| | | }) |
| | | ElMessage.success('已拒绝申请') |
| | | }) |
| | | } |
| | | // 获取在职员工列表 |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | //获取当前登录用户信息 |
| | | getUserProfile().then(res => { |
| | | if(res.code == 200){ |
| | | console.log(res.data.userName) |
| | | currentUser.value = res.data.userName |
| | | } |
| | | }) |
| | | staffOnJobListPage({staffState: 1, ...page}).then(res => { |
| | | tableLoading.value = false; |
| | | // tableData.value = res.data.records |
| | | // //筛选出和currentUser同名的人员 |
| | | tableData.value = res.data.records.filter(item => item.staffName === currentUser.value) |
| | | page.total = res.data.total; |
| | | |
| | | if(tableData.value.length == 0){ |
| | | ElMessage.error('当前用户未加入任何部门') |
| | | } |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | |
| | | |
| | | }; |
| | | |
| | | // 查看制度版本历史 |
| | | const viewVersionHistory = (row) => { |
| | | showVersionHistoryDialog.value = true |
| | | const params = { |
| | | |
| | | category: row.category |
| | | } |
| | | listRuleManagement(page,params).then(res => { |
| | | if(res.code == 200){ |
| | | versionHistory.value = res.data.records |
| | | } |
| | | }) |
| | | } |
| | | // 查看制度详情 |
| | | const viewRegulation = (row) => { |
| | | getList() |
| | | currentRegulationDetail.value = row |
| | | showRegulationDetailDialog.value = true |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | if(readStatusList.value.length==0 && tableData.value.length>0){ |
| | | const params = { |
| | | ruleId: row.id, |
| | | employee: tableData.value[0].staffName, |
| | | department: tableData.value[0].postJob, |
| | | status: 'unconfirmed' |
| | | } |
| | | addReadingStatus(params).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读成功') |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | // 查看制度阅读状态 |
| | | const viewReadStatus = (row) => { |
| | | showReadStatusDialog.value = true |
| | | //查看阅读状态列表 |
| | | getReadingStatusByRuleId(row.id).then(res => { |
| | | if(res.code == 200){ |
| | | readStatusList.value = res.data |
| | | } |
| | | }) |
| | | } |
| | | |
| | | //确认查看 |
| | | const resetForm = (row) => { |
| | | console.log("row",row) |
| | | row.readCount = row.readCount + 1 |
| | | |
| | | updateRuleManagement(row).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('查看数量修改成功') |
| | | //修改阅读状态 |
| | | //根据制度id和当前登录的员工得到阅读状态 |
| | | // let item = readStatusList.value.filter(item => item.employee == tableData.value[0].staffName ) |
| | | // if(item.length>0){ |
| | | // item[0].status = 'confirmed', |
| | | // item[0].confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | // } |
| | | // 筛选当前员工对应该制度的阅读状态记录 |
| | | let statusItem = readStatusList.value.find(item => item.employee === tableData.value[0].staffName && item.ruleId === row.id); |
| | | |
| | | if (statusItem) { |
| | | // 如果找到记录,更新状态和确认时间 |
| | | statusItem.status = 'confirmed'; |
| | | // 格式化时间为"YYYY-MM-DD HH:mm:ss"格式 |
| | | const now = new Date(); |
| | | statusItem.confirmTime = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`; |
| | | // statusItem.confirmTime = new Date().toISOString().replace('T', ' ').split('.')[0]; |
| | | |
| | | updateReadingStatus(statusItem).then(res => { |
| | | if(res.code == 200){ |
| | | ElMessage.success('制度阅读状态修改成功') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | } |
| | | }) |
| | | } |
| | | |
| | |
| | | // 获取印章申请列表数据 |
| | | const getSealApplicationList = async () => { |
| | | tableLoading.value = true |
| | | listSealApplication(page,sealSearchForm) |
| | | listSealApplication(page, sealSearchForm) |
| | | .then(res => { |
| | | //获取当前登录的部门信息 |
| | | // 获取当前登录的部门信息并过滤数据 |
| | | const currentFactoryName = userStore.currentFactoryName |
| | | if (currentFactoryName) { |
| | | // 根据currentFactoryName过滤出department相同的数据 |
| | | sealApplications.value = res.data.records.filter(item => item.department === currentFactoryName) |
| | | // 更新过滤后的总数 |
| | | page.total = sealApplications.value.length |
| | | } else { |
| | | // 如果没有currentFactoryName,则显示所有数据 |
| | | sealApplications.value = res.data.records |
| | | page.total = res.data.total |
| | | } |
| | | // sealApplications.value = res.data.records |
| | | // page.value.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | sealApplications.value = res.data.records |
| | | page.total = res.data.total |
| | | tableLoading.value = false |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | // 获取规章制度列表数据 |
| | | const getRegulationList = async () => { |
| | | tableLoading.value = true |
| | | listRuleManagement(page,regulationSearchForm) |
| | | .then(res => { |
| | | |
| | | regulations.value = res.data.records |
| | | // 过滤掉已废弃的制度 |
| | | // regulations.value = res.data.records.filter(item => item.status !== 'repealed') |
| | | page.total = res.data.total; |
| | | tableLoading.value = false; |
| | | |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | } |
| | | |
| | | // 分页变化处理 |
| | | const paginationChange = (obj) => { |
| | | page.current = obj.page; |
| | |
| | | } else { |
| | | getSealApplicationList() |
| | | } |
| | | getRegulationList() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .mt-20 { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .ml-10 { |
| | | margin-left: 10px; |
| | | } |
| | | |
| | | .regulation-content { |
| | | background-color: #f5f5f5; |
| | | padding: 15px; |
| | | border-radius: 4px; |
| | | line-height: 1.6; |
| | | white-space: pre-wrap; |
| | | height: 200px; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | </style> |
| | |
| | | <div class="main-panel"> |
| | | <div style="display: flex;justify-content: space-between;"> |
| | | <div class="section-title">应收应付统计</div> |
| | | <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable"> |
| | | <el-radio-button label="按周" :value="1" /> |
| | | <el-radio-button label="按月" :value="2" /> |
| | | <el-radio-button label="按季度" :value="3" /> |
| | | </el-radio-group> |
| | | <!-- <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">--> |
| | | <!-- <el-radio-button label="按周" :value="1" />--> |
| | | <!-- <el-radio-button label="按月" :value="2" />--> |
| | | <!-- <el-radio-button label="按季度" :value="3" />--> |
| | | <!-- </el-radio-group>--> |
| | | </div> |
| | | <Echarts ref="chart" |
| | | :color="barColors2" |
| | |
| | | <div class="app-container"> |
| | | <div class="search_form"> |
| | | <div> |
| | | <span class="search_title ml10">产品大类:</span> |
| | | <span class="search_title ml10">产品名称:</span> |
| | | <el-input v-model="searchForm.productName" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable/> |
| | | <span class="search_title ml10">产品大类:</span> |
| | | <el-select v-model="searchForm.parentId" |
| | | style="width: 240px" |
| | | placeholder="请输入" |
| | | clearable> |
| | | <el-option v-for="item of selectList" :key="item.id" :label="item.name" :value="item.id"></el-option> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | |
| | | :row-class-name="tableRowClassName" height="calc(100vh - 18.5em)"> |
| | | <el-table-column align="center" type="selection" width="55" /> |
| | | <el-table-column align="center" label="序号" type="index" width="60" /> |
| | | <el-table-column label="产品大类" prop="productName" show-overflow-tooltip /> |
| | | <el-table-column label="产品名称" prop="productName" show-overflow-tooltip /> |
| | | <el-table-column label="规格型号" prop="model" show-overflow-tooltip /> |
| | | <el-table-column label="单位" prop="unit" show-overflow-tooltip /> |
| | | <el-table-column label="产品图片" prop="url" show-overflow-tooltip> |
| | | <template #default="scope"> |
| | | <img :src="scope.row.url" alt="产品图片" style="width: 50px; height: 50px;"> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="高度" prop="height" show-overflow-tooltip /> |
| | | <el-table-column label="每件数量(支)" prop="boxNum" show-overflow-tooltip /> |
| | | <el-table-column label="单价(美元/件)" prop="dollarPrice" show-overflow-tooltip /> |
| | | <el-table-column label="单价(元/件)" prop="taxInclusiveUnitPrice" show-overflow-tooltip /> |
| | | <el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip /> |
| | | <el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip /> |
| | | <el-table-column label="库存预警数量" prop="warnNum" show-overflow-tooltip /> |
| | | <el-table-column label="备注" prop="remark" show-overflow-tooltip /> |
| | | <el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip /> |
| | | <el-table-column fixed="right" label="操作" min-width="60" align="center"> |
| | | <el-table-column fixed="right" label="操作" width="160" align="center"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button> |
| | | <el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button> |
| | | <el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">解冻</el-button> |
| | | <el-button link type="success" size="small" @click="showQRCode(scope.row,2)">生成条形码</el-button> |
| | | <el-button link type="success" size="small" @click="showERCode(scope.row,2)">生成二维码</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | |
| | | :operation-type="operationType" |
| | | type="qualified" |
| | | @completed="handleQuery" /> |
| | | |
| | | <el-dialog |
| | | v-model="qrCodeDialogVisible" |
| | | title="商品条形码" |
| | | width="400px" |
| | | center |
| | | > |
| | | <div style="text-align: center;"> |
| | | <img id="barcode" style="width:200px;height: 50px;"/> |
| | | <!-- <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" /> --> |
| | | <div style="margin: 20px;"> |
| | | <el-button type="primary" @click="downloadQRCode">下载条形码</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 二维码显示对话框 --> |
| | | <el-dialog |
| | | v-model="erCodeDialogVisible" |
| | | title="商品二维码" |
| | | width="400px" |
| | | center |
| | | > |
| | | <div style="text-align: center;"> |
| | | <img :src="erCodeUrl" alt="二维码" style="width:200px;height:200px;" /> |
| | | <div style="margin: 20px;"> |
| | | <el-button type="primary" @click="downloadERCode">下载二维码图片</el-button> |
| | | </div> |
| | | </div> |
| | | </el-dialog> |
| | | <el-dialog v-model="barcodeDia" title="产品信息" width="40%" @close="closeBarcodeDia"> |
| | | <div> |
| | | <el-row v-if="barcodeDetail.url" :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">图片</div> |
| | | <img class="barcode-img" :src="javaApiUrl+barcodeDetail.url"></img> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">名称</div> |
| | | <div class="barcode-value">{{barcodeDetail.productCategory}}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">产品高度</div> |
| | | <div class="barcode-value">{{barcodeDetail.height}}</div> |
| | | </div> |
| | | </el-col> |
| | | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">单价(美元)/件</div> |
| | | <div class="barcode-value">{{barcodeDetail.dollarPrice}}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">单价(元)/件</div> |
| | | <div class="barcode-value">{{barcodeDetail.taxInclusiveUnitPrice}}</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">数量/件</div> |
| | | <div class="barcode-value">{{barcodeDetail.qualitity}}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">每件数量/支</div> |
| | | <div class="barcode-value">{{barcodeDetail.boxNum}}</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">规格</div> |
| | | <div class="barcode-value">{{barcodeDetail.model}}</div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="barcode-item"> |
| | | <div class="barcode-label">单位</div> |
| | | <div class="barcode-value">{{barcodeDetail.unit}}</div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="closeBarcodeDia">关闭</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import pagination from '@/components/PIMTable/Pagination.vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import {ElMessage, ElMessageBox} from "element-plus"; |
| | | import { getStockInventoryListPage } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { |
| | | getStockInventoryListPage, |
| | | getProductList, |
| | | getStockInventoryById |
| | | } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import QRCode from "qrcode"; |
| | | import JsBarcode from "jsbarcode"; |
| | | |
| | | const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue")); |
| | | const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue")); |
| | | const ImportStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Import.vue")); |
| | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | productName: '', |
| | | } |
| | | }, |
| | | selectList:[ |
| | | { |
| | | id:1, |
| | | label:1 |
| | | },{ |
| | | id:2, |
| | | label:2 |
| | | },{ |
| | | id:3, |
| | | label:3 |
| | | } |
| | | ] |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | const { searchForm, selectList } = toRefs(data) |
| | | |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | |
| | | proxy.$modal.msg("已取消") |
| | | }) |
| | | } |
| | | // 获取产品大类下拉 |
| | | const productCategorySelectList =async ()=>{ |
| | | let res =await getProductList() |
| | | console.log(res) |
| | | if(res?.code === 200 && res?.data) { |
| | | selectList.value = res.data |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getList() |
| | | |
| | | // 二维码相关变量 |
| | | const qrCodeDialogVisible = ref(false); |
| | | const qrCodeUrl = ref(""); |
| | | const showQRCode = async (row,type) => { |
| | | if(barcodeDia.value)return |
| | | try { |
| | | // 构建二维码内容,只包含采购合同号(纯文本) |
| | | let qrContent = row.id || ''; |
| | | // 检查内容是否为空 |
| | | if (!qrContent) { |
| | | proxy.$modal.msgWarning("该行商品id,无法生成条形码"); |
| | | return; |
| | | } |
| | | qrContent+=`,${type}` |
| | | qrCodeDialogVisible.value = true; |
| | | await nextTick(); |
| | | JsBarcode("#barcode", qrContent+'', { |
| | | width:10, |
| | | height:100, |
| | | displayValue: false |
| | | }); |
| | | } catch (error) { |
| | | console.error('生成条形码失败:', error); |
| | | proxy.$modal.msgError("生成条形码失败:" + error.message); |
| | | } |
| | | }; |
| | | |
| | | const erCodeDialogVisible = ref(false); |
| | | const erCodeUrl = ref(""); |
| | | const showERCode = async (row,type) => { |
| | | if(barcodeDia.value)return |
| | | let qrContent = row.id || ''; |
| | | // 检查内容是否为空 |
| | | if (!qrContent) { |
| | | proxy.$modal.msgWarning("该行商品id,无法生成二维码"); |
| | | return; |
| | | } |
| | | qrContent+=`,${type}` |
| | | try { |
| | | erCodeUrl.value = await QRCode.toDataURL(qrContent+'', { |
| | | width: 200, |
| | | margin: 2, |
| | | color: { |
| | | dark: '#000000', |
| | | light: '#FFFFFF' |
| | | } |
| | | }); |
| | | erCodeDialogVisible.value = true; |
| | | } catch (error) { |
| | | console.error('生成二维码失败:', error); |
| | | proxy.$modal.msgError("生成二维码失败:" + error.message); |
| | | } |
| | | }; |
| | | const downloadQRCode = () => { |
| | | const imgSrc = document.getElementById('barcode').src |
| | | const a = document.createElement('a'); |
| | | if(!imgSrc){ |
| | | proxy.$modal.msgWarning('暂无条形码') |
| | | return |
| | | } |
| | | a.href = imgSrc; |
| | | a.download = `商品条形码_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("下载成功"); |
| | | }; |
| | | |
| | | // 下载二维码 |
| | | const downloadERCode = () => { |
| | | if (!erCodeUrl.value) { |
| | | proxy.$modal.msgWarning("二维码未生成"); |
| | | return; |
| | | } |
| | | |
| | | const a = document.createElement('a'); |
| | | a.href = erCodeUrl.value; |
| | | a.download = `商品二维码_${new Date().getTime()}.png`; |
| | | document.body.appendChild(a); |
| | | a.click(); |
| | | document.body.removeChild(a); |
| | | proxy.$modal.msgSuccess("下载成功"); |
| | | }; |
| | | |
| | | //扫码相关参数 |
| | | const barcodeDia = ref(false); |
| | | const scanBarcodeInput = ref(''); |
| | | const barcodeDetail = ref({}) |
| | | // 扫码函数 |
| | | function scanBarcode (e){ |
| | | if(!e||!e.target||!e.target.tagName){ |
| | | return; |
| | | } |
| | | if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { |
| | | return |
| | | } |
| | | if (e.key === 'Enter') { |
| | | let _value = scanBarcodeInput.value |
| | | getDetail(_value) |
| | | scanBarcodeInput.value = "" |
| | | } else { |
| | | scanBarcodeInput.value += e.key |
| | | } |
| | | } |
| | | const getDetail = (barcode)=>{ |
| | | if(barcode.indexOf(",")==-1){ |
| | | proxy.$modal.msgWarning("请扫描正确的条形码") |
| | | return |
| | | } |
| | | let barcodeList = barcode.split(",") |
| | | let barcodeId = barcodeList[0] |
| | | let detailApi = getStockInventoryById |
| | | |
| | | if(!detailApi){ |
| | | proxy.$modal.msgWarning("请扫描正确的条形码") |
| | | return |
| | | } |
| | | detailApi({id:barcodeId}).then((resp) => { |
| | | if(!resp.data){ |
| | | proxy.$modal.msgError("商品不存在") |
| | | return |
| | | } |
| | | if(resp.code!=200){ |
| | | proxy.$modal.msgError(res.msg) |
| | | return |
| | | } |
| | | barcodeDetail.value = resp.data |
| | | barcodeDia.value = true |
| | | }).catch(() => { |
| | | proxy.$modal.msgError("查看详情失败") |
| | | }) |
| | | |
| | | } |
| | | |
| | | |
| | | const closeBarcodeDia = () => { |
| | | barcodeDia.value = false |
| | | } |
| | | |
| | | onMounted(async() => { |
| | | // 添加扫码枪监听事件 |
| | | document.removeEventListener('keypress',scanBarcode) |
| | | document.addEventListener('keypress', scanBarcode) |
| | | await productCategorySelectList() |
| | | await getList() |
| | | }) |
| | | </script> |
| | | |
| | |
| | | :deep(.row-low-stock:hover > td) { |
| | | background-color: #fcd4d4; |
| | | } |
| | | |
| | | .barcode-item{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | padding: 5px 0; |
| | | |
| | | } |
| | | |
| | | .barcode-img{ |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 5px; |
| | | background-color: #F5F5F5; |
| | | } |
| | | </style> |
| | |
| | | |
| | | // 添加表行类名方法 |
| | | const tableRowClassName = ({ row }) => { |
| | | const diff = row.deliveryDaysDiff; |
| | | if (row.isFh) return ''; |
| | | |
| | | const diff = row.deliveryDaysDiff; |
| | | if (diff === 15) { |
| | | return 'yellow'; |
| | | } else if (diff === 10) { |
| | |
| | | const openDialog = async (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }); |
| | | let userLists = await userListNoPage(); |
| | | userList.value = userLists.data; |
| | | form.value = {} |
| | | // 先清空表单验证状态,避免闪烁 |
| | | await nextTick(); |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | |
| | | // 并行加载基础数据 |
| | | const [userListsRes] = await Promise.all([ |
| | | userListNoPage(), |
| | | getProductOptions(), |
| | | getOptions().then((res) => { |
| | | supplierList.value = res.data; |
| | | }) |
| | | ]); |
| | | userList.value = userListsRes.data; |
| | | |
| | | form.value = {} |
| | | testStandardOptions.value = []; |
| | | tableData.value = []; |
| | | getProductOptions(); |
| | | |
| | | if (operationType.value === 'edit') { |
| | | // 先保存 testStandardId,避免被清空 |
| | | const savedTestStandardId = row.testStandardId; |
| | | // 先设置表单数据,但暂时清空 testStandardId,等选项加载完成后再设置 |
| | | form.value = {...row, testStandardId: ''} |
| | | currentProductId.value = row.productId || 0 |
| | | // 编辑模式下,先加载指标选项,然后加载参数列表 |
| | | if (currentProductId.value) { |
| | | // 先加载指标选项 |
| | | let params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | } |
| | | qualityInspectDetailByProductId(params).then(res => { |
| | | testStandardOptions.value = res.data || []; |
| | | // 使用 nextTick 和 setTimeout 确保选项已经渲染到 DOM |
| | | nextTick(() => { |
| | | setTimeout(() => { |
| | | // 如果编辑数据中有 testStandardId,则设置并加载对应的参数 |
| | | if (savedTestStandardId) { |
| | | // 确保类型匹配(item.id 可能是数字或字符串) |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // 确保使用匹配项的 id(保持类型一致) |
| | | form.value.testStandardId = matchedOption.id; |
| | | // 编辑场景保留已有检验值,直接拉取原参数数据 |
| | | getQualityInspectParamList(row.id); |
| | | } else { |
| | | // 如果找不到匹配项,尝试直接使用原值 |
| | | console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } else { |
| | | // 否则使用旧的逻辑 |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | }, 100); |
| | | }); |
| | | }); |
| | | } else { |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | currentProductId.value = row.productId || 0 |
| | | // 清空验证状态,避免数据加载过程中的校验闪烁 |
| | | nextTick(() => { |
| | | proxy.$refs.formRef?.clearValidate(); |
| | | }); |
| | | |
| | | // 编辑模式下,并行加载规格型号和指标选项 |
| | | if (currentProductId.value) { |
| | | // 设置产品名称 |
| | | form.value.productName = findNodeById(productOptions.value, currentProductId.value); |
| | | |
| | | // 并行加载规格型号和指标选项 |
| | | const params = { |
| | | productId: currentProductId.value, |
| | | inspectType: 2 |
| | | }; |
| | | |
| | | Promise.all([ |
| | | modelList({ id: currentProductId.value }), |
| | | qualityInspectDetailByProductId(params) |
| | | ]).then(([modelRes, testStandardRes]) => { |
| | | // 设置规格型号选项 |
| | | modelOptions.value = modelRes || []; |
| | | // 如果表单中已有 productModelId,设置对应的 model 和 unit |
| | | if (form.value.productModelId && modelOptions.value.length > 0) { |
| | | const selectedModel = modelOptions.value.find(item => item.id == form.value.productModelId); |
| | | if (selectedModel) { |
| | | form.value.model = selectedModel.model || ''; |
| | | form.value.unit = selectedModel.unit || ''; |
| | | } |
| | | } |
| | | |
| | | // 设置指标选项 |
| | | testStandardOptions.value = testStandardRes.data || []; |
| | | |
| | | // 设置 testStandardId 并加载参数列表 |
| | | nextTick(() => { |
| | | if (savedTestStandardId) { |
| | | // 确保类型匹配(item.id 可能是数字或字符串) |
| | | const matchedOption = testStandardOptions.value.find(item => |
| | | item.id == savedTestStandardId || String(item.id) === String(savedTestStandardId) |
| | | ); |
| | | if (matchedOption) { |
| | | // 确保使用匹配项的 id(保持类型一致) |
| | | form.value.testStandardId = matchedOption.id; |
| | | } else { |
| | | // 如果找不到匹配项,尝试直接使用原值 |
| | | console.warn('未找到匹配的指标选项,testStandardId:', savedTestStandardId, '可用选项:', testStandardOptions.value); |
| | | form.value.testStandardId = savedTestStandardId; |
| | | } |
| | | } |
| | | // 编辑场景保留已有检验值,直接拉取原参数数据 |
| | | getQualityInspectParamList(row.id); |
| | | }); |
| | | }); |
| | | } else { |
| | | getQualityInspectParamList(row.id); |
| | | } |
| | | } |
| | | } |
| | | const getProductOptions = () => { |
| | | productTreeList().then((res) => { |
| | | return productTreeList().then((res) => { |
| | | productOptions.value = convertIdToValue(res); |
| | | }); |
| | | }; |
| | |
| | | <div> |
| | | <PanelHeader title="工单执行效率分析" /> |
| | | <div class="main-panel panel-item-customers"> |
| | | <Echarts |
| | | ref="chart" |
| | | :chartStyle="chartStyle" |
| | | :grid="grid" |
| | | :legend="barLegend" |
| | | :series="chartSeries" |
| | | :tooltip="tooltip" |
| | | :xAxis="xAxis1" |
| | | :yAxis="yAxis1" |
| | | :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }" |
| | | style="height: 260px" |
| | | /> |
| | | <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="barLegend" :series="chartSeries" |
| | | :tooltip="tooltip" :xAxis="xAxis1" :yAxis="yAxis1" |
| | | :options="{ backgroundColor: 'transparent', textStyle: { color: '#B8C8E0' } }" style="height: 260px" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted } from 'vue' |
| | | import { qualityStatistics } from '@/api/viewIndex.js' |
| | | import { workOrderEfficiencyAnalysis } from '@/api/viewIndex.js' |
| | | import PanelHeader from './PanelHeader.vue' |
| | | import Echarts from '@/components/Echarts/echarts.vue' |
| | | |
| | |
| | | data: ['开工', '完成', '良品率'], |
| | | } |
| | | |
| | | // 柱状图:开工、完成;折线图:良品率(颜色 rgba(90, 216, 166, 1)) |
| | | const chartSeries = ref([ |
| | | { |
| | | name: '开工', |
| | |
| | | const xAxis1 = ref([ |
| | | { type: 'category', axisTick: { show: false }, axisLabel: { color: '#B8C8E0' }, data: [] }, |
| | | ]) |
| | | |
| | | const yAxis1 = [ |
| | | { type: 'value', name: '件', axisLabel: { color: '#B8C8E0' }, nameTextStyle: { color: '#B8C8E0' } }, |
| | | { |
| | |
| | | ] |
| | | |
| | | const fetchData = () => { |
| | | qualityStatistics() |
| | | workOrderEfficiencyAnalysis() |
| | | .then((res) => { |
| | | if (!res?.data?.item || !Array.isArray(res.data.item)) return |
| | | const items = res.data.item |
| | | xAxis1.value[0].data = items.map((d) => d.date) |
| | | // 开工:过程检验数 |
| | | chartSeries.value[0].data = items.map((d) => Number(d.processNum) || 0) |
| | | // 完成:出厂数 |
| | | chartSeries.value[1].data = items.map((d) => Number(d.factoryNum) || 0) |
| | | // 良品率:出厂数/过程数*100(无单独接口时用此占位) |
| | | chartSeries.value[2].data = items.map((d) => { |
| | | const processNum = Number(d.processNum) || 0 |
| | | const factoryNum = Number(d.factoryNum) || 0 |
| | | if (processNum <= 0) return 0 |
| | | return Math.min(100, Math.round((factoryNum / processNum) * 100)) |
| | | }) |
| | | // 根据你的结构,数据直接在 res.data 中 |
| | | if (!res?.data || !Array.isArray(res.data)) return |
| | | |
| | | const list = res.data |
| | | |
| | | xAxis1.value[0].data = list.map((item) => item.date) |
| | | |
| | | chartSeries.value[0].data = list.map((item) => Number(item.startQuantity) || 0) |
| | | |
| | | chartSeries.value[1].data = list.map((item) => Number(item.finishQuantity) || 0) |
| | | |
| | | chartSeries.value[2].data = list.map((item) => Number(item.yieldRate) || 0) |
| | | }) |
| | | .catch((err) => { |
| | | console.error('获取开工与良品率数据失败:', err) |
| | | console.error('获取工单效率数据失败:', err) |
| | | }) |
| | | } |
| | | |
| | |
| | | </el-table-column> |
| | | <el-table-column fixed="right" |
| | | label="操作" |
| | | min-width="250" |
| | | min-width="150" |
| | | align="center"> |
| | | <template #default="scope"> |
| | | <el-button link |
| | | <!-- <el-button link |
| | | type="primary" |
| | | size="small" |
| | | @click="openForm('edit', scope.row)">编辑</el-button> |
| | | @click="openForm('edit', scope.row)">编辑</el-button> --> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="scope.row.isRectify" |
| | | :disabled="scope.row.isRectify || scope.row.rectifyActualTime" |
| | | @click="openForm('edit2', scope.row)">整改</el-button> |
| | | <el-button link |
| | | type="primary" |
| | | size="small" |
| | | :disabled="!scope.row.rectifyActualTime" |
| | | :disabled="!scope.row.rectifyActualTime || scope.row.verifyTime" |
| | | @click="openForm('edit3', scope.row)">验收</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | |
| | | // 添加表行类名方法 |
| | | const tableRowClassName = ({ row }) => { |
| | | const diff = row.deliveryDaysDiff; |
| | | if (row.isFh) return ''; |
| | | |
| | | const diff = row.deliveryDaysDiff; |
| | | if (diff === 15) { |
| | | return 'yellow'; |
| | | } else if (diff === 10) { |
| | |
| | | const { VITE_APP_ENV } = env; |
| | | const baseUrl = |
| | | env.VITE_APP_ENV === "development" |
| | | ? "http://1.15.17.182:9003" |
| | | ? "http://114.132.189.42:9042" |
| | | : env.VITE_BASE_API; |
| | | const javaUrl = |
| | | env.VITE_APP_ENV === "development" |
| | | ? "http://1.15.17.182:9002" |
| | | ? "http://114.132.189.42:9044" |
| | | : env.VITE_JAVA_API; |
| | | return { |
| | | define:{ |