Parcourir la source

已选择产品

cz il y a 1 an
Parent
commit
9f9028b458

+ 1 - 0
package.json

@@ -45,6 +45,7 @@
     "jsencrypt": "3.3.1",
     "jspdf": "^2.5.1",
     "lossless-json": "^2.0.8",
+    "mitt": "^3.0.1",
     "moment": "^2.29.4",
     "nprogress": "0.2.0",
     "pinia": "2.0.22",

+ 3 - 0
src/bus/index.js

@@ -0,0 +1,3 @@
+import mitt from 'mitt';
+const $bus = mitt();
+export default $bus;

+ 17 - 45
src/components/contractCom/contractDetails.vue

@@ -1,61 +1,34 @@
 <template>
   <div style="background: #fff; padding: 0 20px">
-    <el-tabs
-      v-model="activeName"
-      class="demo-tabs"
-      @tab-click="handleClick"
-      @tab-change="handleChange"
-      stretch
-    >
+    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick" @tab-change="handleChange" stretch>
       <el-tab-pane label="销售合同" name="first"></el-tab-pane>
       <el-tab-pane label="采购合同" name="second"></el-tab-pane>
       <el-tab-pane label="交易明细" name="third"></el-tab-pane>
       <el-tab-pane label="外销跟单" name="four"> </el-tab-pane>
-      <el-tab-pane
-        label="预算表"
-        name="five"
-        v-if="isShowSalesFinancemanArrange"
-      >
+      <el-tab-pane label="预算表" name="five" v-if="isShowSalesFinancemanArrange">
       </el-tab-pane>
-      <el-tab-pane
-        label="结算表"
-        name="six"
-        v-if="isShowSalesFinancemanArrange"
-      >
+      <el-tab-pane label="结算表" name="six" v-if="isShowSalesFinancemanArrange">
       </el-tab-pane>
-      <el-tab-pane
-        label="单证"
-        name="seven"
-        v-if="isShowSalesFinancemanArrange"
-      ></el-tab-pane>
+      <el-tab-pane label="单证" name="seven" v-if="isShowSalesFinancemanArrange"></el-tab-pane>
     </el-tabs>
     <div class="content-box" v-if="['first', 'second'].includes(activeName)">
       <div class="left">
-        <div
-          v-for="i in leftList"
-          :key="i.id"
-          class="left-item"
-          :style="{ color: currentItem.id === i.id ? '#409eff' : '' }"
-          @click="handleItemClick(i)"
-        >
+        <div v-for="i in leftList" :key="i.id" class="left-item" :style="{ color: currentItem.id === i.id ? '#409eff' : '' }"
+             @click="handleItemClick(i)">
           <div v-if="activeName === 'first'">{{ i.code }}</div>
           <div v-if="activeName === 'second'">
             {{ i.code }}
           </div>
         </div>
       </div>
+      <div class="center"></div>
       <div class="right">
         <div v-if="leftList && leftList.length > 0">
-          <div
-            style="text-align: right; margin-bottom: 20px"
-            v-if="activeName === 'first'"
-          >
+          <div style="text-align: right; margin-bottom: 20px" v-if="activeName === 'first'">
             <!-- <el-button type="primary" @click="pushProcessApproval(currentItem)"
               >查看详情</el-button
             > -->
-            <el-button type="primary" @click="openPDF(currentItem)"
-              >查看PDF</el-button
-            >
+            <el-button type="primary" @click="openPDF(currentItem)">查看PDF</el-button>
           </div>
           <div v-if="activeName === 'first'">
             <!-- <ContractPDFOne :rowData="rowData"></ContractPDFOne> -->
@@ -70,14 +43,7 @@
       </div>
     </div>
     <div v-if="activeName === 'third'">
-      <byTable
-        :hidePagination="true"
-        :hideSearch="true"
-        :source="tableData"
-        :config="config"
-        highlight-current-row
-        :action-list="[]"
-      >
+      <byTable :hidePagination="true" :hideSearch="true" :source="tableData" :config="config" highlight-current-row :action-list="[]">
         <template #amount="{ item }">
           <div>{{ item.currency }} {{ moneyFormat(item.amount, 2) }}</div>
         </template>
@@ -336,8 +302,14 @@ checkShow();
       cursor: pointer;
     }
   }
+  .center {
+    // height: calc(100% + 10px);
+    margin-top: -15px;
+    width: 20px;
+    background-color: #f0f2f5;
+  }
   .right {
-    width: calc(100% - 150px);
+    width: calc(100% - 150px - 20px);
     padding-left: 10px;
   }
 }

+ 14 - 30
src/components/contractCom/contractDetailsOne.vue

@@ -1,25 +1,14 @@
 <template>
   <div style="background: #fff; padding: 0 20px">
-    <el-tabs
-      v-model="activeName"
-      class="demo-tabs"
-      @tab-click="handleClick"
-      @tab-change="handleChange"
-      stretch
-    >
+    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick" @tab-change="handleChange" stretch>
       <el-tab-pane label="样品单" name="first"></el-tab-pane>
       <el-tab-pane label="采购合同" name="second"></el-tab-pane>
       <el-tab-pane label="交易明细" name="third"></el-tab-pane>
     </el-tabs>
     <div class="content-box" v-if="activeName !== 'third'">
       <div class="left">
