Ver código fonte

尔泓产品库

cz 2 anos atrás
pai
commit
7ad07baa71

+ 4 - 2
src/components/byForm/index.vue

@@ -38,6 +38,7 @@
           :min="i.min"
           :maxlength="i.maxlength"
           :readonly="i.readonly ? i.readonly : false"
+          :style="i.style"
         />
         <el-input
           v-if="i.type == 'selectInput'"
@@ -101,8 +102,9 @@
           }"
           value-key="id"
           :placeholder="i.placeholder || '请选择'"
+          :disabled="i.disabled ? i.disabled : false"
           check-strictly
-           :style="i.style"
+          :style="i.style"
         />
         <el-date-picker
           v-model="formData[i.prop]"
@@ -337,7 +339,7 @@ const commonsEmit = (prop, item) => {
   emit("update:modelValue", formData.value);
 };
 const commonsEmitChange = (prop, item) => {
-  if (item.type == 'input') {
+  if (item.type == "input") {
     formData.value[item.prop] = prop.trim();
   }
   if (item.fn) {

+ 199 - 363
src/views/EHSD/productLibrary/companyProduct/index.vue

@@ -25,7 +25,7 @@
         }"
         :action-list="[
           {
-            text: '导入产品库',
+            text: 'Excel导入',
             action: () => openExcel(),
             disabled: false,
           },
@@ -49,10 +49,9 @@
         </template>
         <template #size="{ item }">
           <div>
-            <span>{{ item.productLong }}</span
-            >* <span>{{ item.packagWide }}</span
-            >*
-            <span>{{ item.packagHigh }}</span>
+            <span>{{ item.productLong }}cm</span>*
+            <span>{{ item.productWide }}cm</span>*
+            <span>{{ item.productHigh }}cm</span>
           </div>
         </template>
       </byTable>
@@ -60,7 +59,7 @@
     <el-dialog
       :title="modalType == 'add' ? '添加产品' : '编辑产品'"
       v-model="dialogVisible"
-      width="60%"
+      width="600"
       v-loading="submitLoading"
       destroy-on-close
     >
@@ -75,76 +74,20 @@
           <template #productPic>
             <div>
               <el-upload
-                v-model:formData.data.imgList="formData.data.imgList"
+                v-model:fileList="fileList"
                 action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
                 :data="uploadData"
                 list-type="picture-card"
-                :on-remove="(file) => handleRemove(file, 'imgList')"
-                :before-upload="(file) => handleBeforeUpload(file, 'imgList')"
+                :on-remove="handleRemove"
+                :before-upload="handleBeforeUpload"
                 accept=".gif, .jpeg, .jpg, .png"
               >
                 <el-icon><Plus /></el-icon>
               </el-upload>
             </div>
           </template>
-          <template #productPicOne>
-            <div>
-              <el-upload
-                v-model:formData.data.minorImgList="formData.data.minorImgList"
-                action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
-                :data="uploadData"
-                list-type="picture-card"
-                :on-remove="(file) => handleRemove(file, 'minorImgList')"
-                :before-upload="
-                  (file) => handleBeforeUpload(file, 'minorImgList')
-                "
-                accept=".gif, .jpeg, .jpg, .png"
-              >
-                <el-icon><Plus /></el-icon>
-              </el-upload>
-            </div>
-          </template>
-
-          <template #productPicTwo>
-            <div>
-              <el-upload
-                v-model:formData.data.fileList="formData.data.fileList"
-                :show-file-list="false"
-                class="upload-demo"
-                action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
-                :data="uploadData"
-                :before-upload="(file) => handleBeforeUpload(file, 'fileList')"
-              >
-                <el-button type="primary">选择</el-button>
-              </el-upload>
-              <div>
-                <div style="margin-top: 15px">
-                  <el-tag
-                    style="margin-right: 10px"
-                    class="ml-2"
-                    type="info"
-                    v-for="(item, index) in formData.data.fileListCopy"
-                    :key="index"
-                    closable
-                    @close="handleClose(index)"
-                    >{{ item.fileName }}</el-tag
-                  >
-                </div>
-              </div>
-            </div>
-          </template>
-
-          <template #detailDec>
-            <div style="width: 100%">
-              <Editor
-                :value="formData.data.remark"
-                @updateValue="updateContent"
-              />
-            </div>
-          </template>
         </byForm>
       </div>
-
       <template #footer>
         <el-button @click="dialogVisible = false" size="large">取 消</el-button>
         <el-button
@@ -158,23 +101,26 @@
       </template>
     </el-dialog>
     <el-dialog
-      title="Excel导入"
+      title="导入产品"
       v-model="openExcelDialog"
       width="400"
-      v-loading="loading"
+      v-loading="excelLoading"
     >
+      <el-upload
+        :action="actionUrl + '/productInfo/excelImportByEhsd'"
+        :headers="headers"
+        :on-success="handleSuccess"
+        :on-progress="handleProgress"
+        :show-file-list="false"
+        :on-error="handleError"
+        accept=".xlsx"
+      >
+        <el-button type="primary">点击导入</el-button>
+      </el-upload>
       <template #footer>
         <el-button @click="openExcelDialog = false" size="large"
           >取 消</el-button
         >
-        <el-button
-          type="primary"
-          @click="submitExcel()"
-          size="large"
-          :loading="submitLoading"
-        >
-          确 定
-        </el-button>
       </template>
     </el-dialog>
   </div>
@@ -185,7 +131,9 @@ import { ElMessage, ElMessageBox } from "element-plus";
 import byTable from "@/components/byTable/index";
 import byForm from "@/components/byForm/index";
 import treeList from "@/components/product/treeList";
-import Editor from "@/components/Editor/index.vue";
+import { getToken } from "@/utils/auth";
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const actionUrl = import.meta.env.VITE_APP_BASE_API;
 const loading = ref(false);
 const submitLoading = ref(false);
 const sourceList = ref({
@@ -202,15 +150,14 @@ const sourceList = ref({
 });
 let dialogVisible = ref(false);
 let openExcelDialog = ref(false);
+let excelLoading = ref(false);
 let modalType = ref("add");
 let rules = ref({
   productClassifyId: [
-    { required: true, message: "请选择类目", trigger: "change" },
-  ],
-  name: [{ required: true, message: "请输入名称(中文)", trigger: "blur" }],
-  nameEnglish: [
-    { required: true, message: "请输入名称(英文)", trigger: "blur" },
+    { required: true, message: "请选择产品分类", trigger: "change" },
   ],
+  name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
+  unit: [{ required: true, message: "请选择产品单位", trigger: "change" }],
   productLong: [
     { required: true, message: "请输入长 (cm)", trigger: "blur" },
   ],
@@ -226,16 +173,6 @@ let rules = ref({
   outerPackMethod: [
     { required: true, message: "请选择外包装方式", trigger: "change" },
   ],
-  netWeight: [{ required: true, message: "请输入净重(kg)", trigger: "blur" }],
-  salesUnit: [
-    { required: true, message: "请输入采购/销售单位", trigger: "blur" },
-  ],
-  issueReceiptUnit: [
-    { required: true, message: "请输入出入库单位", trigger: "blur" },
-  ],
-  conversionScale: [
-    { required: true, message: "请输入换算比列", trigger: "blur" },
-  ],
 });
 const { proxy } = getCurrentInstance();
 const selectConfig = reactive([
@@ -251,50 +188,54 @@ const config = computed(() => {
       attrs: {
         label: "图片",
         slot: "pic",
-        width: 80,
+        align: "center",
+        width: 100,
       },
     },
     {
       attrs: {
-        label: "编号",
-        prop: "customCode",
+        label: "产品编码",
+        prop: "code",
       },
     },
     {
       attrs: {
-        label: "名称",
+        label: "产品名称",
         prop: "name",
       },
     },
     {
       attrs: {
-        label: "尺寸",
-        slot: "size",
+        label: "单位",
+        prop: "unit",
+      },
+      render(unit) {
+        return proxy.dictValueLabel(unit, productUnit.value);
       },
     },
     {
       attrs: {
-        label: "成本价(¥)",
-        prop: "costPrice",
+        label: "尺寸",
+        slot: "size",
       },
     },
     {
       attrs: {
-        label: "销售指导价($)",
+        label: "销售指导价",
         prop: "price",
         width: 150,
       },
-    },
-    {
-      attrs: {
-        label: "型号",
-        prop: "spec",
+      render(price) {
+        return "$" + proxy.moneyFormat(price, 2);
       },
     },
     {
       attrs: {
-        label: "创建人",
-        prop: "createUserName",
+        label: "成本价",
+        prop: "costPrice",
+      },
+      render(costPrice) {
+        return "¥" + proxy.moneyFormat(costPrice, 2);
       },
     },
     {
@@ -359,12 +300,11 @@ const config = computed(() => {
 const uploadData = ref({});
 const fileList = ref([]);
 const fileListCopy = ref([]);
-const fileListOne = ref([]);
-const fileListCopyOne = ref([]);
 let formData = reactive({
   data: {},
 });
 const formOption = reactive({
+  disabled: false,
   inline: true,
   labelWidth: 100,
   itemWidth: 100,
@@ -374,6 +314,7 @@ const byform = ref(null);
 const treeListData = ref([]);
 const innerMethon = ref([]);
 const outsideMethon = ref([]);
+const productUnit = ref([]);
 const formConfig = computed(() => {
   return [
     {
@@ -383,137 +324,105 @@ const formConfig = computed(() => {
     {
       type: "treeSelect",
       prop: "productClassifyId",
-      label: "类",
+      label: "产品分类",
       data: treeListData.value,
-      itemWidth: 50,
+      itemWidth: 100,
+      disabled: false,
       style: {
         width: "100%",
       },
     },
     {
       type: "input",
-      prop: "customCode",
-      label: "编码",
-      itemWidth: 50,
-    },
-    {
-      type: "input",
       prop: "name",
-      label: "名称(中文)",
+      label: "产品名称",
       itemWidth: 100,
-      placeholder: "请输入名称(中文)",
+      disabled: false,
     },
     {
       type: "input",
       prop: "nameEnglish",
-      label: "名称(英文)",
+      label: "英文名",
       itemWidth: 100,
-      placeholder: "请输入名称(英文)",
+      disabled: false,
+    },
+    {
+      type: "select",
+      prop: "unit",
+      label: "单位",
+      required: true,
+      itemWidth: 100,
+      data: productUnit.value,
+      style: {
+        width: "50%",
+      },
+      disabled: false,
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
     },
     {
       type: "title",
-      title: "价格维护",
+      title: "价格信息",
     },
     {
-      type: "number",
+      type: "selectInput",
       prop: "price",
-      label: "销售指导价($)",
+      selectProp: "currency",
+      label: "销售指导价",
       itemWidth: 50,
-      controls: false,
-      precision: 2,
-      min: 0,
-      placeholder: "请输入销售指导价($)",
       style: {
         width: "100%",
       },
+      disabled: false,
     },
     {
-      type: "number",
+      type: "selectInput",
       prop: "costPrice",
-      label: "成本价(¥)",
+      selectProp: "currencyOne",
+      label: "成本价",
       itemWidth: 50,
-      controls: false,
-      precision: 2,
-      min: 0,
-      placeholder: "请输入成本价(¥)",
-
       style: {
         width: "100%",
       },
     },
     {
       type: "title",
-      title: "属性字段",
-    },
-    {
-      type: "input",
-      prop: "materialChinese",
-      label: "材质(中文)",
-      itemWidth: 50,
-      placeholder: "请输入材质(中文)",
-    },
-    {
-      type: "input",
-      prop: "material",
-      label: "材质(英文)",
-      itemWidth: 50,
-      placeholder: "请输入材质(英文)",
+      title: "属性信息",
     },
     {
       type: "input",
       prop: "spec",
-      label: "型号(中文)",
-      itemWidth: 50,
-      placeholder: "请输入型号(中文)",
-    },
-    {
-      type: "input",
-      prop: "productModelEn",
-      label: "型号(英文)",
-      itemWidth: 50,
-      placeholder: "请输入型号(英文)",
+      label: "规格型号",
+      itemWidth: 100,
+      disabled: false,
     },
     {
       type: "input",
       prop: "productLong",
-      label: "样品尺寸",
+      label: "尺寸",
       itemWidth: 33.33,
-      placeholder: "请输入长(cm)",
+      placeholder: "长(cm)",
+      disabled: false,
     },
     {
       type: "input",
       prop: "productWide",
       label: " ",
       itemWidth: 33.33,
-      placeholder: "请输入宽(cm)",
+      placeholder: "宽(cm)",
+      disabled: false,
     },
     {
       type: "input",
       prop: "productHigh",
       label: " ",
       itemWidth: 33.33,
-      placeholder: "请输入高(cm)",
-    },
-    {
-      type: "input",
-      prop: "packagLong",
-      label: "装箱尺寸",
-      itemWidth: 33.33,
-      placeholder: "请输入长(cm)",
-    },
-    {
-      type: "input",
-      prop: "packagWide",
-      label: " ",
-      itemWidth: 33.33,
-      placeholder: "请输入宽(cm)",
-    },
-    {
-      type: "input",
-      prop: "packagHigh",
-      label: " ",
-      itemWidth: 33.33,
-      placeholder: "请输入高(cm)",
+      placeholder: "高(cm)",
+      disabled: false,
     },
     {
       type: "select",
@@ -523,11 +432,11 @@ const formConfig = computed(() => {
       itemWidth: 50,
       multiple: true,
       data: innerMethon.value,
-      placeholder: "请选择内包装方式",
-
+      placeholder: "内包装方式",
       style: {
         width: "100%",
       },
+      disabled: false,
     },
     {
       type: "select",
@@ -537,135 +446,37 @@ const formConfig = computed(() => {
       itemWidth: 50,
       multiple: true,
       data: outsideMethon.value,
-      placeholder: "请选择外包装方式",
-
+      placeholder: "外包装方式",
       style: {
         width: "100%",
       },
+      disabled: false,
     },
     {
       type: "input",
       prop: "netWeight",
       label: "净重(kg)",
-      itemWidth: 50,
-      placeholder: "请输入净重(kg)",
-    },
-    {
-      type: "input",
-      prop: "grossWeight",
-      label: "毛重(kg)",
-      itemWidth: 50,
-      placeholder: "请输入毛重(kg)",
+      itemWidth: 100,
+      style: {
+        width: "30%",
+      },
     },
     {
       type: "input",
-      prop: "customsCode",
+      prop: "hsCode",
       label: "海关编码",
-      itemWidth: 50,
-      placeholder: "请输入海关编码",
-    },
-    {
-      type: "input",
-      prop: "moq",
-      label: "MOQ",
-      itemWidth: 50,
-      placeholder: "请输入MOQ",
-    },
-    {
-      type: "input",
-      prop: "salesUnit",
-      label: "采购/销售单位",
-      itemWidth: 50,
-      placeholder: "请输入采购/销售单位",
-    },
-    {
-      type: "input",
-      prop: "issueReceiptUnit",
-      label: "出入库单位",
-      itemWidth: 50,
-      placeholder: "请输入出入库单位",
-    },
-    {
-      type: "input",
-      prop: "conversionScale",
-      label: "换算比例",
-      itemWidth: 50,
-      placeholder: "换算比例 = 1 采购单位/1出入库单位",
-    },
-    {
-      type: "title",
-      title: "仓储字段",
-    },
-    {
-      type: "input",
-      prop: "safetyStock",
-      label: "安全库存",
-      itemWidth: 50,
-      placeholder: "请输入安全库存",
-    },
-    {
-      type: "input",
-      prop: "procurementCycle",
-      label: "采购周期",
-      itemWidth: 50,
-      placeholder: "请输入采购周期",
-    },
-    {
-      type: "radio",
-      prop: "firstInFirstOut",
-      label: "先入先出",
-      required: true,
-      border: true,
-      itemWidth: 70,
-      data: [
-        {
-          label: "不启用",
-          value: "0",
-        },
-        {
-          label: "一物一码",
-          value: "1",
-        },
-        {
-          label: "一物一码 & 先入先出",
-          value: "2",
-        },
-      ],
+      itemWidth: 100,
+      style: {
+        width: "30%",
+      },
+      disabled: false,
     },
     {
       type: "input",
-      prop: "qualityQualifiedRate",
-      label: "质检合格率",
-      itemWidth: 30,
-      placeholder: "请输入质检合格率",
-    },
-    {
-      type: "title",
-      title: "图片介绍",
-    },
-    {
-      type: "slot",
-      slotName: "productPic",
-      prop: "imgList",
-      label: "产品主图",
-    },
-    {
-      type: "slot",
-      slotName: "productPicOne",
-      prop: "minorImgList",
-      label: "产品副图",
-    },
-    {
-      type: "slot",
-      slotName: "productPicTwo",
-      prop: "fileList",
-      label: "制作图纸",
-    },
-    {
-      type: "slot",
-      slotName: "detailDec",
+      itemType: "textarea",
       prop: "remark",
-      label: "详情描述",
+      label: "备注",
+      itemWidth: 100,
     },
   ];
 });
@@ -692,7 +503,6 @@ const getList = async (req) => {
           proxy
             .post("/fileInfo/getList", {
               businessIdList: productIdList,
-              fileType: 1,
             })
             .then((fileObj) => {
               for (let i = 0; i < sourceList.value.data.length; i++) {
@@ -724,63 +534,41 @@ const openModal = () => {
     definition: "1",
     outerPackMethod: [],
     innerPackMethod: [],
-    remark: "",
-    imgList: [],
-    imgListCopy: [],
-    minorImgList: [],
-    minorImgListCopy: [],
     fileList: [],
     fileListCopy: [],
+    currency: "$",
+    currencyOne: "¥",
   };
-  // fileList.value = [];
-  // fileListCopy.value = [];
-  // fileListOne.value = [];
-  // fileListCopyOne.value = [];
 };
 
 const openExcel = () => {
   openExcelDialog.value = true;
 };
-const submitExcel = () => {
-  openExcelDialog.value = false;
-};
 
 const tree = ref(null);
 const needAtt = [
   "productClassifyId",
-  "customCode",
   "name",
   "spec",
   "remark",
   "fileList",
-  "imgList",
-  "minorImgList",
-  "fileListCopy",
-  "imgListCopy",
-  "minorImgListCopy",
   "id",
+  "unit",
+  "definition",
 ];
+let jsonObj = {};
 const submitForm = () => {
   byform.value.handleSubmit((valid) => {
-    if (!formData.data.imgListCopy.length > 0) {
+    if (!fileListCopy.value.length > 0) {
       return ElMessage({
-        message: "请上传产品图",
+        message: "请上传产品图",
         type: "info",
       });
     }
-    formData.data.imgList = formData.data.imgListCopy.map((x) => ({
+    formData.data.fileList = fileListCopy.value.map((x) => ({
       id: x.id,
       fileName: x.fileName,
     }));
-    formData.data.minorImgList = formData.data.minorImgListCopy.map((x) => ({
-      id: x.id,
-      fileName: x.fileName,
-    }));
-    formData.data.fileList = formData.data.fileListCopy.map((x) => ({
-      id: x.id,
-      fileName: x.fileName,
-    }));
-    let jsonObj = {};
     for (const key in formData.data) {
       if (needAtt.includes(key)) {
       } else {
@@ -804,6 +592,13 @@ const submitForm = () => {
         getList();
       },
       (err) => {
+        for (const key in jsonObj) {
+          formData.data[key] = jsonObj[key];
+        }
+        formData.data.innerPackMethod =
+          formData.data.innerPackMethod.split(",");
+        formData.data.outerPackMethod =
+          formData.data.outerPackMethod.split(",");
         submitLoading.value = false;
       }
     );
@@ -824,13 +619,9 @@ const getDtl = (row) => {
     res.definition = "1"; //产品
     res = {
       ...res,
+      currency: "$",
+      currencyOne: "¥",
       ...JSON.parse(res.ehsdJson),
-      imgList: [],
-      imgListCopy: [],
-      minorImgList: [],
-      minorImgListCopy: [],
-      fileList: [],
-      fileListCopy: [],
     };
     if (res.innerPackMethod) {
       res.innerPackMethod = res.innerPackMethod.split(",");
@@ -845,29 +636,43 @@ const getDtl = (row) => {
     formData.data = res;
     dialogVisible.value = true;
     proxy
-      .post("/fileInfo/getList", { businessIdList: [row.id], fileType: 1 })
-      .then((fileObj) => {
-        formData.data.imgList = fileObj[row.id] || [];
-        formData.data.imgListCopy = fileObj[row.id] || [];
-      });
-    proxy
-      .post("/fileInfo/getList", { businessIdList: [row.id], fileType: 2 })
-      .then((fileObj) => {
-        formData.data.minorImgList = fileObj[row.id] || [];
-        formData.data.minorImgListCopy = fileObj[row.id] || [];
-      });
-    proxy
-      .post("/fileInfo/getList", { businessIdList: [row.id], fileType: 3 })
+      .post("/fileInfo/getList", { businessIdList: [row.id] })
       .then((fileObj) => {
-        formData.data.fileList = fileObj[row.id] || [];
-        formData.data.fileListCopy = fileObj[row.id] || [];
+        if (fileObj[row.id]) {
+          fileList.value = fileObj[row.id].map((x) => ({
+            ...x,
+            url: x.fileUrl,
+          }));
+          fileListCopy.value = fileObj[row.id].map((x) => ({
+            ...x,
+            url: x.fileUrl,
+          }));
+        } else {
+          fileList.value = [];
+          fileListCopy.value = [];
+        }
       });
   });
 };
-const handleBeforeUpload = async (file, att) => {
+
+const isdisabled = ["price", "costPrice", "remark", "netWeight"];
+watch(modalType, (val) => {
+  if (val) {
+    for (let i = 0; i < formConfig.value.length; i++) {
+      const element = formConfig.value[i];
+      if (element.type != "title" || element.type != "slot") {
+        if (!isdisabled.includes(element.prop)) {
+          element.disabled = val == "edit" ? true : false;
+        }
+      }
+    }
+  }
+});
+
+const handleBeforeUpload = async (file) => {
   const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
   uploadData.value = res.uploadBody;
-  formData.data[att + "Copy"].push({
+  fileListCopy.value.push({
     id: res.id,
     fileName: res.fileName,
     path: res.fileUrl,
@@ -875,27 +680,54 @@ const handleBeforeUpload = async (file, att) => {
     uid: file.uid,
   });
 };
-const handleRemove = (file, att) => {
-  const index = formData.data[att + "Copy"].findIndex(
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
     (x) => x.uid === file.uid || x.id === file.id
   );
-  formData.data[att + "Copy"].splice(index, 1);
-};
-const handleClose = (index) => {
-  formData.data.fileList.splice(index, 1);
-  formData.data.fileListCopy.splice(index, 1);
+  fileListCopy.value.splice(index, 1);
 };
+
 const handleClickFile = (file) => {
   window.open(file.fileUrl, "_blank");
 };
-const updateContent = (val) => {
-  formData.data.remark = val;
+
+const handleProgress = () => {
+  excelLoading.value = true;
+};
+
+const handleError = (err) => {
+  console.log(res);
+  ElMessage({
+    message: `${err},请重试!`,
+    type: "info",
+  });
+  openExcelDialog.value = false;
+  excelLoading.value = false;
+};
+
+const handleSuccess = (res) => {
+  if (res.code != 200) {
+    return ElMessage({
+      message: `${res.msg},请重试!`,
+      type: "info",
+    });
+  } else {
+    ElMessage({
+      message: "导入成功!",
+      type: "success",
+    });
+    openExcelDialog.value = false;
+    excelLoading.value = false;
+    getList();
+  }
 };
 const getDict = () => {
   proxy
     .getDictOne([
       "inner_packaging_method_ehsd",
       "outside_packaging_method_ehsd",
+      "unit",
     ])
     .then((res) => {
       innerMethon.value = res["inner_packaging_method_ehsd"].map((x) => ({
@@ -906,6 +738,10 @@ const getDict = () => {
         label: x.dictValue,
         value: x.dictKey,
       }));
+      productUnit.value = res["unit"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
     });
 };
 getDict();

+ 795 - 5
src/views/EHSD/productLibrary/customerProduct/index.vue

@@ -1,13 +1,803 @@
 <template>
-  <div>
-    客户产品库
+  <div class="user">
+    <div class="tree">
+      <treeList
+        title="产品分类"
+        submitType="1"
+        :data="treeListData"
+        v-model="sourceList.pagination.productClassifyId"
+        @change="treeChange"
+        @changeTreeList="getTreeList"
+      >
+      </treeList>
+    </div>
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        highlight-current-row
+        :selectConfig="selectConfig"
+        :table-events="{
+          //element talbe事件都能传
+          select: select,
+        }"
+        :action-list="[
+          {
+            text: 'Excel导入',
+            action: () => openExcel(),
+            disabled: false,
+          },
+          {
+            text: '添加',
+            action: () => openModal('add'),
+            disabled: false,
+          },
+        ]"
+        @get-list="getList"
+      >
+        <template #pic="{ item }">
+          <div v-if="item.fileList.length > 0">
+            <img
+              :src="item.fileList[0].fileUrl"
+              class="pic"
+              @click="handleClickFile(item.fileList[0])"
+            />
+          </div>
+          <div v-else></div>
+        </template>
+        <template #size="{ item }">
+          <div>
+            <span>{{ item.productLong }}cm</span>*
+            <span>{{ item.productWide }}cm</span>*
+            <span>{{ item.productHigh }}cm</span>
+          </div>
+        </template>
+      </byTable>
+    </div>
+    <el-dialog
+      :title="modalType == 'add' ? '添加产品' : '编辑产品'"
+      v-model="dialogVisible"
+      width="600"
+      v-loading="submitLoading"
+      destroy-on-close
+    >
+      <div class="public_height_dialog">
+        <byForm
+          :formConfig="formConfig"
+          :formOption="formOption"
+          v-model="formData.data"
+          :rules="rules"
+          ref="byform"
+        >
+          <template #productPic>
+            <div>
+              <el-upload
+                v-model:fileList="fileList"
+                action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+                :data="uploadData"
+                list-type="picture-card"
+                :on-remove="handleRemove"
+                :before-upload="handleBeforeUpload"
+                accept=".gif, .jpeg, .jpg, .png"
+              >
+                <el-icon><Plus /></el-icon>
+              </el-upload>
+            </div>
+          </template>
+        </byForm>
+      </div>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+    <el-dialog
+      title="导入产品"
+      v-model="openExcelDialog"
+      width="400"
+      v-loading="excelLoading"
+    >
+      <el-upload
+        :action="actionUrl + '/productInfo/excelImportByEhsd'"
+        :headers="headers"
+        :on-success="handleSuccess"
+        :on-progress="handleProgress"
+        :show-file-list="false"
+        :on-error="handleError"
+        accept=".xlsx"
+      >
+        <el-button type="primary">点击导入</el-button>
+      </el-upload>
+      <template #footer>
+        <el-button @click="openExcelDialog = false" size="large"
+          >取 消</el-button
+        >
+      </template>
+    </el-dialog>
   </div>
 </template>
-
+  
 <script setup>
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import treeList from "@/components/product/treeList";
+import { getToken } from "@/utils/auth";
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const actionUrl = import.meta.env.VITE_APP_BASE_API;
+const loading = ref(false);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+    type: "",
+    productClassifyId: "",
+    keyword: "",
+    definition: "1",
+  },
+});
+let dialogVisible = ref(false);
+let openExcelDialog = ref(false);
+let excelLoading = ref(false);
+let modalType = ref("add");
+let rules = ref({
+  customerId: [
+    { required: true, message: "请选择客户名称", trigger: "change" },
+  ],
+  productClassifyId: [
+    { required: true, message: "请选择产品分类", trigger: "change" },
+  ],
+  name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
+  unit: [{ required: true, message: "请选择产品单位", trigger: "change" }],
+  productLong: [
+    { required: true, message: "请输入长 (cm)", trigger: "blur" },
+  ],
+  productWide: [
+    { required: true, message: "请输入宽 (cm)", trigger: "blur" },
+  ],
+  productHigh: [
+    { required: true, message: "请输入高 (cm)", trigger: "blur" },
+  ],
+  innerPackMethod: [
+    { required: true, message: "请选择内包装方式", trigger: "change" },
+  ],
+  outerPackMethod: [
+    { required: true, message: "请选择外包装方式", trigger: "change" },
+  ],
+});
+const { proxy } = getCurrentInstance();
+const selectConfig = reactive([
+  // {
+  //   label: "产品类型",
+  //   prop: "type",
+  //   data: [],
+  // },
+]);
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "图片",
+        slot: "pic",
+        align: "center",
+        width: 100,
+      },
+    },
+    {
+      attrs: {
+        label: "产品编码",
+        prop: "code",
+      },
+    },
+    {
+      attrs: {
+        label: "产品名称",
+        prop: "name",
+      },
+    },
+    {
+      attrs: {
+        label: "单位",
+        prop: "unit",
+      },
+      render(unit) {
+        return proxy.dictValueLabel(unit, productUnit.value);
+      },
+    },
+    {
+      attrs: {
+        label: "尺寸",
+        slot: "size",
+      },
+    },
+    {
+      attrs: {
+        label: "销售指导价",
+        prop: "price",
+        width: 150,
+      },
+      render(price) {
+        return "$" + proxy.moneyFormat(price, 2);
+      },
+    },
+    {
+      attrs: {
+        label: "成本价",
+        prop: "costPrice",
+      },
+      render(costPrice) {
+        return "¥" + proxy.moneyFormat(costPrice, 2);
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "120",
+        align: "center",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "修改",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              getDtl(row);
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "danger",
+              text: true,
+            },
+            el: "button",
+            click() {
+              // 弹窗提示是否删除
+              ElMessageBox.confirm(
+                "此操作将永久删除该数据, 是否继续?",
+                "提示",
+                {
+                  confirmButtonText: "确定",
+                  cancelButtonText: "取消",
+                  type: "warning",
+                }
+              ).then(() => {
+                // 删除
+                proxy
+                  .post("/productInfo/delete", {
+                    id: row.id,
+                  })
+                  .then((res) => {
+                    ElMessage({
+                      message: "删除成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
 
-</script>
+const uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
+let formData = reactive({
+  data: {},
+});
+const formOption = reactive({
+  disabled: false,
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const byform = ref(null);
+const treeListData = ref([]);
+const innerMethon = ref([]);
+const outsideMethon = ref([]);
+const productUnit = ref([]);
+const customerData = ref([]);
+const formConfig = computed(() => {
+  return [
+    {
+      type: "title",
+      title: "客户信息",
+    },
+    {
+      type: "select",
+      prop: "customerId",
+      label: "客户名称",
+      required: true,
+      itemWidth: 100,
+      data: customerData.value,
+      style: {
+        width: "50%",
+      },
+      disabled: false,
+    },
+    {
+      type: "title",
+      title: "基本信息",
+    },
+    {
+      type: "treeSelect",
+      prop: "productClassifyId",
+      label: "产品分类",
+      data: treeListData.value,
+      itemWidth: 100,
+      disabled: false,
+      style: {
+        width: "100%",
+      },
+    },
+    {
+      type: "input",
+      prop: "name",
+      label: "产品名称",
+      itemWidth: 100,
+      disabled: false,
+    },
+    {
+      type: "input",
+      prop: "nameEnglish",
+      label: "英文名",
+      itemWidth: 100,
+      disabled: false,
+    },
+    {
+      type: "select",
+      prop: "unit",
+      label: "单位",
+      required: true,
+      itemWidth: 100,
+      data: productUnit.value,
+      style: {
+        width: "50%",
+      },
+      disabled: false,
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
+    {
+      type: "title",
+      title: "价格信息",
+    },
+    {
+      type: "selectInput",
+      prop: "price",
+      selectProp: "currency",
+      label: "销售指导价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+      disabled: false,
+    },
+    {
+      type: "selectInput",
+      prop: "costPrice",
+      selectProp: "currencyOne",
+      label: "成本价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+    },
+    {
+      type: "title",
+      title: "属性信息",
+    },
+    {
+      type: "input",
+      prop: "spec",
+      label: "规格型号",
+      itemWidth: 100,
+      disabled: false,
+    },
+    {
+      type: "input",
+      prop: "productLong",
+      label: "尺寸",
+      itemWidth: 33.33,
+      placeholder: "长(cm)",
+      disabled: false,
+    },
+    {
+      type: "input",
+      prop: "productWide",
+      label: " ",
+      itemWidth: 33.33,
+      placeholder: "宽(cm)",
+      disabled: false,
+    },
+    {
+      type: "input",
+      prop: "productHigh",
+      label: " ",
+      itemWidth: 33.33,
+      placeholder: "高(cm)",
+      disabled: false,
+    },
+    {
+      type: "select",
+      prop: "innerPackMethod",
+      label: "内包装方式",
+      required: true,
+      itemWidth: 50,
+      multiple: true,
+      data: innerMethon.value,
+      placeholder: "内包装方式",
+      style: {
+        width: "100%",
+      },
+      disabled: false,
+    },
+    {
+      type: "select",
+      prop: "outerPackMethod",
+      label: "外包装方式",
+      required: true,
+      itemWidth: 50,
+      multiple: true,
+      data: outsideMethon.value,
+      placeholder: "外包装方式",
+      style: {
+        width: "100%",
+      },
+      disabled: false,
+    },
+    {
+      type: "input",
+      prop: "netWeight",
+      label: "净重(kg)",
+      itemWidth: 100,
+      style: {
+        width: "30%",
+      },
+    },
+    {
+      type: "input",
+      prop: "hsCode",
+      label: "海关编码",
+      itemWidth: 100,
+      style: {
+        width: "30%",
+      },
+      disabled: false,
+    },
+    {
+      type: "input",
+      itemType: "textarea",
+      prop: "remark",
+      label: "备注",
+      itemWidth: 100,
+    },
+  ];
+});
 
-<style lang="scss" scoped>
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/productInfo/getCustomerProductList", sourceList.value.pagination)
+    .then(
+      (message) => {
+        sourceList.value.data = message.rows.map((x) => ({
+          ...x,
+          fileList: [],
+          ...JSON.parse(x.ehsdJson),
+        }));
+        sourceList.value.pagination.total = message.total;
+        setTimeout(() => {
+          loading.value = false;
+        }, 200);
+        const productIdList = message.rows.map((x) => x.id);
+        // 请求文件数据并回显
+        if (productIdList.length > 0) {
+          proxy
+            .post("/fileInfo/getList", {
+              businessIdList: productIdList,
+            })
+            .then((fileObj) => {
+              for (let i = 0; i < sourceList.value.data.length; i++) {
+                const e = sourceList.value.data[i];
+                for (const key in fileObj) {
+                  if (e.id === key) {
+                    e.fileList = fileObj[key];
+                  }
+                }
+              }
+            });
+        }
+      },
+      (err) => {
+        loading.value = false;
+      }
+    );
+};
+
+const treeChange = (e) => {
+  sourceList.value.pagination.productClassifyId = e.id;
+  getList({ productClassifyId: e.id });
+};
+
+const openModal = () => {
+  dialogVisible.value = true;
+  modalType.value = "add";
+  formData.data = {
+    definition: "1",
+    outerPackMethod: [],
+    innerPackMethod: [],
+    fileList: [],
+    fileListCopy: [],
+    currency: "$",
+    currencyOne: "¥",
+  };
+};
+
+const openExcel = () => {
+  openExcelDialog.value = true;
+};
+
+const tree = ref(null);
+const needAtt = [
+  "productClassifyId",
+  "name",
+  "spec",
+  "remark",
+  "fileList",
+  "id",
+  "unit",
+  "definition",
+];
+let jsonObj = {};
+const submitForm = () => {
+  byform.value.handleSubmit((valid) => {
+    if (!fileListCopy.value.length > 0) {
+      return ElMessage({
+        message: "请上传产品图片",
+        type: "info",
+      });
+    }
+    formData.data.fileList = fileListCopy.value.map((x) => ({
+      id: x.id,
+      fileName: x.fileName,
+    }));
+    for (const key in formData.data) {
+      if (needAtt.includes(key)) {
+      } else {
+        jsonObj[key] = formData.data[key];
+        delete formData.data[key];
+      }
+    }
+    jsonObj.innerPackMethod = jsonObj.innerPackMethod.join(",");
+    jsonObj.outerPackMethod = jsonObj.outerPackMethod.join(",");
+    // jsonObj.customerId = jsonObj.customerId + "";
+    jsonObj.type = "2"; //2为客户产品库
+    formData.data.ehsdJson = JSON.stringify(jsonObj);
+    submitLoading.value = true;
+    proxy.post(`/productInfo/${modalType.value}ByEhsd`, formData.data).then(
+      (res) => {
+        ElMessage({
+          message: modalType.value == "add" ? "添加成功" : "编辑成功",
+          type: "success",
+        });
+        dialogVisible.value = false;
+        submitLoading.value = false;
+        getList();
+      },
+      (err) => {
+        for (const key in jsonObj) {
+          formData.data[key] = jsonObj[key];
+        }
+        formData.data.innerPackMethod =
+          formData.data.innerPackMethod.split(",");
+        formData.data.outerPackMethod =
+          formData.data.outerPackMethod.split(",");
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const getTreeList = () => {
+  proxy
+    .post("/productClassify/tree", { parentId: "", name: "", definition: "1" })
+    .then((message) => {
+      treeListData.value = message;
+    });
+};
+
+const getDtl = (row) => {
+  modalType.value = "edit";
+  proxy.post("/productInfo/detailByEhsd", { id: row.id }).then((res) => {
+    res.definition = "1"; //产品
+    res = {
+      ...res,
+      currency: "$",
+      currencyOne: "¥",
+      ...JSON.parse(res.ehsdJson),
+    };
+    if (res.innerPackMethod) {
+      res.innerPackMethod = res.innerPackMethod.split(",");
+    } else {
+      res.innerPackMethod = [];
+    }
+    if (res.outerPackMethod) {
+      res.outerPackMethod = res.outerPackMethod.split(",");
+    } else {
+      res.outerPackMethod = [];
+    }
+    formData.data = res;
+    dialogVisible.value = true;
+    proxy
+      .post("/fileInfo/getList", { businessIdList: [row.id] })
+      .then((fileObj) => {
+        if (fileObj[row.id]) {
+          fileList.value = fileObj[row.id].map((x) => ({
+            ...x,
+            url: x.fileUrl,
+          }));
+          fileListCopy.value = fileObj[row.id].map((x) => ({
+            ...x,
+            url: x.fileUrl,
+          }));
+        } else {
+          fileList.value = [];
+          fileListCopy.value = [];
+        }
+      });
+  });
+};
+const isdisabled = ["price", "costPrice", "remark", "netWeight"];
+watch(modalType, (val) => {
+  if (val) {
+    for (let i = 0; i < formConfig.value.length; i++) {
+      const element = formConfig.value[i];
+      if (element.type != "title" || element.type != "slot") {
+        if (!isdisabled.includes(element.prop)) {
+          element.disabled = val == "edit" ? true : false;
+        }
+      }
+    }
+  }
+});
 
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    url: res.fileUrl,
+    uid: file.uid,
+  });
+};
+
+const handleRemove = (file) => {
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
+  fileListCopy.value.splice(index, 1);
+};
+
+const handleClickFile = (file) => {
+  window.open(file.fileUrl, "_blank");
+};
+
+const handleProgress = () => {
+  excelLoading.value = true;
+};
+
+const handleError = (err) => {
+  console.log(res);
+  ElMessage({
+    message: `${err},请重试!`,
+    type: "info",
+  });
+  openExcelDialog.value = false;
+  excelLoading.value = false;
+};
+
+const handleSuccess = (res) => {
+  if (res.code != 200) {
+    return ElMessage({
+      message: `${res.msg},请重试!`,
+      type: "info",
+    });
+  } else {
+    ElMessage({
+      message: "导入成功!",
+      type: "success",
+    });
+    openExcelDialog.value = false;
+    excelLoading.value = false;
+    getList();
+  }
+};
+const getDict = () => {
+  proxy
+    .getDictOne([
+      "inner_packaging_method_ehsd",
+      "outside_packaging_method_ehsd",
+      "unit",
+    ])
+    .then((res) => {
+      innerMethon.value = res["inner_packaging_method_ehsd"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+      outsideMethon.value = res["outside_packaging_method_ehsd"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+      productUnit.value = res["unit"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+    });
+
+  proxy.post("/customer/page", { pageNum: 1, pageSize: 9999 }).then(
+    (res) => {
+      customerData.value = res.rows.map((x) => ({
+        label: x.name,
+        value: x.id,
+      }));
+    },
+    (err) => {
+      console.log(err);
+    }
+  );
+};
+getDict();
+getTreeList();
+getList();
+</script>
+  
+<style lang="scss" scoped>
+.user {
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  .tree {
+    width: 300px;
+  }
+  .content {
+    width: calc(100% - 320px);
+  }
+}
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
 </style>