gaoluyang
昨天 fa652918dd80558deafeb55790228adf9240eae1
src/views/basicData/product/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container product-view">
    <div class="left">
      <div>
      <div class="left-header">
        <el-input
          v-model="search"
          style="width: 210px"
@@ -11,12 +11,17 @@
          clearable
          prefix-icon="Search"
        />
        <el-button
          type="primary"
          @click="openProDia('addOne')"
          style="margin-left: 10px"
          >新增产品大类</el-button
        >
        <div class="button-group">
          <el-button
            type="primary"
            @click="openProDia('addOne')"
          >新增产品大类</el-button>
          <el-button
            type="success"
            @click="handleImport"
            icon="Upload"
          >导入</el-button>
        </div>
      </div>
      <div ref="containerRef">
        <el-tree
@@ -25,18 +30,13 @@
          :data="list"
          @node-click="handleNodeClick"
          :expand-on-click-node="false"
          default-expand-all
          :default-expanded-keys="expandedKeys"
          :draggable="true"
          :filter-node-method="filterNode"
          :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">
@@ -45,7 +45,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
@@ -55,7 +55,7 @@
                >
                  编辑
                </el-button>
                <el-button type="primary" link @click="openProDia('add', data)">
                <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
                  添加产品
                </el-button>
                <el-button
@@ -99,7 +99,7 @@
        @pagination="pagination"
      ></PIMTable>
    </div>
    <el-dialog v-model="productDia" title="产品" width="400px">
    <el-dialog v-model="productDia" title="产品" width="400px" @keydown.enter.prevent>
      <el-form
        :model="form"
        label-width="140px"
@@ -113,7 +113,10 @@
              <el-input
                v-model="form.productName"
                placeholder="请输入产品名称"
                maxlength="20"
                show-word-limit
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
@@ -126,11 +129,11 @@
        </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"
@@ -146,6 +149,19 @@
                v-model="modelForm.model"
                placeholder="请输入规格型号"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="图纸编号:" prop="drawingNumber">
              <el-input
                  v-model="modelForm.drawingNumber"
                  placeholder="请输入图纸编号"
                  clearable
                  @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
@@ -157,7 +173,23 @@
                v-model="modelForm.unit"
                placeholder="请输入单位"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="产品类型:" prop="productType">
              <el-select
                v-model="modelForm.productType"
                placeholder="请选择产品类型"
                clearable
                style="width: 100%"
              >
                <el-option label="物料" :value="1" />
                <el-option label="产品" :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -169,12 +201,47 @@
        </div>
      </template>
    </el-dialog>
    <el-dialog v-model="importDia" title="产品导入" width="600px">
      <el-upload
        ref="importUploadRef"
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
        :headers="importUpload.headers"
        :before-upload="importUpload.beforeUpload"
        :on-success="importUpload.onSuccess"
        :on-error="importUpload.onError"
        :on-progress="importUpload.onProgress"
        :on-change="importUpload.onChange"
        :auto-upload="false"
        drag
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处,或<em>点击上传</em>
        </div>
        <template #tip>
          <div class="el-upload__tip">
            仅支持 xls/xlsx,大小不超过 10MB。
            <el-button link type="primary" @click="importTemplate">下载导入模板</el-button>
          </div>
        </template>
      </el-upload>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitImport">确认导入</el-button>
          <el-button @click="importDia = false">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref } from "vue";
import { ElMessageBox } from "element-plus";
import { getToken } from "@/utils/auth.js";
import { FileUpload } from "@/components/Upload";
import {
  addOrEditProduct,
  addOrEditProductModel,
@@ -182,15 +249,18 @@
  delProductModel,
  modelListPage,
  productTreeList,
  downloadTemplate,
} from "@/api/basicData/product.js";
import ImportExcel from "./ImportExcel/index.vue";
const { proxy } = getCurrentInstance();
const tree = ref(null);
const containerRef = ref(null);
const importUploadRef = ref(null);
const productDia = ref(false);
const modelDia = ref(false);
const importDia = ref(false);
const modelOperationType = ref("");
const search = ref("");
const currentId = ref("");
@@ -205,8 +275,18 @@
    prop: "model",
  },
  {
    label: "图纸编号",
    prop: "drawingNumber",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "产品类型",
    prop: "productType",
    width: 100,
    formatData: (v) => ({ "1": "物料", "2": "产品" }[String(v)] ?? v),
  },
  {
    dataType: "action",
@@ -237,18 +317,72 @@
    productName: "",
  },
  rules: {
    productName: [{ required: true, message: "请输入", trigger: "blur" }],
    productName: [
      { required: true, message: "请输入", trigger: "blur" },
      { max: 20, message: "产品名称不能超过20个字符", trigger: "blur" },
    ],
  },
  modelForm: {
    model: "",
    unit: "",
    drawingNumber: "",
    productType: "",
  },
  modelRules: {
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    drawingNumber: [{ required: true, message: "请输入", trigger: "blur" }],
    productType: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const { form, rules, modelForm, modelRules } = toRefs(data);
const importUpload = reactive({
  title: "产品导入",
  open: false,
  url: import.meta.env.VITE_APP_BASE_API + "/basic/product/import",
  headers: { Authorization: "Bearer " + getToken() },
  isUploading: false,
  beforeUpload: (file) => {
    const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    const isLt10M = file.size / 1024 / 1024 < 10;
    if (!isExcel) {
      proxy.$modal.msgError("上传文件只能是 xlsx/xls 格式!");
      return false;
    }
    if (!isLt10M) {
      proxy.$modal.msgError("上传文件大小不能超过 10MB!");
      return false;
    }
    return true;
  },
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
  },
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  },
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', response, file, fileList);
    importUpload.isUploading = false;
    if (response.code === 200) {
      proxy.$modal.msgSuccess("导入成功");
      importDia.value = false;
      if (importUploadRef.value) {
        importUploadRef.value.clearFiles();
      }
      getProductTreeList();
    } else {
      proxy.$modal.msgError(response.msg || "导入失败");
    }
  },
  onError: (error, file, fileList) => {
    console.log('上传失败', error, file, fileList);
    importUpload.isUploading = false;
    proxy.$modal.msgError("导入失败");
  }
});
// 查询产品树
const getProductTreeList = () => {
  treeLoad.value = true;
@@ -282,8 +416,8 @@
  modelOperationType.value = type;
  modelDia.value = true;
  modelForm.value.model = "";
  modelForm.value.model = "";
  modelForm.value.id = "";
  modelForm.value.productType = "";
  if (type === "edit") {
    modelForm.value = { ...data };
  }
@@ -315,6 +449,7 @@
  proxy.$refs.formRef.resetFields();
  productDia.value = false;
};
// 删除产品
const remove = (node, data) => {
  let ids = [];
@@ -456,6 +591,22 @@
  // 没匹配到返回false
  return false;
};
const handleImport = () => {
  importDia.value = true;
  if (importUploadRef.value) {
    importUploadRef.value.clearFiles();
  }
};
const submitImport = () => {
  importUploadRef.value.submit();
};
const importTemplate = () => {
  proxy.download("/basic/product/downloadTemplate", {}, "产品导入模板.xlsx");
};
getProductTreeList();
</script>
@@ -464,18 +615,31 @@
  display: flex;
}
.left {
  width: 380px;
  width: 450px;
  min-width: 450px;
  padding: 16px;
  background: #ffffff;
}
.left-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
.button-group {
  display: flex;
  gap: 10px;
}
.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;
@@ -483,13 +647,42 @@
  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;
}
</style>