-        <div
-          v-for="(i, index) in leftList"
-          :key="i.id"
-          class="left-item"
-          :style="{ color: currentItem.id === i.id ? '#409eff' : '' }"
-          @click="handleItemClick(i)"
-        >
+        <div v-for="(i, index) in leftList" :key="i.id" class="left-item" :style="{ color: currentItem.id === i.id ? '#409eff' : '' }"
+             @click="handleItemClick(i)">
           <div v-if="activeName === 'first'">
             <!-- v {{ i.version || index + 1 }} -->
             {{ i.code }}
@@ -29,18 +18,14 @@
           </div>
         </div>
       </div>
+      <div class="center"></div>
       <div class="right">
         <div v-if="leftList && leftList.length > 0">
-          <div
-            style="text-align: right; margin-bottom: 20px"
-            v-if="activeName === 'first'"
-          >
+          <div style="text-align: right; margin-bottom: 20px" v-if="activeName === 'first'">
             <!-- <el-button type="primary" @click="pushProcessApproval(currentItem)"
               >查看详情</el-button
             > -->
-            <el-button type="primary" @click="openPDF(currentItem)"
-              >查看PDF</el-button
-            >
+            <el-button type="primary" @click="openPDF(currentItem)">查看PDF</el-button>
           </div>
           <div v-if="activeName === 'first'">
             <!-- <SamplePDF :rowData="rowData"></SamplePDF> -->
@@ -55,14 +40,7 @@
       </div>
     </div>
     <div v-if="activeName === 'third'">
-      <byTable
-        :hidePagination="true"
-        :hideSearch="true"
-        :source="tableData"
-        :config="config"
-        highlight-current-row
-        :action-list="[]"
-      >
+      <byTable :hidePagination="true" :hideSearch="true" :source="tableData" :config="config" highlight-current-row :action-list="[]">
         <template #amount="{ item }">
           <div>{{ item.currency }} {{ moneyFormat(item.amount, 2) }}</div>
         </template>
@@ -288,8 +266,14 @@ if (route.query.currentSampleId) {
       cursor: pointer;
     }
   }
+  .center {
+    // height: calc(100% + 10px);
+    margin-top: -15px;
+    width: 20px;
+    background-color: #f0f2f5;
+  }
   .right {
-    width: calc(100% - 150px);
+    width: calc(100% - 150px - 20px);
     padding-left: 10px;
   }
 }

+ 7 - 4
src/components/process/EHSD/Contract.vue

@@ -580,11 +580,12 @@
     </byForm>
 
     <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
-      <CompanyProduct :selectStatus="true" @selectProduct="selectProduct"></CompanyProduct>
+      <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.contractProductList"></SelectCompanyProduct>
     </el-dialog>
 
     <el-dialog v-if="openProductCustomer" v-model="openProductCustomer" title="客户产品库" width="90%" append-to-body>
-      <CustomerProduct :selectStatus="true" :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"></CustomerProduct>
+      <SelectCustomerProduct :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"
+                             :alreadySelectData="formData.data.contractProductList"></SelectCustomerProduct>
     </el-dialog>
 
     <el-dialog v-if="copyContract" v-model="copyContract" :title="copyType === 1 ? '合同选择' : '样品单选择'" width="90%" append-to-body>
@@ -596,8 +597,8 @@
 
 <script setup>
 import byForm from "@/components/byForm/index";
-import CompanyProduct from "@/views/EHSD/productLibrary/companyProduct/index";
-import CustomerProduct from "@/views/EHSD/productLibrary/customerProduct/index";
+import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
+import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
 import { ElMessage } from "element-plus";
 import selectCity from "@/components/selectCity/index.vue";
 import { useRoute } from "vue-router";
@@ -1229,6 +1230,7 @@ const selectProduct = (goods) => {
         amount: "",
         tradeMethods: "",
         packMethod: packMethod,
+        selectType: goods.selectType,
       });
     } else {
       formData.data.contractProductList = [
@@ -1249,6 +1251,7 @@ const selectProduct = (goods) => {
           amount: "",
           tradeMethods: "",
           packMethod: packMethod,
+          selectType: goods.selectType,
         },
       ];
     }

+ 7 - 4
src/components/process/EHSD/ContractChange.vue

@@ -572,19 +572,20 @@
     </byForm>
 
     <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
-      <CompanyProduct :selectStatus="true" @selectProduct="selectProduct"></CompanyProduct>
+      <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.contractProductList"></SelectCompanyProduct>
     </el-dialog>
 
     <el-dialog v-if="openProductCustomer" v-model="openProductCustomer" title="客户产品库" width="90%" append-to-body>
-      <CustomerProduct :selectStatus="true" :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"></CustomerProduct>
+      <SelectCustomerProduct :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"
+                             :alreadySelectData="formData.data.contractProductList"></SelectCustomerProduct>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
 import byForm from "@/components/byForm/index";
-import CompanyProduct from "@/views/EHSD/productLibrary/companyProduct/index";
-import CustomerProduct from "@/views/EHSD/productLibrary/customerProduct/index";
+import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
+import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
 import { ElMessage } from "element-plus";
 import selectCity from "@/components/selectCity/index.vue";
 import { useRoute } from "vue-router";
