Explorar el Código

客户画像优化

cz hace 2 años
padre
commit
335fd83e43

+ 21 - 24
src/components/product/SelectMaterial.vue

@@ -112,6 +112,8 @@ import treeList from "@/components/product/treeList";
 import { computed, defineComponent, ref } from "vue";
 const loading = ref(false);
 const submitLoading = ref(false);
+const materialUnit = ref([]);
+const materialType = ref([]);
 const sourceList = ref({
   data: [],
   pagination: {
@@ -142,28 +144,7 @@ const selectConfig = computed(() => {
     {
       label: "物料类型",
       prop: "type",
-      data: [
-        {
-          label: "原料",
-          value: "1",
-        },
-        {
-          label: "辅料",
-          value: "2",
-        },
-        {
-          label: "配件",
-          value: "3",
-        },
-        {
-          label: "包材",
-          value: "4",
-        },
-        {
-          label: "其他",
-          value: "5",
-        },
-      ],
+      data: materialType.value,
     },
   ];
 });
@@ -175,7 +156,7 @@ const config = computed(() => {
         prop: "type",
       },
       render(type) {
-        return formConfig.value[1].data.find((x) => x.id == type).label;
+        return proxy.dictValueLabel(type, materialType.value);
       },
     },
     {
@@ -193,7 +174,6 @@ const config = computed(() => {
     {
       attrs: {
         label: "图片",
-        prop: "unit",
         slot: "pic",
       },
     },
@@ -202,6 +182,9 @@ const config = computed(() => {
         label: "单位",
         prop: "unit",
       },
+      render(unit) {
+        return proxy.dictValueLabel(unit, materialUnit.value);
+      },
     },
     {
       attrs: {
@@ -491,6 +474,20 @@ const handleClickFile = (file) => {
 const handleSelect = (row) => {
   proxy.$emit("handleSelect", toRaw(row));
 };
+
+const getDict = () => {
+  proxy.getDictOne(["material_unit", "material_type"]).then((res) => {
+    materialUnit.value = res["material_unit"].map((x) => ({
+      label: x.dictValue,
+      value: x.dictKey,
+    }));
+    materialType.value = res["material_type"].map((x) => ({
+      label: x.dictValue,
+      value: x.dictKey,
+    }));
+  });
+};
+getDict();
 </script>
   
 <style lang="scss" scoped>

+ 6 - 6
src/views/EHSD/productLibrary/companyProduct/index.vue

@@ -590,12 +590,12 @@ const needAtt = [
 let jsonObj = {};
 const submitForm = () => {
   byform.value.handleSubmit((valid) => {
-    if (!fileListCopy.value.length > 0) {
-      return ElMessage({
-        message: "请上传产品图片",
-        type: "info",
-      });
-    }
+    // if (!fileListCopy.value.length > 0) {
+    //   return ElMessage({
+    //     message: "请上传产品图片",
+    //     type: "info",
+    //   });
+    // }
     formData.data.fileList = fileListCopy.value.map((x) => ({
       id: x.id,
       fileName: x.fileName,

+ 180 - 102
src/views/EHSD/productLibrary/customerProduct/index.vue

@@ -7,7 +7,8 @@
         :data="treeListData"
         v-model="sourceList.pagination.productClassifyId"
         @change="treeChange"
-        @changeTreeList="getTreeList">
+        @changeTreeList="getTreeList"
+      >
       </treeList>
     </div>
     <div class="content">
@@ -38,16 +39,22 @@
                 disabled: false,
               },
         ]"
-        @get-list="getList">
+        @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])" />
+            <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.productLong }}cm</span>*
+            <span>{{ item.productWide }}cm</span>*
             <span>{{ item.productHigh }}cm</span>
           </div>
         </template>
@@ -63,9 +70,21 @@
         </template>
       </byTable>
     </div>
-    <el-dialog :title="modalType == 'add' ? '添加产品' : '编辑产品'" v-model="dialogVisible" width="600" v-loading="submitLoading" destroy-on-close>
+    <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">
+        <byForm
+          :formConfig="formConfig"
+          :formOption="formOption"
+          v-model="formData.data"
+          :rules="rules"
+          ref="byform"
+        >
           <template #productPic>
             <div>
               <el-upload
@@ -75,7 +94,8 @@
                 list-type="picture-card"
                 :on-remove="handleRemove"
                 :before-upload="handleBeforeUpload"
-                accept=".gif, .jpeg, .jpg, .png">
+                accept=".gif, .jpeg, .jpg, .png"
+              >
                 <el-icon><Plus /></el-icon>
               </el-upload>
             </div>
@@ -84,10 +104,21 @@
       </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>
+        <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-dialog
+      title="导入产品"
+      v-model="openExcelDialog"
+      width="400"
+      v-loading="excelLoading"
+    >
       <el-upload
         :action="actionUrl + '/productInfo/excelImportByEhsd'"
         :headers="headers"
@@ -95,11 +126,14 @@
         :on-progress="handleProgress"
         :show-file-list="false"
         :on-error="handleError"
-        accept=".xlsx">
+        accept=".xlsx"
+      >
         <el-button type="primary">点击导入</el-button>
       </el-upload>
       <template #footer>
-        <el-button @click="openExcelDialog = false" size="large">取 消</el-button>
+        <el-button @click="openExcelDialog = false" size="large"
+          >取 消</el-button
+        >
       </template>
     </el-dialog>
   </div>
@@ -138,15 +172,29 @@ 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" }],
+  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" }],
+  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 props = defineProps({
@@ -254,11 +302,15 @@ const config = computed(() => {
                 el: "button",
                 click() {
                   // 弹窗提示是否删除
-                  ElMessageBox.confirm("此操作将永久删除该数据, 是否继续?", "提示", {
-                    confirmButtonText: "确定",
-                    cancelButtonText: "取消",
-                    type: "warning",
-                  }).then(() => {
+                  ElMessageBox.confirm(
+                    "此操作将永久删除该数据, 是否继续?",
+                    "提示",
+                    {
+                      confirmButtonText: "确定",
+                      cancelButtonText: "取消",
+                      type: "warning",
+                    }
+                  ).then(() => {
                     // 删除
                     proxy
                       .post("/productInfo/delete", {
@@ -484,42 +536,44 @@ const getList = async (req) => {
     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),
-      }));
-      console.log(message.rows);
-      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];
+  proxy
+    .post("/productInfo/getCustomerProductList", sourceList.value.pagination)
+    .then(
+      (message) => {
+        message.rows = message.rows.map((x) => ({
+          ...x,
+          fileList: [],
+          ...JSON.parse(x.ehsdJson),
+        }));
+        console.log(message.rows);
+        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;
       }
-    },
-    (err) => {
-      loading.value = false;
-    }
-  );
+    );
 };
 
 const treeChange = (e) => {
@@ -549,16 +603,25 @@ const openExcel = () => {
 };
 
 const tree = ref(null);
-const needAtt = ["productClassifyId", "name", "spec", "remark", "fileList", "id", "unit", "definition"];
+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",
-      });
-    }
+    // if (!fileListCopy.value.length > 0) {
+    //   return ElMessage({
+    //     message: "请上传产品图片",
+    //     type: "info",
+    //   });
+    // }
     formData.data.fileList = fileListCopy.value.map((x) => ({
       id: x.id,
       fileName: x.fileName,
@@ -590,8 +653,10 @@ const submitForm = () => {
         for (const key in jsonObj) {
           formData.data[key] = jsonObj[key];
         }
-        formData.data.innerPackMethod = formData.data.innerPackMethod.split(",");
-        formData.data.outerPackMethod = formData.data.outerPackMethod.split(",");
+        formData.data.innerPackMethod =
+          formData.data.innerPackMethod.split(",");
+        formData.data.outerPackMethod =
+          formData.data.outerPackMethod.split(",");
         submitLoading.value = false;
       }
     );
@@ -599,9 +664,11 @@ const submitForm = () => {
 };
 
 const getTreeList = () => {
-  proxy.post("/productClassify/tree", { parentId: "", name: "", definition: "1" }).then((message) => {
-    treeListData.value = message;
-  });
+  proxy
+    .post("/productClassify/tree", { parentId: "", name: "", definition: "1" })
+    .then((message) => {
+      treeListData.value = message;
+    });
 };
 
 const getDtl = (row) => {
@@ -627,21 +694,23 @@ const getDtl = (row) => {
     }
     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 = [];
-      }
-    });
+    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"];
@@ -671,7 +740,9 @@ const handleBeforeUpload = async (file) => {
 };
 
 const handleRemove = (file) => {
-  const index = fileListCopy.value.findIndex((x) => x.uid === file.uid || x.id === file.id);
+  const index = fileListCopy.value.findIndex(
+    (x) => x.uid === file.uid || x.id === file.id
+  );
   fileListCopy.value.splice(index, 1);
 };
 
@@ -710,24 +781,31 @@ const handleSuccess = (res) => {
   }
 };
 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.dictKey,
-      value: x.dictValue,
-    }));
-  });
+  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.dictKey,
+        value: x.dictValue,
+      }));
+    });
   proxy.post("/customer/page", { pageNum: 1, pageSize: 9999 }).then(
     (res) => {
       customerData.value = res.rows.map((x) => ({

+ 9 - 2
src/views/connect/E-mail/mail/com/left.vue

@@ -37,7 +37,7 @@
         </el-tab-pane>
       </el-tabs>
       <div>
-        <el-button type="primary" style="width: 100%" @click="handleGoWrite"
+        <el-button type="primary" style="width: 100%" @click="handleGoWrite('')"
           >写信</el-button
         >
       </div>
@@ -138,12 +138,15 @@ const handleOpenMenu = (item) => {
   mailStore.currentId = menu.id;
 };
 
-const handleGoWrite = () => {
+const handleGoWrite = (mail = "") => {
   const menu = {
     title: "写信",
     type: selectMail.value.type,
     id: "write",
   };
+  if (mail) {
+    menu.reMail = mail;
+  }
   const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
   if (menuItem === undefined) {
     mailStore.mailMenuList.push(menu);
@@ -155,6 +158,10 @@ const handleGoWrite = () => {
 onMounted(() => {
   getMialList();
 });
+
+defineExpose({
+  handleGoWrite,
+});
 </script>
 
 <style lang="scss" scoped>

+ 9 - 2
src/views/connect/E-mail/mail/com/mailWrite.vue

@@ -180,7 +180,6 @@ import Editor from "@/components/Editor/index.vue";
 import { validEmail } from "@/utils/validate.js";
 import useMailStore from "@/store/modules/mail";
 const mailStore = useMailStore();
-console.log(mailStore, "wda");
 const { proxy } = getCurrentInstance();
 const loading = ref(false);
 let uploadData = ref({});
@@ -335,7 +334,6 @@ const handleAdd = (val) => {
         address: formData.data.to,
         personal: null,
       });
-      console.log(to.value, "ada");
       formData.data.to = "";
       break;
     }
@@ -384,6 +382,15 @@ const querySearchPerson = (queryString, callback) => {
     : distributionCenterData.value;
   callback(results);
 };
+
+onMounted(() => {
+  if (mailStore.currentMenu.reMail) {
+    to.value.push({
+      address: mailStore.currentMenu.reMail,
+      personal: null,
+    });
+  }
+});
 </script>
 
 <style lang="scss" scoped>

+ 8 - 1
src/views/connect/E-mail/mail/index.vue

@@ -2,7 +2,7 @@
   <div>
     <div class="box">
       <div class="left">
-        <mailLeft></mailLeft>
+        <mailLeft ref="leftDom"></mailLeft>
       </div>
       <div class="right">
         <mailMain></mailMain>
@@ -47,6 +47,13 @@ const handleClose = (icon) => {
   iconClose.value = iconArr;
   iconCon.value = iconConArr;
 };
+const route = useRoute();
+const leftDom = ref(null);
+onMounted(() => {
+  if (route.query.mail) {
+    leftDom.value.handleGoWrite(route.query.mail);
+  }
+});
 </script>
 
 <style lang="scss" scoped>

+ 408 - 24
src/views/customer/portrait/com/CustomerInfo.vue

@@ -9,11 +9,11 @@
       ><span>{{ detailsData.customerCode }}</span>
       <span class="title_" style="margin: 0 6px">|</span>
       <span class="title_"> 客户类型:</span
-      ><span>{{ dictDataEcho(detailsData.status, customerStatus) }}</span>
+      ><span>{{ dictValueLabel(detailsData.status, customerStatus) }}</span>
     </div>
     <div class="line">
       <span class="title_">客户来源:</span>
-      <span>{{ dictDataEcho(detailsData.source, customerSource) }}</span>
+      <span>{{ dictValueLabel(detailsData.source, customerSource) }}</span>
     </div>
     <div class="line">
       <span class="title_">地址:</span>
@@ -24,7 +24,16 @@
       </span>
     </div>
     <div class="contacts" v-if="detailsData.customerUserList.length > 0">
-      <div>相关联系人:</div>
+      <div style="display: flex; justify-content: space-between">
+        <span> 相关联系人: </span>
+        <el-button
+          type="primary"
+          icon="Plus"
+          circle
+          style="margin-top: -15px"
+          @click="handleAddUserList(detailsData)"
+        />
+      </div>
       <div>
         <div
           v-for="(item, index) in detailsData.customerUserList"
@@ -45,19 +54,217 @@
             </div>
             <div class="information">
               <div class="first">
-                <span class="val">Tel:</span>{{ item.phone }}
+                <span class="val">Tel:</span>{{ getPhone(item) }}
+              </div>
+              <div style="display: flex; justify-content: space-between">
+                <span class="val" style="flex: 1">Email:</span>{{ item.email }}
+                <img
+                  src="@/assets/images/portrait/icon_email.png"
+                  alt=""
+                  style="cursor: pointer; margin-left: 10px"
+                  @click="handleSendEmail(item)"
+                  v-show="item.email"
+                />
               </div>
-              <div><span class="val">Email:</span>{{ item.mailbox }}</div>
             </div>
           </div>
         </div>
+        <div
+          style="float: right"
+          v-if="
+            detailsData.customerUserList &&
+            detailsData.customerUserList.length > 1
+          "
+        >
+          <el-button type="primary" link size="small">更多联系人</el-button>
+        </div>
       </div>
     </div>
+
+    <el-dialog
+      :title="'添加联系人'"
+      v-if="dialogVisible"
+      v-model="dialogVisible"
+      width="800"
+      v-loading="loadingOperation"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="submit"
+      >
+        <template #person>
+          <div style="width: 100%">
+            <!-- <el-button type="primary" @click="clickAddPerson">添 加</el-button> -->
+            <el-table
+              :data="formData.data.customerUserList"
+              style="width: 100%; margin-top: 16px"
+            >
+              <el-table-column label="联系人" width="160">
+                <template #default="{ row, $index }">
+                  <div style="width: 100%">
+                    <el-form-item
+                      :prop="'customerUserList.' + $index + '.name'"
+                      :rules="rules.name2"
+                      :inline-message="true"
+                    >
+                      <el-input v-model="row.name" placeholder="请输入联系人" />
+                    </el-form-item>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="电子邮箱">
+                <template #default="{ row, $index }">
+                  <div style="width: 100%">
+                    <el-form-item
+                      :prop="'customerUserList.' + $index + '.email'"
+                      :rules="rules.email"
+                      :inline-message="true"
+                    >
+                      <el-input
+                        v-model="row.email"
+                        placeholder="请输入电子邮箱"
+                      />
+                    </el-form-item>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                label="操作"
+                width="120"
+                fixed="right"
+              >
+                <template #default="{ row, $index }">
+                  <el-button
+                    type="primary"
+                    link
+                    @click="clickInformationMore(row, $index)"
+                    >更多</el-button
+                  >
+                  <!-- <el-button type="primary" link @click="clickDelete($index)"
+                    >删除</el-button
+                  > -->
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm()"
+          size="large"
+          :loading="submitLoading"
+          >确 定</el-button
+        >
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      title="更多联系方式"
+      v-if="openPerson"
+      v-model="openPerson"
+      width="700"
+    >
+      <el-form
+        :label-position="'top'"
+        :model="formPerson.data"
+        :rules="rulesPerson"
+        ref="person"
+      >
+        <el-form-item label="联系人" prop="name">
+          <el-input v-model="formPerson.data.name" />
+        </el-form-item>
+        <el-form-item label="电子邮箱" prop="email">
+          <el-input v-model="formPerson.data.email" />
+        </el-form-item>
+        <el-form-item label="更多联系方式">
+          <div style="width: 100%">
+            <el-button type="primary" @click="clickAddMoreInformation"
+              >添 加</el-button
+            >
+            <el-table
+              :data="formPerson.data.contact"
+              style="width: 100%; margin-top: 16px"
+            >
+              <el-table-column label="类型" width="180">
+                <template #default="{ row, $index }">
+                  <div style="width: 100%">
+                    <el-form-item
+                      :prop="'contact.' + $index + '.type'"
+                      :rules="rulesPerson.type"
+                      :inline-message="true"
+                    >
+                      <el-select
+                        v-model="row.type"
+                        placeholder="请选择类型"
+                        style="width: 100%"
+                      >
+                        <el-option
+                          v-for="item in contactType"
+                          :key="item.value"
+                          :label="item.label"
+                          :value="item.value"
+                        />
+                      </el-select>
+                    </el-form-item>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="联系号码">
+                <template #default="{ row, $index }">
+                  <div style="width: 100%">
+                    <el-form-item
+                      :prop="'contact.' + $index + '.contactNo'"
+                      :rules="rulesPerson.contactNo"
+                      :inline-message="true"
+                    >
+                      <el-input
+                        v-model="row.contactNo"
+                        placeholder="请输入联系号码"
+                      />
+                    </el-form-item>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column
+                align="center"
+                label="操作"
+                width="120"
+                fixed="right"
+              >
+                <template #default="{ $index }">
+                  <el-button
+                    type="primary"
+                    link
+                    @click="clickInformationDelete($index)"
+                    >删除</el-button
+                  >
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="openPerson = false" size="large">取 消</el-button>
+        <el-button type="primary" @click="submitPerson()" size="large"
+          >确 定</el-button
+        >
+      </template>
+    </el-dialog>
   </div>
 </template>
   
 <script setup>
+import { ElMessage, ElMessageBox } from "element-plus";
 import useUserStore from "@/store/modules/user";
+import byForm from "@/components/byForm/index";
 const props = defineProps({
   customerId: {
     type: String,
@@ -65,9 +272,49 @@ const props = defineProps({
 });
 const { proxy } = getCurrentInstance();
 const loading = ref(false);
+const submitLoading = ref(false);
 let detailsData = ref({
   customerUserList: [],
 });
+let formData = reactive({
+  data: {},
+});
+let formPerson = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+let rules = ref({
+  name: [{ required: true, message: "请输入客户名称", trigger: "blur" }],
+  name2: [{ required: true, message: "请输入联系人", trigger: "blur" }],
+  email: [{ required: true, message: "请输入电子邮箱", trigger: "blur" }],
+  countryId: [{ required: true, message: "请选择国家", trigger: "change" }],
+  provinceId: [{ required: true, message: "请选择省/州", trigger: "change" }],
+  cityId: [{ required: true, message: "请选择城市", trigger: "change" }],
+  source: [{ required: true, message: "请选择客户来源", trigger: "change" }],
+  status: [{ required: true, message: "请选择类型", trigger: "change" }],
+});
+let rulesPerson = ref({
+  name: [{ required: true, message: "请输入联系人", trigger: "blur" }],
+  email: [{ required: true, message: "请输入电子邮箱", trigger: "blur" }],
+  type: [{ required: true, message: "请选择类型", trigger: "change" }],
+  contactNo: [{ required: true, message: "请输入联系号码", trigger: "blur" }],
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "slot",
+      slotName: "person",
+      label: "客户联系人",
+    },
+  ];
+});
+const dialogVisible = ref(false);
+const openPerson = ref(false);
 const getData = () => {
   loading.value = true;
   proxy.post("/customer/detail", { id: props.customerId }).then((res) => {
@@ -79,27 +326,32 @@ const getData = () => {
 };
 const customerSource = ref([]);
 const customerStatus = ref([]);
+const contactType = ref([]);
 const getDict = () => {
   proxy
-    .post("/dictTenantData/page", {
-      pageNum: 1,
-      pageSize: 999,
-      dictCode: "customer_source",
-      tenantId: useUserStore().user.tenantId,
-    })
+    .getDictOne([
+      "customer_tag",
+      "customer_source",
+      "customer_status",
+      "contact_type",
+    ])
     .then((res) => {
-      customerSource.value = res.rows;
-    });
-
-  proxy
-    .post("/dictTenantData/page", {
-      pageNum: 1,
-      pageSize: 999,
-      dictCode: "customer_status",
-      tenantId: useUserStore().user.tenantId,
-    })
-    .then((res) => {
-      customerStatus.value = res.rows;
+      // customerTag.value = res["customer_tag"].map((x) => ({
+      //   label: x.dictValue,
+      //   value: x.dictKey,
+      // }));
+      customerSource.value = res["customer_source"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+      customerStatus.value = res["customer_status"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
+      contactType.value = res["contact_type"].map((x) => ({
+        label: x.dictValue,
+        value: x.dictKey,
+      }));
     });
 };
 getDict();
@@ -108,6 +360,128 @@ onMounted(() => {
     getData();
   }
 });
+
+const handleSendEmail = (item) => {
+  proxy.$router.push({
+    path: "/connect/E-mail/mail",
+    query: {
+      mail: item.email,
+    },
+  });
+};
+const handleAddUserList = (res) => {
+  formData.data.customerUserList = [
+    {
+      name: "",
+      email: "",
+    },
+  ];
+  dialogVisible.value = true;
+};
+
+const clickAddPerson = () => {
+  if (
+    formData.data.customerUserList &&
+    formData.data.customerUserList.length > 0
+  ) {
+    formData.data.customerUserList.push({
+      name: "",
+      email: "",
+    });
+  } else {
+    formData.data.customerUserList = [
+      {
+        name: "",
+        email: "",
+      },
+    ];
+  }
+};
+const moreIndex = ref(-1);
+const clickInformationMore = (item, index) => {
+  moreIndex.value = index;
+  if (item.contactJson) {
+    item.contact = JSON.parse(item.contactJson);
+  } else {
+    item.contact = [];
+  }
+  formPerson.data = proxy.deepClone(item);
+  openPerson.value = true;
+};
+
+const clickDelete = (index) => {
+  formData.data.customerUserList.splice(index, 1);
+};
+
+const clickAddMoreInformation = () => {
+  if (formPerson.data.contact && formPerson.data.contact.length > 0) {
+    formPerson.data.contact.push({
+      type: "",
+      contactNo: "",
+    });
+  } else {
+    formPerson.data.contact = [
+      {
+        type: "",
+        contactNo: "",
+      },
+    ];
+  }
+};
+const clickInformationDelete = (index) => {
+  formPerson.data.contact.splice(index, 1);
+};
+const person = ref(null);
+const submit = ref(null);
+
+const submitPerson = () => {
+  person.value.validate((valid) => {
+    if (valid) {
+      formPerson.data.contactJson = JSON.stringify(formPerson.data.contact);
+      formData.data.customerUserList[moreIndex.value] = formPerson.data;
+      openPerson.value = false;
+    }
+  });
+};
+
+const submitForm = () => {
+  submit.value.handleSubmit(() => {
+    if (
+      formData.data.customerUserList &&
+      formData.data.customerUserList.length > 0
+    ) {
+      submitLoading.value = true;
+      const data = {
+        customerId: detailsData.value.id,
+        ...formData.data.customerUserList[0],
+      };
+      proxy.post("/customerUser/add", data).then(
+        () => {
+          ElMessage({
+            message: "添加成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          getData();
+        },
+        (err) => {
+          submitLoading.value = false;
+        }
+      );
+    } else {
+      ElMessage("请添加客户联系人");
+    }
+  });
+};
+
+const getPhone = (res) => {
+  if (res.contactJson) {
+    let contactJson = JSON.parse(res.contactJson);
+    if (contactJson && contactJson.length > 0) {
+      return contactJson[0].contactNo;
+    }
+  }
+};
 </script>
   
 <style lang="scss" scoped>
@@ -141,10 +515,14 @@ onMounted(() => {
       .details {
         margin-left: 10px;
         display: flex;
+        flex: 1;
         flex-direction: column;
         justify-content: space-between;
         .information {
           display: flex;
+          align-items: center;
+          flex-wrap: wrap;
+          // overflow: auto;
           .first {
             margin-right: 10px;
           }
@@ -165,4 +543,10 @@ onMounted(() => {
 .line {
   margin-bottom: 6px;
 }
-</style>
+.img {
+  // width: 15px;
+  // height: 15px;
+  // object-fit: contain;
+  cursor: pointer;
+}
+</style>

+ 4 - 2
src/views/customer/portrait/com/SaleStatistics.vue

@@ -5,7 +5,7 @@
         <div class="money">
           {{ moneyFormat(detailsData.amount, 2) }}
         </div>
-        <div>销售额({{ detailsData.currency }})</div>
+        <div>销售额({{ detailsData.currency || "¥" }})</div>
       </div>
       <div class="right_">
         <div class="icon">
@@ -82,7 +82,9 @@ const getData = () => {
   proxy
     .post("/saleQuotation/salesStatistics", { id: props.customerId })
     .then((res) => {
-      detailsData.value = res.contractVo;
+      if (res.contractVo) {
+        detailsData.value = res.contractVo;
+      }
       setTimeout(() => {
         loading.value = false;
       }, 200);

+ 12 - 17
src/views/product/material/index.vue

@@ -141,8 +141,10 @@ const sourceList = ref({
 });
 let dialogVisible = ref(false);
 let openExcelDialog = ref(false);
-
 let modalType = ref("add");
+const materialUnit = ref([]);
+const materialType = ref([]);
+const treeData = ref([]);
 let rules = ref({
   productClassifyId: [
     { required: true, message: "请选择物料分类", trigger: "change" },
@@ -167,7 +169,7 @@ const config = computed(() => {
         prop: "type",
       },
       render(type) {
-        return proxy.dictDataEcho(type, materialType.value);
+        return proxy.dictValueLabel(type, materialType.value);
       },
     },
     {
@@ -195,7 +197,7 @@ const config = computed(() => {
         prop: "unit",
       },
       render(unit) {
-        return proxy.dictDataEcho(unit, materialUnit.value);
+        return proxy.dictValueLabel(unit, materialUnit.value);
       },
     },
     {
@@ -287,14 +289,14 @@ const formConfig = computed(() => {
       type: "treeSelect",
       prop: "productClassifyId",
       label: "物料分类",
-      data: [],
+      data: treeData.value,
     },
     {
       type: "select",
       prop: "type",
       label: "物料类型",
       required: true,
-      data: [],
+      data: materialType.value,
     },
     {
       type: "input",
@@ -316,7 +318,7 @@ const formConfig = computed(() => {
       prop: "unit",
       label: "单位",
       required: true,
-      data: [],
+      data: materialUnit.value,
     },
     {
       type: "slot",
@@ -445,7 +447,7 @@ const getTreeList = () => {
     .post("/productClassify/tree", { parentId: "", name: "", definition: "2" })
     .then((message) => {
       treeListData.value = message;
-      formConfig.value[0].data = message;
+      treeData.value = message;
     });
 };
 
@@ -492,24 +494,17 @@ const handleClickFile = (file) => {
   window.open(file.fileUrl, "_blank");
 };
 
-const materialUnit = ref([]);
-const materialType = ref([]);
 const getDict = () => {
   proxy.getDictOne(["material_unit", "material_type"]).then((res) => {
-    materialUnit.value = res["material_unit"];
-    materialType.value = res["material_type"];
-    formConfig.value[4].data = materialUnit.value.map((x) => ({
-      label: x.dictValue,
-      value: x.dictKey,
-    }));
-    formConfig.value[1].data = materialType.value.map((x) => ({
+    materialUnit.value = res["material_unit"].map((x) => ({
       label: x.dictValue,
       value: x.dictKey,
     }));
-    selectConfig[0].data = materialType.value.map((x) => ({
+    materialType.value = res["material_type"].map((x) => ({
       label: x.dictValue,
       value: x.dictKey,
     }));
+    selectConfig[0].data = materialType.value;
   });
 };
 getDict();

+ 15 - 8
src/views/purchaseSales/outAndInWarehouse/record/index.vue

@@ -14,7 +14,8 @@
             action: () => deriveExcel(),
           },
         ]"
-        @get-list="getList">
+        @get-list="getList"
+      >
         <template #warehouseName="{ item }">
           <div>
             <span v-if="item.opType == 1">{{ item.toWarehouseName }}</span>
@@ -103,6 +104,10 @@ const typeList = ref([
     label: "京东退货入库",
     value: "15",
   },
+  {
+    label: "生产任务出库",
+    value: "20",
+  },
 ]);
 const warehouseList = ref([]);
 const sourceList = ref({
@@ -226,13 +231,15 @@ const getDict = () => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/stockJournalDetails/page", sourceList.value.pagination).then((res) => {
-    sourceList.value.data = res.rows;
-    sourceList.value.pagination.total = res.total;
-    setTimeout(() => {
-      loading.value = false;
-    }, 200);
-  });
+  proxy
+    .post("/stockJournalDetails/page", sourceList.value.pagination)
+    .then((res) => {
+      sourceList.value.data = res.rows;
+      sourceList.value.pagination.total = res.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
 };
 getDict();
 

+ 36 - 12
src/views/purchaseSales/outAndInWarehouse/waitingForDelivery/index.vue

@@ -8,15 +8,31 @@
         :loading="loading"
         :selectConfig="selectConfig"
         highlight-current-row
-        @get-list="getList">
+        @get-list="getList"
+      >
       </byTable>
     </div>
 
-    <el-dialog title="出库" v-if="dialogVisible" v-model="dialogVisible" width="400" v-loading="loadingDialog">
-      <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit"> </byForm>
+    <el-dialog
+      title="出库"
+      v-if="dialogVisible"
+      v-model="dialogVisible"
+      width="400"
+      v-loading="loadingDialog"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="submit"
+      >
+      </byForm>
       <template #footer>
         <el-button @click="dialogVisible = false" size="large">取 消</el-button>
-        <el-button type="primary" @click="submitForm()" size="large">确 定</el-button>
+        <el-button type="primary" @click="submitForm()" size="large"
+          >确 定</el-button
+        >
       </template>
     </el-dialog>
   </div>
@@ -70,6 +86,10 @@ const businessType = ref([
     label: "销售订单出库",
     value: 6,
   },
+  {
+    label: "生产任务出库",
+    value: 7,
+  },
 ]);
 const sourceList = ref({
   data: [],
@@ -213,13 +233,15 @@ const getDict = () => {
 const getList = async (req) => {
   sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
   loading.value = true;
-  proxy.post("/stockWaitDetails/page", sourceList.value.pagination).then((res) => {
-    sourceList.value.data = res.rows;
-    sourceList.value.pagination.total = res.total;
-    setTimeout(() => {
-      loading.value = false;
-    }, 200);
-  });
+  proxy
+    .post("/stockWaitDetails/page", sourceList.value.pagination)
+    .then((res) => {
+      sourceList.value.data = res.rows;
+      sourceList.value.pagination.total = res.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
 };
 getDict();
 getList();
@@ -290,7 +312,9 @@ const formConfig = computed(() => {
 });
 const rules = ref({
   warehouseId: [{ required: true, message: "请选择仓库", trigger: "change" }],
-  warehousingQuantity: [{ required: true, message: "请输入出库数量", trigger: "blur" }],
+  warehousingQuantity: [
+    { required: true, message: "请输入出库数量", trigger: "blur" },
+  ],
 });
 const submitForm = () => {
   submit.value.handleSubmit(() => {