| | |
| | | <div class="app-container product-view"> |
| | | <div class="left"> |
| | | <div> |
| | | <el-input |
| | | v-model="search" |
| | | <el-input v-model="search" |
| | | style="width: 210px" |
| | | placeholder="输入关键字进行搜索" |
| | | @change="searchFilter" |
| | | @clear="searchFilter" |
| | | clearable |
| | | prefix-icon="Search" |
| | | /> |
| | | <el-button |
| | | prefix-icon="Search" /> |
| | | <el-button v-if="false" |
| | | type="primary" |
| | | @click="openProDia('addOne')" |
| | | style="margin-left: 10px" |
| | | >新增产品大类</el-button |
| | | > |
| | | style="margin-left: 10px">新增产品大类</el-button> |
| | | </div> |
| | | <div ref="containerRef"> |
| | | <el-tree |
| | | ref="tree" |
| | | <el-tree ref="tree" |
| | | v-loading="treeLoad" |
| | | :data="list" |
| | | @node-click="handleNodeClick" |
| | | :expand-on-click-node="false" |
| | | @node-expand="handleNodeExpand" |
| | | @node-collapse="handleNodeCollapse" |
| | | :key="treeKey" |
| | | :default-expanded-keys="expandedKeys" |
| | | :filter-node-method="filterNode" |
| | | :props="{ children: 'children', label: 'label' }" |
| | | highlight-current |
| | | node-key="id" |
| | | class="product-tree-scroll" |
| | | style="height: calc(100vh - 190px); overflow-y: auto" |
| | | > |
| | | style="height: calc(100vh - 190px); overflow-y: auto"> |
| | | <template #default="{ node, data }"> |
| | | <div class="custom-tree-node"> |
| | | <span class="tree-node-content"> |
| | |
| | | <span class="tree-node-label">{{ data.label }}</span> |
| | | </span> |
| | | <div> |
| | | <el-button |
| | | type="primary" |
| | | <el-button type="primary" |
| | | link |
| | | @click="openProDia('edit', data)" |
| | | > |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="openProDia('edit', data)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3"> |
| | | <el-button type="primary" |
| | | link |
| | | @click="openProDia('add', data)"> |
| | | 添加产品 |
| | | </el-button> |
| | | <el-button |
| | | v-if="!node.childNodes.length" |
| | | <el-button v-if="!node.childNodes.length" |
| | | style="margin-left: 4px" |
| | | type="danger" |
| | | link |
| | | @click="remove(node, data)" |
| | | > |
| | | :disabled="isTopLevelNode(data, node)" |
| | | @click="remove(node, data)"> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="right"> |
| | | <div style="margin-bottom: 10px" v-if="isShowButton"> |
| | | <el-button type="primary" @click="openModelDia('add')"> |
| | | <div style="margin-bottom: 10px" |
| | | v-if="isShowButton"> |
| | | <el-button type="primary" |
| | | @click="openModelDia('add')"> |
| | | 新增规格型号 |
| | | </el-button> |
| | | <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" /> |
| | | <el-button |
| | | type="danger" |
| | | <ImportExcel :product-id="currentId" |
| | | @uploadSuccess="getModelList" /> |
| | | <el-button type="danger" |
| | | @click="handleDelete" |
| | | style="margin-left: 10px" |
| | | plain |
| | | > |
| | | plain> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | ></PIMTable> |
| | | @pagination="pagination"></PIMTable> |
| | | </div> |
| | | <el-dialog v-model="productDia" title="产品" width="400px" @keydown.enter.prevent> |
| | | <el-form |
| | | :model="form" |
| | | <el-dialog v-model="productDia" |
| | | title="产品" |
| | | width="400px" |
| | | @keydown.enter.prevent> |
| | | <el-form :model="form" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | > |
| | | ref="formRef"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="24"> |
| | | <el-form-item label="产品名称:" prop="productName"> |
| | | <el-input |
| | | v-model="form.productName" |
| | | <el-form-item label="产品名称:" |
| | | prop="productName"> |
| | | <el-input v-model="form.productName" |
| | | placeholder="请输入产品名称" |
| | | maxlength="20" |
| | | show-word-limit |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeProDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | <el-dialog |
| | | v-model="modelDia" |
| | | <el-dialog v-model="modelDia" |
| | | title="规格型号" |
| | | width="400px" |
| | | @close="closeModelDia" |
| | | @keydown.enter.prevent |
| | | > |
| | | <el-form |
| | | :model="modelForm" |
| | | @keydown.enter.prevent> |
| | | <el-form :model="modelForm" |
| | | label-width="140px" |
| | | label-position="top" |
| | | :rules="modelRules" |
| | | ref="modelFormRef" |
| | | > |
| | | ref="modelFormRef"> |
| | | <el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="规格型号:" prop="model"> |
| | | <el-input |
| | | v-model="modelForm.model" |
| | | <el-form-item label="产品编号:" |
| | | prop="productCode"> |
| | | <el-input v-model="modelForm.productCode" |
| | | placeholder="请输入产品编号" |
| | | clearable |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="规格型号:" |
| | | prop="model"> |
| | | <el-input v-model="modelForm.model" |
| | | placeholder="请输入规格型号" |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="单位:" prop="unit"> |
| | | <el-input |
| | | v-model="modelForm.unit" |
| | | <el-form-item label="单位:" |
| | | prop="unit"> |
| | | <el-input v-model="modelForm.unit" |
| | | placeholder="请输入单位" |
| | | clearable |
| | | @keydown.enter.prevent |
| | | /> |
| | | @keydown.enter.prevent /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitModelForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitModelForm">确认</el-button> |
| | | <el-button @click="closeModelDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref } from "vue"; |
| | | import { nextTick, ref } from "vue"; |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { |
| | | addOrEditProduct, |
| | |
| | | const { proxy } = getCurrentInstance(); |
| | | const tree = ref(null); |
| | | const containerRef = ref(null); |
| | | const treeKey = ref(0); |
| | | const expandedKeySet = new Set(); |
| | | const EXPANDED_STORAGE_KEY = "basicData_product_tree_expanded_keys_v2"; |
| | | |
| | | const loadExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return []; |
| | | } |
| | | try { |
| | | const saved = localStorage.getItem(EXPANDED_STORAGE_KEY); |
| | | return saved ? JSON.parse(saved) : []; |
| | | } catch (error) { |
| | | console.error(error); |
| | | return []; |
| | | } |
| | | }; |
| | | |
| | | const saveExpandedKeys = () => { |
| | | if (typeof window === "undefined") { |
| | | return; |
| | | } |
| | | localStorage.setItem( |
| | | EXPANDED_STORAGE_KEY, |
| | | JSON.stringify(Array.from(expandedKeySet)) |
| | | ); |
| | | }; |
| | | |
| | | loadExpandedKeys().forEach(key => expandedKeySet.add(key)); |
| | | |
| | | const syncExpandedKeysFromTree = () => { |
| | | const keys = []; |
| | | const walk = nodes => { |
| | | (nodes || []).forEach(item => { |
| | | if (item.expanded && item.data?.id !== undefined) { |
| | | keys.push(item.data.id); |
| | | } |
| | | if (item.childNodes && item.childNodes.length) { |
| | | walk(item.childNodes); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(tree.value?.root?.childNodes); |
| | | expandedKeySet.clear(); |
| | | keys.forEach(key => expandedKeySet.add(key)); |
| | | expandedKeys.value = keys; |
| | | saveExpandedKeys(); |
| | | }; |
| | | |
| | | const normalizeExpandedKeys = treeData => { |
| | | const parentMap = new Map(); |
| | | const walk = (nodes, parentId = null) => { |
| | | (nodes || []).forEach(item => { |
| | | parentMap.set(item.id, parentId); |
| | | if (item.children && item.children.length) { |
| | | walk(item.children, item.id); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | walk(treeData); |
| | | |
| | | const normalizedKeys = Array.from(expandedKeySet).filter(key => { |
| | | if (!parentMap.has(key)) { |
| | | return false; |
| | | } |
| | | let currentId = key; |
| | | while (parentMap.has(currentId)) { |
| | | const parentId = parentMap.get(currentId); |
| | | if (!parentId) { |
| | | return true; |
| | | } |
| | | if (!expandedKeySet.has(parentId)) { |
| | | return false; |
| | | } |
| | | currentId = parentId; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | if (normalizedKeys.length !== expandedKeySet.size) { |
| | | expandedKeySet.clear(); |
| | | normalizedKeys.forEach(key => expandedKeySet.add(key)); |
| | | saveExpandedKeys(); |
| | | } |
| | | }; |
| | | |
| | | const productDia = ref(false); |
| | | const modelDia = ref(false); |
| | |
| | | const list = ref([]); |
| | | const expandedKeys = ref([]); |
| | | const tableColumn = ref([ |
| | | { |
| | | label: "产品编号", |
| | | prop: "productCode", |
| | | }, |
| | | { |
| | | label: "规格型号", |
| | | prop: "model", |
| | |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openModelDia("edit", row); |
| | | }, |
| | | }, |
| | |
| | | modelForm: { |
| | | model: "", |
| | | unit: "", |
| | | productCode: "", |
| | | }, |
| | | modelRules: { |
| | | model: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | unit: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | productCode: [{ required: true, message: "请输入", trigger: "blur" }], |
| | | }, |
| | | }); |
| | | const { form, rules, modelForm, modelRules } = toRefs(data); |
| | |
| | | const getProductTreeList = () => { |
| | | treeLoad.value = true; |
| | | productTreeList() |
| | | .then((res) => { |
| | | list.value = res; |
| | | list.value.forEach((a) => { |
| | | expandedKeys.value.push(a.label); |
| | | .then(res => { |
| | | list.value = res || []; |
| | | normalizeExpandedKeys(list.value); |
| | | expandedKeys.value = Array.from(expandedKeySet); |
| | | treeKey.value += 1; |
| | | nextTick(() => { |
| | | tree.value?.setDefaultExpandedKeys?.(expandedKeys.value); |
| | | }); |
| | | treeLoad.value = false; |
| | | }) |
| | | .catch((err) => { |
| | | .catch(err => { |
| | | console.error(err); |
| | | }) |
| | | .finally(() => { |
| | | treeLoad.value = false; |
| | | }); |
| | | }; |
| | | const handleNodeExpand = data => { |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | const handleNodeCollapse = (data, node) => { |
| | | node?.eachNode?.(item => { |
| | | item.collapse(); |
| | | }); |
| | | nextTick(syncExpandedKeysFromTree); |
| | | }; |
| | | // 过滤产品树 |
| | | const searchFilter = () => { |
| | | proxy.$refs.tree.filter(search.value); |
| | | }; |
| | | const isTopLevelNode = (data, node) => { |
| | | if (node?.level !== undefined) { |
| | | return node.level === 1; |
| | | } |
| | | return [null, undefined, "", 0, "0"].includes(data?.parentId); |
| | | }; |
| | | // 打开产品弹框 |
| | | const openProDia = (type, data) => { |
| | | if (data && type === "edit" && isTopLevelNode(data)) { |
| | | proxy.$modal.msgWarning("一级节点不能编辑或删除"); |
| | | return; |
| | | } |
| | | operationType.value = type; |
| | | productDia.value = true; |
| | | form.value.productName = ""; |
| | |
| | | modelOperationType.value = type; |
| | | modelDia.value = true; |
| | | modelForm.value.model = ""; |
| | | modelForm.value.model = ""; |
| | | modelForm.value.unit = ""; |
| | | modelForm.value.productCode = ""; |
| | | modelForm.value.id = ""; |
| | | if (type === "edit") { |
| | | modelForm.value = { ...data }; |
| | |
| | | }; |
| | | // 提交产品名称修改 |
| | | const submitForm = () => { |
| | | proxy.$refs.formRef.validate((valid) => { |
| | | proxy.$refs.formRef.validate(valid => { |
| | | if (valid) { |
| | | if (operationType.value === "add") { |
| | | form.value.parentId = currentId.value; |
| | |
| | | form.value.id = currentId.value; |
| | | form.value.parentId = ""; |
| | | } |
| | | addOrEditProduct(form.value).then((res) => { |
| | | addOrEditProduct(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeProDia(); |
| | | getProductTreeList(); |
| | |
| | | |
| | | // 删除产品 |
| | | const remove = (node, data) => { |
| | | if (isTopLevelNode(data, node)) { |
| | | proxy.$modal.msgWarning("一级节点不能编辑或删除"); |
| | | return; |
| | | } |
| | | let ids = []; |
| | | ids.push(data.id); |
| | | ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", { |
| | |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProduct(ids) |
| | | .then((res) => { |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("删除成功"); |
| | | getProductTreeList(); |
| | | }) |
| | |
| | | |
| | | // 提交规格型号修改 |
| | | const submitModelForm = () => { |
| | | proxy.$refs.modelFormRef.validate((valid) => { |
| | | proxy.$refs.modelFormRef.validate(valid => { |
| | | if (valid) { |
| | | modelForm.value.productId = currentId.value; |
| | | addOrEditProductModel(modelForm.value).then((res) => { |
| | | addOrEditProductModel(modelForm.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeModelDia(); |
| | | getModelList(); |
| | |
| | | modelDia.value = false; |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 查询规格型号 |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getModelList(); |
| | |
| | | id: currentId.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | }).then((res) => { |
| | | }).then(res => { |
| | | console.log("res", res); |
| | | tableData.value = res.records; |
| | | page.total = res.total; |
| | |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | |
| | | .then(() => { |
| | | tableLoading.value = true; |
| | | delProductModel(ids) |
| | | .then((res) => { |
| | | .then(res => { |
| | | proxy.$modal.msgSuccess("删除成功"); |
| | | getModelList(); |
| | | }) |