@@ -1259,6 +1260,7 @@ const selectProduct = (goods) => {
         amount: "",
         tradeMethods: "",
         packMethod: packMethod,
+        selectType: goods.selectType,
       });
     } else {
       formData.data.contractProductList = [
@@ -1279,6 +1281,7 @@ const selectProduct = (goods) => {
           amount: "",
           tradeMethods: "",
           packMethod: packMethod,
+          selectType: goods.selectType,
         },
       ];
     }

+ 7 - 4
src/components/process/EHSD/PriceSheet.vue

@@ -330,19 +330,20 @@
     </byForm>
 
     <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
-      <CompanyProduct :selectStatus="true" @selectProduct="selectProduct"></CompanyProduct>
+      <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.quotationProductList"></SelectCompanyProduct>
     </el-dialog>
 
     <el-dialog v-if="openProductCustomer" v-model="openProductCustomer" title="客户产品库" width="90%" append-to-body>
-      <CustomerProduct :selectStatus="true" :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"></CustomerProduct>
+      <SelectCustomerProduct :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"
+                             :alreadySelectData="formData.data.quotationProductList"></SelectCustomerProduct>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
 import byForm from "@/components/byForm/index";
-import CompanyProduct from "@/views/EHSD/productLibrary/companyProduct/index";
-import CustomerProduct from "@/views/EHSD/productLibrary/customerProduct/index";
+import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
+import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
 import { ElMessage } from "element-plus";
 import selectCity from "@/components/selectCity/index.vue";
 import { useRoute } from "vue-router";
@@ -793,6 +794,7 @@ const selectProduct = (goods) => {
         amount: "",
         tradeMethods: "",
         packMethod: packMethod,
+        selectType: goods.selectType,
       });
     } else {
       formData.data.quotationProductList = [
@@ -811,6 +813,7 @@ const selectProduct = (goods) => {
           amount: "",
           tradeMethods: "",
           packMethod: packMethod,
+          selectType: goods.selectType,
         },
       ];
     }

+ 7 - 5
src/components/process/EHSD/Sample.vue

@@ -524,11 +524,12 @@
     </byForm>
 
     <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
-      <CompanyProduct :selectStatus="true" @selectProduct="selectProduct"></CompanyProduct>
+      <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.sampleProductList"></SelectCompanyProduct>
     </el-dialog>
 
     <el-dialog v-if="openProductCustomer" v-model="openProductCustomer" title="客户产品库" width="90%" append-to-body>
-      <CustomerProduct :selectStatus="true" :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"></CustomerProduct>
+      <SelectCustomerProduct :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"
+                             :alreadySelectData="formData.data.sampleProductList"></SelectCustomerProduct>
     </el-dialog>
 
     <el-dialog v-if="copyContract" v-model="copyContract" title="样品单选择" width="90%" append-to-body>
@@ -539,10 +540,9 @@
 
 <script setup>
 import byForm from "@/components/byForm/index";
-import CompanyProduct from "@/views/EHSD/productLibrary/companyProduct/index";
-import CustomerProduct from "@/views/EHSD/productLibrary/customerProduct/index";
+import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
+import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
 import SelectSample from "@/components/contractCom/selectSample.vue";
-
 import { ElMessage } from "element-plus";
 import selectCity from "@/components/selectCity/index.vue";
 import { useRoute } from "vue-router";
@@ -1075,6 +1075,7 @@ const selectProduct = (goods) => {
         amount: "",
         tradeMethods: "",
         packMethod: packMethod,
+        selectType: goods.selectType,
       });
     } else {
       formData.data.sampleProductList = [
@@ -1094,6 +1095,7 @@ const selectProduct = (goods) => {
           amount: "",
           tradeMethods: "",
           packMethod: packMethod,
+          selectType: goods.selectType,
         },
       ];
     }

+ 7 - 4
src/components/process/EHSD/SampleChange.vue

@@ -518,19 +518,20 @@
     </byForm>
 
     <el-dialog v-if="openProductCompany" v-model="openProductCompany" title="公司产品库" width="90%" append-to-body>
-      <CompanyProduct :selectStatus="true" @selectProduct="selectProduct"></CompanyProduct>
+      <SelectCompanyProduct @selectProduct="selectProduct" :alreadySelectData="formData.data.sampleProductList"></SelectCompanyProduct>
     </el-dialog>
 
     <el-dialog v-if="openProductCustomer" v-model="openProductCustomer" title="客户产品库" width="90%" append-to-body>
-      <CustomerProduct :selectStatus="true" :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"></CustomerProduct>
+      <SelectCustomerProduct :buyCorporationId="formData.data.buyCorporationId" @selectProduct="selectProduct"
+                             :alreadySelectData="formData.data.sampleProductList"></SelectCustomerProduct>
     </el-dialog>
   </div>
 </template>
 
 <script setup>
 import byForm from "@/components/byForm/index";
-import CompanyProduct from "@/views/EHSD/productLibrary/companyProduct/index";
-import CustomerProduct from "@/views/EHSD/productLibrary/customerProduct/index";
+import SelectCompanyProduct from "@/components/product/SelectCompanyProduct.vue";
+import SelectCustomerProduct from "@/components/product/SelectCustomerProduct.vue";
 import { ElMessage } from "element-plus";
 import selectCity from "@/components/selectCity/index.vue";
 import { useRoute } from "vue-router";
@@ -1068,6 +1069,7 @@ const selectProduct = (goods) => {
         amount: "",
         tradeMethods: "",
         packMethod: packMethod,
+        selectType: goods.selectType,
       });
     } else {
       formData.data.sampleProductList = [
@@ -1087,6 +1089,7 @@ const selectProduct = (goods) => {
           amount: "",
           tradeMethods: "",
           packMethod: packMethod,
+          selectType: goods.selectType,
         },
       ];
     }

+ 744 - 0
src/components/product/SelectCompanyProduct.vue

@@ -0,0 +1,744 @@
+<template>
+  <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="[]" @get-list="getList">
+        <template #name="{ item }">
+          <div>
+            <span style="color: #409eff; cursor: pointer; word-break: break-all" @click="handleOpenProductContract(item)">{{ item.name }}</span>
+          </div>
+        </template>
+        <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>
+        <template #price="{ item }">
+          <div>
+            <span v-if="item.price">{{ item.currency }} {{ item.price }}</span>
+          </div>
+        </template>
+        <template #costPrice="{ item }">
+          <div>
+            <span v-if="item.costPrice">{{ item.costCurrency }} {{ item.costPrice }}</span>
+          </div>
+        </template>
+      </byTable>
+    </div>
+    <div class="right">
+      <div style="margin-bottom:30px">
+        <TitleInfo :content="'已选择商品'"></TitleInfo>
+      </div>
+      <el-tag style="margin-right: 10px; margin-bottom: 10px" type="info" v-for="(good, index) in goodList" :key="good.productId">
+        {{ good.productCnName || good.productName }}
+      </el-tag>
+    </div>
+
+    <el-dialog :title="modalType == 'add' ? '添加产品' : '编辑产品'" v-model="dialogVisible" width="700" 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 #nameEnglish>
+            <div style="width: 100%">
+              <el-form-item label="英文名" prop="nameEnglish">
+                <el-input v-model="formData.data.nameEnglish" placeholder="请输入" onkeyup="value=value.replace(/[^\x00-\xff]/g, '')"></el-input>
+                <!-- @input="(val) => handleKeyup(val)" -->
+              </el-form-item>
+            </div>
+          </template>
+          <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" :on-preview="handlePreview"
+                         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>
+
+    <el-dialog v-if="productContractDialog" v-model="productContractDialog" :title="'外销合同'" width="80%" append-to-body>
+      <ProductContract :currentProductId="currentProductId"></ProductContract>
+    </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";
+import ProductContract from "@/components/contractCom/productContract.vue";
+import TitleInfo from "@/components/TitleInfo/index.vue";
+import { watch } from "vue";
+const { proxy } = getCurrentInstance();
+const props = defineProps({
+  alreadySelectData: Array,
+});
+const goodList = ref([]);
+onMounted(() => {
+  goodList.value = proxy.deepClone(props.alreadySelectData);
+});
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const actionUrl = import.meta.env.VITE_APP_BASE_API;
+const loading = ref(false);
+const submitLoading = ref(false);
+const treeListData = ref([]);
+const innerMethon = ref([]);
+const outsideMethon = ref([]);
+const productUnit = ref([]);
+const accountCurrency = ref([]);
+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({
+  productClassifyId: [
+    { required: true, message: "请选择产品分类", trigger: "change" },
+  ],
+  name: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
+  nameEnglish: [
+    { required: true, message: "请输入产品英文名", trigger: "blur" },
+  ],
+  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 selectConfig = reactive([
+  // {
+  //   label: "产品类型",
+  //   prop: "type",
+  //   data: [],
+  // },
+]);
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "图片",
+        slot: "pic",
+        align: "center",
+        width: 100,
+      },
+    },
+    {
+      attrs: {
+        label: "产品编码",
+        prop: "code",
+      },
+    },
+    {
+      attrs: {
+        label: "产品名称",
+        slot: "name",
+      },
+    },
+
+    {
+      attrs: {
+        label: "尺寸",
+        slot: "size",
+      },
+    },
+    {
+      attrs: {
+        label: "销售指导价",
+        slot: "price",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "成本价",
+        slot: "costPrice",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "80",
+        align: "center",
+        fixed: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "选择",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              clickSelect(row);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+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 formConfig = computed(() => {
+  return [
+    {
+      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: "slot",
+      slotName: "nameEnglish",
+      label: "",
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
+    {
+      type: "title",
+      title: "价格信息",
+    },
+    {
+      type: "selectInput",
+      prop: "price",
+      selectProp: "currency",
+      label: "销售指导价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+      data: accountCurrency.value,
+    },
+    {
+      type: "selectInput",
+      prop: "costPrice",
+      selectProp: "costCurrency",
+      label: "成本价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+      data: accountCurrency.value,
+    },
+    {
+      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,
+      filterable: true,
+      placeholder: "内包装方式",
+      style: {
+        width: "100%",
+      },
+      disabled: false,
+    },
+    {
+      type: "select",
+      prop: "outerPackMethod",
+      label: "外包装方式",
+      required: true,
+      itemWidth: 50,
+      multiple: true,
+      data: outsideMethon.value,
+      filterable: true,
+      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,
+    },
+  ];
+});
+
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/productInfo/getConditionProductList", 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: "",
+    costCurrency: "",
+  };
+  if (accountCurrency.value && accountCurrency.value.length > 0) {
+    formData.data.currency = accountCurrency.value[0].value;
+    formData.data.costCurrency = accountCurrency.value[0].value;
+  }
+  fileList.value = [];
+  fileListCopy.value = [];
+};
+
+const openExcel = () => {
+  openExcelDialog.value = true;
+};
+
+const needAtt = [
+  "productClassifyId",
+  "name",
+  "spec",
+  "remark",
+  "fileList",
+  "id",
+  "unit",
+  "definition",
+];
+const submitForm = () => {
+  byform.value.handleSubmit((valid) => {
+    // if (!fileListCopy.value.length > 0) {
+    //   return ElMessage({
+    //     message: "请上传产品图片",
+    //     type: "info",
+    //   });
+    // }
+    let jsonObj = {};
+    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.type = "1"; //1为公司产品库
+    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"; //产品
+    let jsonObj = JSON.parse(res.ehsdJson);
+    res = {
+      ...res,
+      currency: jsonObj.currency
+        ? jsonObj.currency
+        : accountCurrency.value[0].value,
+      costCurrency: jsonObj.costCurrency
+        ? jsonObj.costCurrency
+        : accountCurrency.value[0].value,
+      ...jsonObj,
+    };
+    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) => {
+  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",
+      "account_currency",
+    ])
+    .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,
+      }));
+      accountCurrency.value = res["account_currency"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+    });
+};
+getDict();
+getTreeList();
+getList();
+const clickSelect = (item) => {
+  item.selectType = "1";
+  goodList.value.push({
+    ...item,
+    productName: item.name,
+  });
+  proxy.$emit("selectProduct", item);
+};
+
+const handleKeypress = (event) => {
+  // 判断输入字符是否为中文字符
+  if (event.key.match(/[\u4e00-\u9fa5]/)) {
+    // 阻止输入
+    event.preventDefault();
+  }
+};
+const handleKeyup = (val) => {
+  // 过滤掉中文字符
+  formData.data.nameEnglish = formData.data.nameEnglish.replace(
+    /[\u4e00-\u9fa5]/g,
+    ""
+  );
+};
+
+const handlePreview = (file) => {
+  if (file && file.fileUrl) {
+    window.open(file.fileUrl, "_black");
+  }
+};
+const productContractDialog = ref(false);
+const currentProductId = ref("");
+const handleOpenProductContract = (row) => {
+  currentProductId.value = row.id;
+  productContractDialog.value = true;
+};
+</script>
+
+<style lang="scss" scoped>
+.user {
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  .tree {
+    width: 300px;
+  }
+  .content {
+    width: calc(100% - 320px - 170px);
+  }
+  .right {
+    padding-left: 10px;
+    width: 170px;
+    border-left: 1px solid #eee;
+  }
+}
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
+</style>

+ 809 - 0
src/components/product/SelectCustomerProduct.vue

@@ -0,0 +1,809 @@
+<template>
+  <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="[]" @get-list="getList">
+        <template #name="{ item }">
+          <div>
+            <span style="color: #409eff; cursor: pointer; word-break: break-all" @click="handleOpenProductContract(item)">{{ item.name }}</span>
+          </div>
+        </template>
+        <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>
+        <template #price="{ item }">
+          <div>
+            <span v-if="item.price">{{ item.currency }} {{ item.price }}</span>
+          </div>
+        </template>
+        <template #costPrice="{ item }">
+          <div>
+            <span v-if="item.costPrice">{{ item.costCurrency }} {{ item.costPrice }}</span>
+          </div>
+        </template>
+      </byTable>
+    </div>
+    <div class="right">
+      <div style="margin-bottom:30px">
+        <TitleInfo :content="'已选择商品'"></TitleInfo>
+      </div>
+      <el-tag style="margin-right: 10px; margin-bottom: 10px" type="info" v-for="(good, index) in goodList" :key="good.productId">
+        {{ good.productCnName || good.productName }}
+      </el-tag>
+    </div>
+    <el-dialog :title="modalType == 'add' ? '添加产品' : '编辑产品'" v-model="dialogVisible" width="700" 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 #customerId>
+            <div>
+              <el-select v-model="formData.data.customerId" filterable remote reserve-keyword placeholder="请输入关键字" remote-show-suffix
+                         :remote-method="remoteMethod" :loading="loadingSearch" @input="remoteMethod" v-if="modalType == 'add'">
+                <el-option v-for="item in customerData" :key="item.value" :label="item.label" :value="item.value" />
+              </el-select>
+              <el-select v-model="formData.data.customerName" disabled v-else>
+              </el-select>
+            </div>
+          </template>
+          <template #nameEnglish>
+            <div style="width: 100%">
+              <el-form-item label="英文名" prop="nameEnglish">
+                <el-input v-model="formData.data.nameEnglish" placeholder="请输入" onkeyup="value=value.replace(/[^\x00-\xff]/g, '')"></el-input>
+                <!-- @input="(val) => handleKeyup(val)" -->
+              </el-form-item>
+            </div>
+          </template>
+          <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" :on-preview="handlePreview"
+                         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>
+
+    <el-dialog v-if="productContractDialog" v-model="productContractDialog" :title="'外销合同'" width="80%" append-to-body>
+      <ProductContract :currentProductId="currentProductId"></ProductContract>
+    </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";
+import ProductContract from "@/components/contractCom/productContract.vue";
+import TitleInfo from "@/components/TitleInfo/index.vue";
+
+const props = defineProps({
+  buyCorporationId: String,
+  alreadySelectData: Array,
+});
+const goodList = ref([]);
+onMounted(() => {
+  goodList.value = proxy.deepClone(props.alreadySelectData);
+});
+const headers = ref({ Authorization: "Bearer " + getToken() });
+const actionUrl = import.meta.env.VITE_APP_BASE_API;
+const loading = ref(false);
+const innerMethon = ref([]);
+const outsideMethon = ref([]);
+const productUnit = ref([]);
+const accountCurrency = ref([]);
+const customerData = ref([]);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+    type: "",
+    productClassifyId: "",
+    keyword: "",
+    definition: "1",
+    customerId: "",
+  },
+});
+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" }],
+  nameEnglish: [
+    { required: true, message: "请输入产品英文名", trigger: "blur" },
+  ],
+  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: "客户名称",
+        prop: "customerName",
+        align: "left",
+        width: 180,
+      },
+    },
+    {
+      attrs: {
+        label: "图片",
+        slot: "pic",
+        align: "center",
+        width: 100,
+      },
+    },
+    {
+      attrs: {
+        label: "产品编码",
+        prop: "code",
+      },
+    },
+    {
+      attrs: {
+        label: "产品名称",
+        slot: "name",
+      },
+    },
+
+    {
+      attrs: {
+        label: "尺寸",
+        slot: "size",
+      },
+    },
+    {
+      attrs: {
+        label: "销售指导价",
+        slot: "price",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "成本价",
+        slot: "costPrice",
+        width: 150,
+      },
+    },
+    {
+      attrs: {
+        label: "操作",
+        width: "80",
+        align: "center",
+        fixed: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "选择",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              clickSelect(row);
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+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 formConfig = computed(() => {
+  return [
+    {
+      type: "title",
+      title: "客户信息",
+    },
+    // {
+    //   type: "select",
+    //   prop: modalType.value == "add" ? "customerId" : "customerName",
+    //   label: "客户名称",
+    //   required: true,
+    //   itemWidth: 100,
+    //   data: customerData.value,
+    //   style: {
+    //     width: "50%",
+    //   },
+    //   disabled: false,
+    // },
+    {
+      type: "slot",
+      slotName: "customerId",
+      label: "客户名称",
+    },
+    {
+      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: "slot",
+      slotName: "nameEnglish",
+      label: "",
+    },
+    {
+      type: "slot",
+      slotName: "productPic",
+      prop: "fileList",
+      label: "产品图片",
+    },
+    {
+      type: "title",
+      title: "价格信息",
+    },
+    {
+      type: "selectInput",
+      prop: "price",
+      selectProp: "currency",
+      label: "销售指导价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+      data: accountCurrency.value,
+    },
+    {
+      type: "selectInput",
+      prop: "costPrice",
+      selectProp: "costCurrency",
+      label: "成本价",
+      itemWidth: 50,
+      style: {
+        width: "100%",
+      },
+      data: accountCurrency.value,
+    },
+    {
+      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,
+    },
+  ];
+});
+
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  if (props.buyCorporationId) {
+    sourceList.value.pagination.customerId = props.buyCorporationId;
+  }
+  loading.value = true;
+  proxy
+    .post("/productInfo/getCustomerProductList", sourceList.value.pagination)
+    .then(
+      (message) => {
+        message.rows = message.rows.map((x) => ({
+          ...x,
+          fileList: [],
+          ...JSON.parse(x.ehsdJson),
+        }));
+        sourceList.value.data = message.rows;
+        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 = () => {
+  fileList.value = [];
+  dialogVisible.value = true;
+  modalType.value = "add";
+  formData.data = {
+    definition: "1",
+    outerPackMethod: [],
+    innerPackMethod: [],
+    fileList: [],
+    fileListCopy: [],
+    currency: "",
+    costCurrency: "",
+  };
+  if (accountCurrency.value && accountCurrency.value.length > 0) {
+    formData.data.currency = accountCurrency.value[0].value;
+    formData.data.costCurrency = accountCurrency.value[0].value;
+  }
+  fileList.value = [];
+  fileListCopy.value = [];
+};
+
+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) => {
+  fileList.value = [];
+  modalType.value = "edit";
+  proxy.post("/productInfo/detailByEhsd", { id: row.id }).then((res) => {
+    res.definition = "1"; //产品
+    let jsonObj = JSON.parse(res.ehsdJson);
+    res = {
+      ...res,
+      currency: jsonObj.currency
+        ? jsonObj.currency
+        : accountCurrency.value[0].value,
+      costCurrency: jsonObj.costCurrency
+        ? jsonObj.costCurrency
+        : accountCurrency.value[0].value,
+      ...jsonObj,
+    };
+    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) => {
+  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.post("/customer/selPage", { pageNum: 1, pageSize: 50 }).then((res) => {
+    customerData.value = res.rows.map((x) => ({
+      ...x,
+      label: x.name,
+      value: x.id,
+    }));
+  });
+  proxy
+    .getDictOne([
+      "inner_packaging_method_ehsd",
+      "outside_packaging_method_ehsd",
+      "unit",
+      "account_currency",
+    ])
+    .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,
+      }));
+      accountCurrency.value = res["account_currency"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+    });
+};
+getDict();
+getTreeList();
+getList();
+const clickSelect = (item) => {
+  item.selectType = "2";
+  goodList.value.push({
+    ...item,
+    productName: item.name,
+  });
+  proxy.$emit("selectProduct", item);
+};
+const loadingSearch = ref(false);
+const remoteMethod = (keyword) => {
+  if (keyword && typeof keyword === "string") {
+    loadingSearch.value = true;
+    proxy.post("/customer/selPage", { keyword }).then((res) => {
+      customerData.value = res.rows.map((x) => ({
+        ...x,
+        label: x.name,
+        value: x.id,
+      }));
+      loadingSearch.value = false;
+    });
+  }
+  return;
+};
+
+const handlePreview = (file) => {
+  if (file && file.fileUrl) {
+    window.open(file.fileUrl, "_black");
+  }
+};
+
+const productContractDialog = ref(false);
+const currentProductId = ref("");
+const handleOpenProductContract = (row) => {
+  currentProductId.value = row.id;
+  productContractDialog.value = true;
+};
+</script>
+
+<style lang="scss" scoped>
+.user {
+  padding: 20px;
+  display: flex;
+  justify-content: space-between;
+  .tree {
+    width: 300px;
+  }
+  .content {
+    width: calc(100% - 320px - 170px);
+  }
+  .right {
+    padding-left: 10px;
+    width: 170px;
+    border-left: 1px solid #eee;
+  }
+}
+.pic {
+  object-fit: contain;
+  width: 50px;
+  height: 50px;
+  cursor: pointer;
+  vertical-align: middle;
+}
+</style>

+ 24 - 125
src/views/connect/E-mail/mail/com/mailWrite.vue

@@ -1,13 +1,7 @@
 <template>
   <div v-loading="loading" style="padding: 10px">
     <div style="width: 100%">
-      <el-form
-        ref="submit"
-        :model="formData.data"
-        :rules="rules"
-        label-width="65px"
-        labelPosition="right"
-      >
+      <el-form ref="submit" :model="formData.data" :rules="rules" label-width="65px" labelPosition="right">
         <el-form-item>
           <el-button type="primary" @click="handleSend()"> 发 送 </el-button>
         </el-form-item>
@@ -24,11 +18,7 @@
                 @keyup.enter.native="handleAdd(10)"
               >
               </el-autocomplete> -->
-              <el-input
-                v-model="formData.data.to"
-                placeholder="请输入"
-                @keyup.enter.native="handleAdd(10)"
-              />
+              <el-input v-model="formData.data.to" placeholder="请输入" @keyup.enter.native="handleAdd(10)" />
               <!-- <el-button
                 type="primary"
                 @click="handleAdd(10)"
@@ -38,32 +28,16 @@
               > -->
             </div>
             <div style="margin-top: 15px" v-if="to && to.length > 0">
-              <el-tag
-                style="margin-right: 10px"
-                class="ml-2"
-                type="info"
-                v-for="(item, index) in to"
-                :key="index"
-                closable
-                @close="handleClose(index, 10)"
-                >{{ item.address }}</el-tag
-              >
+              <el-tag style="margin-right: 10px" class="ml-2" type="info" v-for="(item, index) in to" :key="index" closable
+                      @close="handleClose(index, 10)">{{ item.address }}</el-tag>
             </div>
           </div>
         </el-form-item>
         <el-form-item label="" prop="">
           <div style="display: flex">
-            <span
-              style="color: #666666; cursor: pointer"
-              @click="showcc = !showcc"
-              >抄送</span
-            >
+            <span style="color: #666666; cursor: pointer" @click="showcc = !showcc">抄送</span>
             <span style="color: ##dddddd; margin: 0 8px">|</span>
-            <span
-              style="color: #666666; cursor: pointer"
-              @click="showbcc = !showbcc"
-              >密送</span
-            >
+            <span style="color: #666666; cursor: pointer" @click="showbcc = !showbcc">密送</span>
           </div>
         </el-form-item>
         <el-form-item label="抄送人" prop="cc" v-if="showcc">
@@ -79,11 +53,7 @@
                 @keyup.enter.native="handleAdd(20)"
               >
               </el-autocomplete> -->
-              <el-input
-                v-model="formData.data.cc"
-                placeholder="请输入"
-                @keyup.enter.native="handleAdd(20)"
-              />
+              <el-input v-model="formData.data.cc" placeholder="请输入" @keyup.enter.native="handleAdd(20)" />
               <!-- <el-button
                 type="primary"
                 @click="handleAdd(20)"
@@ -93,16 +63,8 @@
               > -->
             </div>
             <div style="margin-top: 15px" v-if="cc && cc.length > 0">
-              <el-tag
-                style="margin-right: 10px"
-                class="ml-2"
-                type="info"
-                v-for="(item, index) in cc"
-                :key="index"
-                closable
-                @close="handleClose(index, 20)"
-                >{{ item.address }}</el-tag
-              >
+              <el-tag style="margin-right: 10px" class="ml-2" type="info" v-for="(item, index) in cc" :key="index" closable
+                      @close="handleClose(index, 20)">{{ item.address }}</el-tag>
             </div>
           </div>
         </el-form-item>
@@ -118,11 +80,7 @@
                 @keyup.enter.native="handleAdd(30)"
               >
               </el-autocomplete> -->
-              <el-input
-                v-model="formData.data.bcc"
-                placeholder="请输入"
-                @keyup.enter.native="handleAdd(30)"
-              />
+              <el-input v-model="formData.data.bcc" placeholder="请输入" @keyup.enter.native="handleAdd(30)" />
               <!-- <el-button
                 type="primary"
                 @click="handleAdd(30)"
@@ -132,16 +90,8 @@
               > -->
             </div>
             <div style="margin-top: 15px" v-if="bcc && bcc.length > 0">
-              <el-tag
-                style="margin-right: 10px"
-                class="ml-2"
-                type="info"
-                v-for="(item, index) in bcc"
-                :key="index"
-                closable
-                @close="handleClose(index, 30)"
-                >{{ item.address }}</el-tag
-              >
+              <el-tag style="margin-right: 10px" class="ml-2" type="info" v-for="(item, index) in bcc" :key="index" closable
+                      @close="handleClose(index, 30)">{{ item.address }}</el-tag>
             </div>
           </div>
         </el-form-item>
@@ -150,45 +100,21 @@
         </el-form-item>
         <el-form-item label="附件">
           <div style="width: 100%">
-            <el-upload
-              v-model:fileList="fileList"
-              class="upload-demo"
-              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
-              :data="uploadData"
-              drag
-              multiple
-              :show-file-list="false"
-              :before-upload="handleBeforeUpload"
-            >
+            <el-upload v-model:fileList="fileList" class="upload-demo" action="https://winfaster.obs.cn-south-1.myhuaweicloud.com" :data="uploadData"
+                       drag multiple :show-file-list="false" :before-upload="handleBeforeUpload">
               <div>
-                <img
-                  src="@/assets/images/icon_attachment.svg"
-                  class="att-img"
-                  fit="scale-down"
-                />
-                <span style="padding-left: 8px"
-                  >将文件拖到此处,或<span style="color: #409eff"
-                    >点击上传</span
-                  ></span
-                >
+                <img src="@/assets/images/icon_attachment.svg" class="att-img" fit="scale-down" />
+                <span style="padding-left: 8px">将文件拖到此处,或<span style="color: #409eff">点击上传</span></span>
               </div>
             </el-upload>
             <div class="att-box" v-if="fileListCopy && fileListCopy.length > 0">
               <div v-for="(itemFile, index) in fileListCopy" :key="index">
                 <div class="att-item">
-                  <img
-                    src="@/assets/images/icon_dz.svg"
-                    style="cursor: pointer"
-                    fit="scale-down"
-                  />
+                  <img src="@/assets/images/icon_dz.svg" style="cursor: pointer" fit="scale-down" />
                   <div class="att-name">
                     {{ itemFile.fileName }}
                   </div>
-                  <img
-                    src="@/assets/images/icon_delete.svg"
-                    class="att-img"
-                    fit="scale-down"
-                  />
+                  <img src="@/assets/images/icon_delete.svg" class="att-img" fit="scale-down" />
                 </div>
               </div>
             </div>
@@ -201,11 +127,7 @@
               @updateValue="updateContent"
               ref="contentEditor"
             /> -->
-            <TinymceEditor
-              :value="formData.data.content"
-              @updateValue="updateContent"
-              ref="contentEditor"
-            />
+            <TinymceEditor :value="formData.data.content" @updateValue="updateContent" ref="contentEditor" />
           </div>
         </el-form-item>
         <!-- <el-form-item label="发件人" prop="replyTo">
@@ -218,37 +140,15 @@
         <el-row>
           <el-col :span="6">
             <el-form-item label="发件人" prop="replyTo">
-              <el-select
-                v-model="formData.data.replyTo"
-                filterable
-                placeholder="请选择"
-                style="width: 100%"
-                @change="handleChangeReply"
-              >
-                <el-option
-                  v-for="item in userMailList"
-                  :key="item.mailUser"
-                  :label="item.mailUser"
-                  :value="item.mailUser"
-                />
+              <el-select v-model="formData.data.replyTo" filterable placeholder="请选择" style="width: 100%" @change="handleChangeReply">
+                <el-option v-for="item in userMailList" :key="item.mailUser" :label="item.mailUser" :value="item.mailUser" />
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="6">
             <el-form-item label="签  名:">
-              <el-select
-                v-model="formData.data.templateId"
-                filterable
-                placeholder="请选择"
-                style="width: 100%"
-                @change="handleChangeTemplate"
-              >
-                <el-option
-                  v-for="item in templateList"
-                  :key="item.id"
-                  :label="item.templateName"
-                  :value="item.id"
-                />
+              <el-select v-model="formData.data.templateId" filterable placeholder="请选择" style="width: 100%" @change="handleChangeTemplate">
+                <el-option v-for="item in templateList" :key="item.id" :label="item.templateName" :value="item.id" />
               </el-select>
             </el-form-item>
           </el-col>
@@ -267,10 +167,9 @@ import byForm from "@/components/byForm/index";
 import { ElMessage, ElMessageBox } from "element-plus";
 import Editor from "@/components/Editor/index.vue";
 import TinymceEditor from "@/components/Editor/TinymceEditor.vue";
-
 import { validEmail } from "@/utils/validate.js";
 import useMailStore from "@/store/modules/mail";
-import { computed, nextTick, watch } from "vue";
+import $bus from "@/bus/index.js";
 const mailStore = useMailStore();
 const { proxy } = getCurrentInstance();
 const userMailList = computed(() => mailStore.userMailList);