zhujun il y a 2 ans
Parent
commit
0defcaebf2

+ 1 - 1
.gitignore

@@ -21,5 +21,5 @@ yarn-error.log*
 *.njsproj
 *.sln
 *.sw?
-
+vue.config.js
 package-lock.json

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "echarts": "^5.3.3",
     "echarts-wordcloud": "^2.0.0",
     "ele-admin": "^1.11.2",
+    "element-china-category-data": "^1.0.4",
     "element-ui": "2.15.7",
     "file-loader": "^6.2.0",
     "github-markdown-css": "^5.1.0",

+ 68 - 0
src/api/saleManage/contact.js

@@ -0,0 +1,68 @@
+import request from '@/utils/request';
+
+/**
+ * 获取客户信息列表
+ */
+export async function contactPage(params) {
+  const res = await request.get(`/eom/contact/page`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 获取客户信息详情
+ */
+export async function contactDetail(id) {
+  const res = await request.get(`/eom/contact/getById/${id}`, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 更新客户信息
+ */
+export async function contactUpdate(data) {
+  const res = await request.put(`/eom/contact/update`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 新增客户信息
+ */
+export async function contactSave(data) {
+  const res = await request.post(`/eom/contact/save`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 删除事项
+ */
+export async function contactDelete(data) {
+  const res = await request.post('/eom/contact/delete', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+
+/**
+ * 获取客户分类树
+ */
+export async function contactTypeTree(data) {
+  const res = await request.get(`/main/categoryLevel/getTreeByPid/${data.type}`);
+  if (res.data.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 50 - 33
src/api/system/file/index.js

@@ -1,68 +1,85 @@
 import request from '@/utils/request';
+import { download } from '@/utils/file';
 
 /**
  * 上传文件
  * @param file 文件
  */
-export async function uploadFile(file) {
+export async function uploadFile (data) {
   const formData = new FormData();
-  formData.append('file', file);
-  const res = await request.post('/file/upload', formData);
-  if (res.data.code === 0) {
-    return res.data.data;
+  formData.append('multiPartFile', data.multiPartFile);
+  formData.append('module', data.module);
+  const res = await request.post('/main/file/upload', formData);
+  if (res.data.code === '0') {
+    return res.data;
   }
   return Promise.reject(new Error(res.data.message));
 }
-
 /**
- * 上传 base64 文件
- * @param base64 文件数据
- * @param fileName 文件名称
+ * 上传文件 批量
+ * @param file 文件
  */
-export async function uploadBase64File(base64, fileName) {
+export async function uploadBatch (data) {
   const formData = new FormData();
-  formData.append('base64', base64);
-  if (fileName) {
-    formData.append('fileName', fileName);
-  }
-  const res = await request.post('/file/upload/base64', formData);
-  if (res.data.code === 0 && res.data.data) {
-    return res.data.data;
+  data.multiPartFiles.forEach((item, index) => {
+    formData.append(`multiPartFiles`, item);
+  });
+  const res = await request.post(
+    `/main/file/uploadBatch?module=${data.module}`,
+    formData
+  );
+  if (res.data.code === '0') {
+    return res.data;
   }
   return Promise.reject(new Error(res.data.message));
 }
 
 /**
- * 分页查询文件上传记录
+ * 获取文件路径
  */
-export async function pageFiles(params) {
-  const res = await request.get('/file/page', { params });
-  if (res.data.code === 0) {
+export async function getPathAddress () {
+  const res = await request.post('/main/file/getPathAddress');
+  if (res.data.code === '0') {
     return res.data.data;
   }
-  return Promise.reject(new Error(res.data.message));
+  return Promise.reject();
+}
+/**
+ * 获取文件
+ */
+export async function getFile (params, fileName) {
+  const res = await request.get('/main/file/getFile', {
+    params,
+    responseType: 'blob'
+  });
+  const arr = params.objectName.split('/');
+  download(res.data, fileName || arr[arr.length - 1]);
+  // if (res.data.code === '0') {
+  //   return res.data.data;
+  // }
+  // return Promise.reject();
 }
 
 /**
  * 删除文件
  */
-export async function removeFile(id) {
-  const res = await request.delete('/file/remove/' + id);
-  if (res.data.code === 0) {
+export async function removeFile (data) {
+  const res = await request.post(
+    `/main/file/delete?fileId=${data.fileId}`,
+    data
+  );
+  if (res.data.code === '0') {
     return res.data.message;
   }
   return Promise.reject(new Error(res.data.message));
 }
-
 /**
- * 批量删除文件
+ * 文件列表
  */
-export async function removeFiles(data) {
-  const res = await request.delete('/file/remove/batch', {
-    data
-  });
-  if (res.data.code === 0) {
-    return res.data.message;
+export async function getFileList (data) {
+  const res = await request.post(`/main/file/list`, data);
+  if (res.data.code === '0') {
+    return res.data.data;
   }
   return Promise.reject(new Error(res.data.message));
 }

+ 15 - 5
src/components/CommomSelect/dept-select.vue

@@ -7,8 +7,10 @@
     label-key="name"
     value-key="id"
     default-expand-all
+    ref="treeSelect"
     :placeholder="placeholder"
     @input="updateValue"
+    @change="changeChoose"
   />
 </template>
 
@@ -24,17 +26,17 @@
         default: '请选择'
       }
     },
-    data() {
+    data () {
       return {
         treeData: []
       };
     },
-    created() {
+    created () {
       this.getData();
     },
     methods: {
-      async getData(parmas = {}) {
-        const data = await listOrganizations(parmas);
+      async getData (params = {}) {
+        const data = await listOrganizations(params);
         this.treeData = this.$util.toTreeData({
           data: data || [],
           idField: 'id',
@@ -42,8 +44,16 @@
         });
       },
       /* 更新选中数据 */
-      updateValue(value) {
+      updateValue (value) {
         this.$emit('input', value);
+      },
+
+      changeChoose (val) {
+        this.$emit(
+          'changeGroup',
+          val,
+          this.$refs.treeSelect.getNodeByValue(val)
+        );
       }
     }
   };

+ 73 - 0
src/components/CommomSelect/person-select.vue

@@ -0,0 +1,73 @@
+<template>
+  <el-select
+    v-model="selectVal"
+    filterable
+    style="width: 100%"
+    v-bind="$attrs"
+    v-on="$listeners"
+    clearable
+  >
+    <el-option
+      v-for="item in dictList"
+      :key="item.id"
+      :value="item.id"
+      :label="item.name"
+    ></el-option>
+  </el-select>
+</template>
+
+<script>
+  import { getUserPage } from '@/api/system/organization';
+  export default {
+    model: {
+      prop: 'value',
+      event: 'updateVal'
+    },
+    props: {
+      value: {
+        type: [String, Number, Array],
+        default: ''
+      },
+      init: {
+        type: Boolean,
+        default: true
+      }
+    },
+    data () {
+      return {
+        dictList: []
+      };
+    },
+    computed: {
+      selectVal: {
+        set (val) {
+          this.$emit(
+            'selfChange',
+            val,
+            this.dictList.find((i) => i.id === val)
+          );
+          this.$emit('updateVal', val);
+        },
+        get () {
+          return this.value;
+        }
+      }
+    },
+    created () {
+      if (this.init) {
+        this.getList();
+      }
+    },
+    methods: {
+      async getList (params) {
+        let data = { pageNum: 1, size: -1 };
+        // 如果传了参数就是获取巡点检人员数据
+        if (params) {
+          data = Object.assign(data, params);
+        }
+        const res = await getUserPage(data);
+        this.dictList = res.list;
+      }
+    }
+  };
+</script>

+ 115 - 0
src/components/upload/WithView.vue

@@ -0,0 +1,115 @@
+<template>
+  <div>
+    <div class="img-view" v-if="dialogImageUrl">
+      <img :src="dialogImageUrl" alt="" srcset="" />
+    </div>
+    <div class="placeholder-box" v-else>
+      <img src="~@/assets/upload-placeholder.svg" alt="" />
+    </div>
+    <div class="btn-box">
+      <el-upload
+        class="avatar-div"
+        action="#"
+        accept="image/png,image/jpeg"
+        :show-file-list="false"
+        ref="uploadRef"
+        :on-exceed="handleExceed"
+        :limit="1"
+        :http-request="handlSuccess"
+        :multiple="false"
+      >
+        <el-button type="text">上传{{ assetName }}图片</el-button>
+      </el-upload>
+      <el-button type="text" @click="clearImg">清除图片</el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+  import { uploadFile, removeFile } from '@/api/system/file/index.js';
+  import { getImageUrl } from '@/utils/file';
+  export default {
+    props: {
+      assetName: {
+        type: String,
+        default: '设备'
+      },
+      value: {
+        type: Object,
+        default: () => []
+      },
+      // 所属模块
+      module: {
+        type: String,
+        default: 'main'
+      }
+    },
+    data () {
+      return {};
+    },
+    computed: {
+      dialogImageUrl () {
+        return this.value?.storePath && getImageUrl(this.value.storePath);
+      }
+    },
+    methods: {
+      // 清空已上传的文件列表
+      clearUploadFiles () {},
+      //图片添加
+      async handlSuccess (params) {
+        let res = await uploadFile({
+          multiPartFile: params.file,
+          module: this.module
+        });
+        if (res?.data) {
+          this.$emit('input', res.data);
+        }
+      },
+      async clearImg () {
+        await removeFile({ fileId: this.value.id });
+        this.$emit('input', {});
+        this.$refs.uploadRef.clearFiles();
+      },
+      // 限制上传的数量
+      handleExceed (files, fileList) {
+        this.$message.warning(`最多允许上传一张图片!`);
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .img-view {
+    width: 280px;
+    height: 342px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-width: 1px;
+    border-style: solid;
+    border-color: rgba(215, 215, 215, 1);
+    img {
+      max-width: 100%;
+    }
+  }
+  .placeholder-box {
+    width: 280px;
+    height: 342px;
+    text-align: center;
+
+    background-color: rgba(242, 242, 242, 1);
+    box-sizing: border-box;
+    border-width: 1px;
+    border-style: solid;
+    border-color: rgba(215, 215, 215, 1);
+    padding-top: 60px;
+    img {
+      width: 158px;
+      height: 158px;
+    }
+  }
+
+  .btn-box {
+    display: flex;
+    justify-content: space-around;
+  }
+</style>

+ 270 - 0
src/components/upload/fileUpload.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="upload-file">
+    <el-upload
+      class="upload-demo"
+      action="#"
+      :http-request="handlRequest"
+      :before-remove="beforeRemove"
+      :on-remove="handleRemove"
+      multiple
+      :before-upload="beforeUpload"
+      :file-list="fileList"
+      :show-file-list="!showLib"
+    >
+      <slot>
+        <el-button type="primary" icon="el-icon-plus">点击上传</el-button>
+      </slot>
+    </el-upload>
+    <el-button type="primary" class="lib" @click="handleOpenLib" v-if="showLib"
+      >文档库</el-button
+    >
+    <div class="imgs-box" v-if="fileList.length && showLib">
+      <p class="imgs-p">
+        <span> {{ fileList[0].name }}</span>
+        <el-link @click="delFileList" type="primary" class="link">删除</el-link>
+      </p>
+    </div>
+    <!--图文档弹窗 -->
+    <el-dialog
+      title="图文档"
+      append-to-body
+      :visible.sync="documentVisible"
+      width="72%"
+    >
+      <el-form label-width="100px">
+        <el-row :gutter="12">
+          <el-col :span="8">
+            <el-form-item label-width="70px" label="文档名称">
+              <el-input v-model="documentForm.name"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-button type="primary" @click="reload">搜索</el-button>
+          </el-col>
+        </el-row>
+      </el-form>
+      <ele-pro-table
+        ref="table"
+        :current.sync="selectItem"
+        :columns="columns"
+        highlight-current-row
+        :datasource="datasource"
+        :initLoad="false"
+        height="500px"
+        class="dict-table"
+        tool-class="ele-toolbar-actions"
+      >
+      </ele-pro-table>
+      <div slot="footer" class="dialog-footer">
+        <el-button size="small" @click="documentVisible = false"
+          >关 闭</el-button
+        >
+        <el-button size="small" @click="submitDocument" type="primary"
+          >确 认</el-button
+        >
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import {
+    uploadFile,
+    removeFile,
+    getFileList
+  } from '@/api/system/file/index.js';
+  import { getImageUrl, getImagePath } from '@/utils/file';
+  export default {
+    props: {
+      value: {
+        type: Array,
+        default: () => []
+      },
+      // 所属模块
+      module: {
+        type: String,
+        required: true
+      },
+      // 文档库
+      showLib: {
+        type: Boolean,
+        default: false
+      },
+      // 限制数量
+      limit: {
+        type: Number,
+        default: -1
+      },
+      // 限制大小 mb
+      size: {
+        type: Number,
+        default: 10
+      }
+    },
+    data () {
+      return {
+        documentVisible: false,
+        selectItem: null,
+        documentForm: {
+          name: ''
+        },
+        columns: [
+          {
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center'
+          },
+          {
+            label: '文档名称',
+            prop: 'name',
+            minWidth: '180',
+            showOverflowTooltip: true
+          },
+          {
+            label: '文档类型',
+            prop: 'type'
+          },
+          {
+            label: '系统'
+          },
+          {
+            label: '储存路径',
+            prop: 'storePath',
+            minWidth: '180',
+            showOverflowTooltip: true
+          },
+          {
+            label: '模块名',
+            prop: 'module'
+          },
+          {
+            label: '上传时间',
+            prop: 'createTime'
+          }
+        ]
+      };
+    },
+    computed: {
+      fileList: {
+        set (val) {
+          // console.log(val);
+          this.$emit(
+            'input',
+            val.map((item) => ({
+              ...item,
+              url: getImagePath(item.url)
+            }))
+          );
+        },
+        get () {
+          // console.log(this.value, 2);
+          const arr =
+            (this.value &&
+              this.value.map((item) => ({
+                ...item,
+                url: getImageUrl(item.url)
+              }))) ||
+            [];
+          return arr;
+        }
+      }
+    },
+
+    methods: {
+      delFileList () {
+        this.$emit('input', []);
+      },
+      handleOpenLib () {
+        this.documentVisible = true;
+        this.$nextTick(() => {
+          this.reload();
+        });
+      },
+      //图文档勾选
+      submitDocument () {
+        this.$emit('input', [
+          { url: this.selectItem.storePath, ...this.selectItem }
+        ]);
+        this.documentVisible = false;
+      },
+      datasource ({ page, limit }) {
+        return getFileList({
+          ...this.documentForm,
+          pageNum: page,
+          size: limit
+        });
+      },
+      reload () {
+        this.$refs.table.reload();
+      },
+      beforeRemove (file) {
+        if (file.id) {
+          return removeFile({
+            fileId: file.id
+          }).then(() => {
+            return true;
+          });
+        } else {
+          return true;
+        }
+      },
+      handleRemove (file, fileList) {
+        this.fileList = fileList;
+      },
+      beforeUpload (file) {
+        if (file.size / 1024 / 1024 > this.size) {
+          this.$message.error(`大小不能超过 ${this.size}MB`);
+          return false;
+        }
+
+        if (this.limit > 0 && this.fileList.length === this.limit) {
+          this.$message.error(`最多上传 ${this.limit}个文件`);
+          return false;
+        }
+        return uploadFile({
+          module: this.module,
+          multiPartFile: file
+        }).then((res) => {
+          if (res.data) {
+            this.$emit('input', [
+              ...(this.value || []),
+              { ...file, url: res.data.storePath, ...res.data }
+            ]);
+          }
+          return res.data;
+        });
+      },
+      handlRequest () {
+        return Promise.resolve();
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .upload-file {
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+
+    .lib {
+      margin-left: 12px;
+    }
+
+    .imgs-box {
+      margin-left: 10px;
+      flex: 1;
+    }
+    .imgs-box .imgs-p {
+      height: 30px;
+      background: #f0f3f3;
+      line-height: 30px;
+      min-width: 480px;
+      margin-bottom: 5px;
+      padding: 0 10px;
+      display: flex;
+      justify-content: space-between;
+    }
+  }
+</style>

+ 106 - 0
src/components/upload/imgUpload.vue

@@ -0,0 +1,106 @@
+<template>
+  <ele-image-upload
+    v-model="images"
+    :limit="limit"
+    :drag="true"
+    :multiple="true"
+    :upload-handler="uploadHandler"
+    :beforeUpload="beforeUpload"
+    @upload="onUpload"
+  >
+  </ele-image-upload>
+</template>
+
+<script>
+  import EleImageUpload from 'ele-admin/es/ele-image-upload';
+  import { getImageUrl, getImagePath } from '@/utils/file';
+  import { uploadFile } from '@/api/system/file/index';
+
+  export default {
+    components: { EleImageUpload },
+    props: {
+      // 所属模块
+      module: {
+        type: String,
+        default: 'main'
+      },
+      // 限制数量
+      limit: {
+        type: Number,
+        default: -1
+      },
+      value: {
+        type: Array,
+        default: () => []
+      }
+    },
+    data () {
+      return {};
+    },
+    computed: {
+      images: {
+        set (val) {
+          this.$emit(
+            'input',
+            val.map((item) => ({
+              ...item,
+              url: getImagePath(item.url)
+            }))
+          );
+        },
+        get () {
+          const arr =
+            (this.value &&
+              this.value.map((item) => ({
+                ...item,
+                url: getImageUrl(item.url)
+              }))) ||
+            [];
+
+          return arr;
+        }
+      }
+    },
+    methods: {
+      // beforeUpload(){},
+      /* 上传事件 */
+      uploadHandler (file) {
+        const item = {
+          file,
+          uid: file.uid,
+          name: file.name,
+          progress: 0,
+          status: null
+        };
+        if (!file.type.startsWith('image')) {
+          this.$message.error('只能选择图片');
+          return;
+        }
+        if (file.size / 1024 / 1024 > 2) {
+          this.$message.error('大小不能超过 2MB');
+          return;
+        }
+        this.$emit('input', [...this.value, item]);
+        this.onUpload(item);
+      },
+      /* 上传 item */
+      async onUpload (item) {
+        // 模拟上传
+        item.status = 'uploading';
+        item.progress = 20;
+
+        const res = await uploadFile({
+          module: this.module,
+          multiPartFile: item.file
+        });
+        if (res.data) {
+          item.url = res.data.storePath;
+          item.id = res.data.id;
+
+          item.progress === 100;
+          item.status = 'done';
+        }
+      }
+    }
+  };
+</script>

+ 5 - 1
src/enum/dict.js

@@ -16,7 +16,11 @@ export default {
   紧急程度: 'urgent_type',
   订单计划类型: 'plan_type',
   交付要求: 'require_ments',
-  交货状态: 'delivery_status'
+  交货状态: 'delivery_status',
+  客户状态: 'contact_status',
+  企业类型: 'company_category',
+  结算方式: 'settlement_mode',
+  客户联系人状态: 'contact_link_status'
 };
 
 export const numberList = [

+ 35 - 0
src/utils/file.js

@@ -0,0 +1,35 @@
+import { getToken } from './token-util';
+import { TOKEN_HEADER_NAME } from '@/config/setting';
+
+// 获取图片反显url
+export function getImageUrl (path) {
+  return `${sessionStorage.filePath}${path}`;
+  // if (process.env.NODE_ENV === 'development') {
+  //   return `http://192.168.3.51:18086/main/file/getFile?${TOKEN_HEADER_NAME}=${getToken()}&objectName=${path}`;
+  // } else {
+  //   return `${
+  //     sessionStorage.filePath
+  //   }${path}`;
+  // }
+}
+// 从反显url上获取接口需要path
+export function getImagePath (url) {
+  if (!url) {
+    return '';
+  }
+
+  const match = url.match(/&objectName=([\S\s]*)/);
+
+  return (match && match[1].split('&')[0]) || '';
+}
+
+// 下载方法
+export function download (data, name) {
+  const a = document.createElement('a');
+  const url = window.URL.createObjectURL(data);
+  const filename = name;
+  a.href = url;
+  a.download = filename;
+  a.click();
+  window.URL.revokeObjectURL(url);
+}

+ 753 - 0
src/views/saleManage/contact/components/addContactDialog.vue

@@ -0,0 +1,753 @@
+<template>
+  <ele-modal
+    custom-class="ele-dialog-form"
+    :visible.sync="visible"
+    v-if="visible"
+    :title="title"
+    width="80%"
+    @close="cancel"
+  >
+    <el-tabs v-model="activeName" type="card">
+      <el-tab-pane label="基本信息" name="base">
+        <el-form
+          label-width="100px"
+          ref="form"
+          :model="form"
+          :rules="rules"
+          style="margin-top: 30px"
+        >
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="客户名称" prop="name">
+                <el-input placeholder="请输入" v-model="form.name"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="客户代码" prop="serialNo">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.serialNo"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="客户简称" prop="simpleName">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.simpleName"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="授信额度" prop="authorizationLimit">
+                <el-input
+                  placeholder="请输入"
+                  v-model.number="form.authorizationLimit"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="地址" prop="addressId">
+                <el-cascader
+                  clearable
+                  v-model="form.addressId"
+                  :options="options.cityDataLabel"
+                  ref="address"
+                  style="width: 100%"
+                ></el-cascader>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="详细地址" prop="address">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.address"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item
+                label="是否存在上级集团公司"
+                prop="hasParentGroup"
+                label-width="155px"
+              >
+                <el-radio v-model="form.hasParentGroup" :label="1">是</el-radio>
+                <el-radio v-model="form.hasParentGroup" :label="0">否</el-radio>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="经营范围" prop="businessScope">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.businessScope"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="所属行业" prop="industry">
+                <el-cascader
+                  :options="options.categoryData"
+                  v-model="form.industry"
+                  style="width: 100%"
+                  ref="industry"
+                >
+                </el-cascader>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="主营产品" prop="mainProduct">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.mainProduct"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="企业类型" prop="companyCategoryId">
+                <DictSelection
+                  dictName="企业类型"
+                  clearable
+                  v-model="form.companyCategoryId"
+                >
+                </DictSelection>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="单位电话" prop="phone">
+                <el-input placeholder="请输入" v-model="form.phone"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="登记日期" prop="registerDate">
+                <el-date-picker
+                  style="width: 100%"
+                  clearable
+                  v-model="form.registerDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="请选择日期"
+                >
+                </el-date-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="官方行业" prop="officialIndustry">
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.officialIndustry"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item
+                label="统一社会信用代码	"
+                prop="unifiedSocialCreditCode"
+                 label-width="130px"
+              >
+                <el-input
+                  placeholder="请输入"
+                  v-model="form.unifiedSocialCreditCode"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item prop="businessLicenseFile" label="营业执照附件">
+                <fileUpload
+                  v-model="form.businessLicenseFiles"
+                  module="main"
+                  :showLib="false"
+                  :limit="1"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="16">
+              <el-form-item label="备注" prop="remark">
+                <el-input
+                  type="textarea"
+                  resize="none"
+                  v-model="form.remark"
+                  :rows="2"
+                  placeholder="请输入"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="银行信息" name="bank">
+        <ele-pro-table
+          ref="table"
+          :columns="bankColumns"
+          :datasource="tableBankData"
+          height="350px"
+          :need-page="false"
+        >
+          <!-- 表头工具栏 -->
+          <template v-slot:toolbar>
+            <el-button type="primary" @click="addBank">添加</el-button>
+          </template>
+          <template v-slot:action="{ row, $index }">
+            <el-link type="primary" @click="handleBankDel(row, $index)"
+              >删除</el-link
+            >
+          </template>
+          <template v-slot:accountName="{ row }">
+            <el-input v-model="row.accountName" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:accountNo="{ row }">
+            <el-input v-model="row.accountNo" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:bankName="{ row }">
+            <el-input v-model="row.bankName" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:interbankNo="{ row }">
+            <el-input v-model="row.interbankNo" placeholder="请输入"></el-input>
+          </template>
+        </ele-pro-table>
+      </el-tab-pane>
+      <el-tab-pane label="联系人信息" name="link">
+        <ele-pro-table
+          ref="linkTable"
+          :columns="linkColumns"
+          :datasource="tableLinkData"
+          height="350px"
+          :need-page="false"
+        >
+          <!-- 表头工具栏 -->
+          <template v-slot:toolbar>
+            <el-button type="primary" @click="addLink">添加</el-button>
+          </template>
+          <template v-slot:action="{ row, $index }">
+            <el-link type="primary" @click="handleLinkDel(row, $index)"
+              >删除</el-link
+            >
+          </template>
+          <template v-slot:linkName="{ row }">
+            <el-input v-model="row.linkName" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:mobilePhone="{ row }">
+            <el-input v-model="row.mobilePhone" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:phone="{ row }">
+            <el-input v-model="row.phone" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:email="{ row }">
+            <el-input v-model="row.email" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:post="{ row }">
+            <el-input v-model="row.post" placeholder="请输入"></el-input>
+          </template>
+          <template v-slot:ifChief="{ row }">
+            <el-select v-model="row.ifChief" placeholder="请选择" class="w100">
+              <el-option
+                v-for="item in ifChiefList"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              ></el-option>
+            </el-select>
+          </template>
+          <template v-slot:status="{ row }">
+            <DictSelection
+              dictName="客户联系人状态"
+              v-model="row.status"
+            ></DictSelection>
+          </template>
+          <template v-slot:remark="{ row }">
+            <el-input v-model="row.remark" placeholder="请输入"></el-input>
+          </template>
+        </ele-pro-table>
+      </el-tab-pane>
+      <el-tab-pane label="其他信息" name="other">
+        <el-form
+          label-width="100px"
+          ref="otherForm"
+          :model="otherForm"
+          :rules="otherRules"
+          style="margin-top: 30px"
+        >
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="结算方式" prop="settlementMode">
+                <DictSelection
+                  dictName="结算方式"
+                  clearable
+                  v-model="otherForm.settlementMode"
+                >
+                </DictSelection>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="税率" prop="taxRate">
+                <el-input v-model.number="otherForm.taxRate">
+                  <template slot="append">%</template>
+                </el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="折扣率" prop="discount">
+                <el-input v-model.number="otherForm.discount">
+                  <template slot="append">%</template>
+                </el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item
+                label="分管部门:"
+                prop="deptId"
+                style="margin-bottom: 22px"
+              >
+                <deptSelect
+                  v-model="otherForm.deptId"
+                  @changeGroup="change_principalDep"
+                  placeholder="请选择分管部门"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="业务员:" prop="salesmanId">
+                <personSelect
+                  ref="directorRef"
+                  v-model="otherForm.salesmanId"
+                  :init="false"
+                />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="8">
+              <el-form-item label="寄件人" prop="sender">
+                <el-input placeholder="请输入" v-model="otherForm.sender"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="寄件人电话" prop="senderPhone">
+                <el-input
+                  placeholder="请输入"
+                  v-model.number="otherForm.senderPhone"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="寄件人地址" prop="addressId">
+                <el-cascader
+                  clearable
+                  v-model="otherForm.addressId"
+                  :options="options.cityDataLabel"
+                  ref="sendAddress"
+                  style="width: 100%"
+                ></el-cascader>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="寄件人详细地址" prop="address"
+                 label-width="130px">
+                <el-input
+                  placeholder="请输入"
+                  v-model="otherForm.address"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </el-tab-pane>
+    </el-tabs>
+
+    <div slot="footer" class="footer">
+      <el-button type="primary" @click="save">保存</el-button>
+      <el-button @click="cancel">返回</el-button>
+    </div>
+  </ele-modal>
+</template>
+
+<script>
+  import {
+    contactSave,
+    contactUpdate,
+    contactDetail
+  } from '@/api/saleManage/contact';
+  import {
+    categoryData,
+    CodeToText,
+    TextToCode
+  } from 'element-china-category-data';
+  import fileUpload from '@/components/upload/fileUpload';
+  import { cityDataLabel } from 'ele-admin/packages/utils/regions';
+  import {
+    uploadFile,
+    removeFile
+  } from '@/api/system/file/index.js';
+  import dictMixins from '@/mixins/dictMixins';
+  import deptSelect from '@/components/CommomSelect/dept-select.vue';
+  import personSelect from '@/components/CommomSelect/person-select.vue';
+  export default {
+    props: {
+      categoryId: String
+    },
+    mixins: [dictMixins],
+    components: {
+      fileUpload,
+      deptSelect,
+      personSelect
+    },
+    data() {
+      return {
+        visible: false,
+        title: '',
+        row: {},
+        activeName: 'base',
+        form: {
+          address: '',
+          addressId: 0,
+          addressName: '',
+          authorizationLimit: 0,
+          businessLicenseFiles: [],
+          businessLicenseFile: {},
+          businessScope: '',
+          categoryId: '',
+          companyCategoryId: '',
+          companyCategoryName: '',
+          hasParentGroup: 0,
+          industryCode: '',
+          industryFullName: '',
+          mainProduct: '',
+          name: '',
+          officialIndustry: '',
+          phone: '',
+          registerDate: '',
+          remark: '',
+          serialNo: '',
+          simpleName: '',
+          type: 1,
+          unifiedSocialCreditCode: ''
+        },
+        otherForm: {
+          settlementMode: '',
+          settlementModeName: '',
+          taxRate: 0,
+          address: '',
+          addressId: '',
+          deptId: '',
+          deptName: '',
+          discount: 0,
+          salesmanId: '',
+          salesmanName: '',
+          sender: '',
+          senderPhone: ''
+        },
+        // removeBankList: [],
+        // removeLinkList: [],
+        tableBankData: [],
+        tableLinkData: [],
+        ifChiefList: [
+          {
+            value: 0,
+            label: '非首要'
+          },
+          {
+            value: `2`,
+            label: '首要'
+          }
+        ],
+        bankColumns: [
+          {
+            type: 'index',
+            width: 55,
+            align: 'center'
+          },
+          {
+            label: '单位名称',
+            prop: 'accountName',
+            slot: 'accountName',
+            action: 'accountName'
+          },
+          {
+            label: '银行账号',
+            prop: 'accountNo',
+            slot: 'accountNo',
+            action: 'accountNo'
+          },
+          {
+            label: '开户行',
+            prop: 'bankName',
+            slot: 'bankName',
+            action: 'bankName'
+          },
+          {
+            label: '银行银联号',
+            prop: 'interbankNo',
+            prop: 'interbankNo',
+            slot: 'interbankNo',
+            action: 'interbankNo'
+          },
+          {
+            action: 'action',
+            slot: 'action',
+            label: '操作'
+          }
+        ],
+        linkColumns: [
+          {
+            type: 'index',
+            width: 55,
+            align: 'center'
+          },
+          {
+            label: '联系人名称',
+            prop: 'linkName',
+            slot: 'linkName',
+            action: 'linkName'
+          },
+          {
+            label: '手机',
+            prop: 'mobilePhone',
+            slot: 'mobilePhone',
+            action: 'mobilePhone'
+          },
+          {
+            label: '电话',
+            prop: 'phone',
+            slot: 'phone',
+            action: 'phone'
+          },
+          {
+            label: '邮箱',
+            prop: 'email',
+            prop: 'email',
+            slot: 'email',
+            action: 'email'
+          },
+          {
+            label: '职务',
+            prop: 'post',
+            prop: 'post',
+            slot: 'post',
+            action: 'post'
+          },
+          {
+            label: '状态',
+            prop: 'status',
+            prop: 'status',
+            slot: 'status',
+            action: 'status'
+          },
+          {
+            label: '是否首要',
+            prop: 'ifChief',
+            prop: 'ifChief',
+            slot: 'ifChief',
+            action: 'ifChief'
+          },
+          {
+            label: '备注',
+            prop: 'remark',
+            prop: 'remark',
+            slot: 'remark',
+            action: 'remark'
+          },
+          {
+            action: 'action',
+            slot: 'action',
+            label: '操作'
+          }
+        ],
+        rules: {
+          name: [
+            { required: true, message: '请输入客户名称', trigger: 'blur' }
+          ],
+          authorizationLimit: [
+            { required: true, message: '请输入授信额度', trigger: 'change' }
+          ]
+        },
+        otherRules: {
+          settlementMode: [
+            { required: true, message: '请输入结算方式', trigger: 'blur' }
+          ],
+          taxRate: [
+            { required: true, message: '请输入税率', trigger: 'change' }
+          ]
+        },
+        options: {
+          cityDataLabel,
+          categoryData
+        },
+        // 提交状态
+        loading: false,
+        // 是否是修改
+        isUpdate: false
+      };
+    },
+    methods: {
+      async open(type, row) {
+        this.title = type;
+        this.row = row;
+        this.visible = true;
+        if (type == '新建客户') {
+        } else {
+          this.isUpdate = true;
+        this._getById(row.id);
+        }
+      },
+
+      // 选择负责人部门
+      change_principalDep(id, info) {
+        this.otherForm.salesmanId = '';
+        // 根据部门获取人员
+        const params = { groupId: id };
+        this.$nextTick(() => {
+          this.$refs.directorRef.getList(params);
+        });
+      },
+      addBank() {
+        this.tableBankData.push({
+          accountName: '',
+          accountNo: '',
+          bankName: '',
+          interbankNo: ''
+        });
+      },
+      addLink() {
+        this.tableLinkData.push({
+          linkName: '',
+          mobilePhone: '',
+          phone: '',
+          email: '',
+          post: '',
+          status: '',
+          ifChief: 0,
+          remark: ''
+        });
+      },
+      handleBankDel(row, index) {
+        this.tableBankData.splice(index, 1);
+        if (row?.id) {
+        //   this.removeBankList.push(row.id);
+        }
+      },
+      handleLinkDel(row, index) {
+        this.linkColumns.splice(index, 1);
+        if (row?.id) {
+        //   this.removeLinkList.push(row.id);
+        }
+      },
+      async save() {
+        const isBaseValid = await this.$refs.form.validate();
+        const isOtherValid = await this.$refs.form.validate();
+        if (!isBaseValid || !isOtherValid) {
+          return false;
+        }
+
+        this.loading = true;
+        // 基本信息处理
+        this.form.categoryId = this.categoryId;
+        debugger
+        if (this.$refs.address.getCheckedNodes()) {
+          let node = this.$refs.address.getCheckedNodes()[0];
+          if (node) {
+            this.form.addressId = node.path.join();;
+            this.form.addressName = node.pathLabels.join();
+          }
+        }
+        if (this.$refs.industry.getCheckedNodes()) {
+          let node = this.$refs.industry.getCheckedNodes()[0];
+          if (node) {
+            this.form.industryCode = node.path.join();;
+            this.form.industryFullName = node.pathLabels.join();
+          }
+        }
+        if (this.form.companyCategoryId) {
+          this.form.companyCategoryName = this.getDictValue(
+            '企业类型',
+            this.form.companyCategoryId
+          );
+        }
+        if (
+          this.form.businessLicenseFiles &&
+          this.form.businessLicenseFiles.length > 0
+        ) {
+          this.form.businessLicenseFile = this.form.businessLicenseFile[0];
+        }
+        // 其他信息处理
+        if (this.form.settlementMode) {
+          this.form.settlementModeName = this.getDictValue(
+            '结算方式',
+            this.form.settlementMode
+          );
+        }
+        if (this.$refs.sendAddress.getCheckedNodes()) {
+          let node = this.$refs.sendAddress.getCheckedNodes()[0];
+          if (node) {
+            this.otherForm.addressId = node.path.join();;
+            this.otherForm.addressName = node.pathLabels.join();
+          }
+        }
+
+        if (!this.isUpdate) {
+          delete this.form.id;
+        } else {
+        //   this.form.removeBankList = this.removeBankList;
+        //   this.form.removeLinkList = this.removeLinkList;
+        }
+        const data = {
+          base: this.form,
+          other: this.otherForm,
+          bankList: this.tableBankData,
+          linkList: this.tableLinkData
+        };
+
+        if (this.isUpdate) {
+          contactUpdate(data)
+            .then((res) => {
+              this.loading = false;
+              this.$message.success("修改成功");
+              this.cancel();
+              this.$emit('done');
+            })
+            .catch((e) => {
+              this.loading = false;
+            });
+        } else {
+          contactSave(data)
+            .then((res) => {
+              this.loading = false;
+              this.$message.success("新增成功");
+              this.cancel();
+              this.$emit('done');
+            })
+            .catch((e) => {
+              this.loading = false;
+            });
+        }
+      },
+      cancel() {
+        this.visible = false;
+      },
+      async _getById(id) {
+        const data = await contactDetail(id);
+        this.form = data.base;
+        this.otherForm = data.other;
+        this.tableBankData = data.bankList;
+        this.tableLinkData = data.linkList;
+      },
+      // 文件上传
+      async handlSuccess(params) {
+        let res = await uploadFile({
+          multiPartFile: params.file,
+          module: this.module
+        });
+        if (res?.data) {
+          this.$emit('input', res.data);
+        }
+      },
+      async clearImg() {
+        await removeFile({ fileId: this.value.id });
+        this.$emit('input', {});
+        this.$refs.uploadRef.clearFiles();
+      },
+      // 限制上传的数量
+      handleExceed(files, fileList) {
+        this.$message.warning(`最多允许上传一张图片!`);
+      }
+    }
+  };
+</script>

+ 78 - 0
src/views/saleManage/contact/components/contactSearch.vue

@@ -0,0 +1,78 @@
+<!-- 搜索表单 -->
+<template>
+  <el-form
+    label-width="100px"
+    class="ele-form-search"
+    @keyup.enter.native="search"
+    @submit.native.prevent
+  >
+    <el-row :gutter="15">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="客户名称:" prop="name">
+          <el-input
+            clearable
+            placeholder="请输入"
+            v-model.trim="params.name"
+          ></el-input>
+        </el-form-item>
+      </el-col>
+      <!-- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="编号/代码:" prop="codeOrSerialNo">
+          <el-input
+            clearable
+            v-model="params.codeOrSerialNo"
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+      </el-col> -->
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <div class="ele-form-actions">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            class="ele-btn-icon"
+            @click="search"
+          >
+            查询
+          </el-button>
+          <el-button @click="reset">重置</el-button>
+        </div>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+<script>
+  export default {
+    data () {
+      // 默认表单数据
+      const defaultParams = {
+        informationName: '',
+        informationCode: ''
+      };
+      return {
+        // 表单数据
+        params: { ...defaultParams }
+      };
+    },
+    computed: {
+      // 是否开启响应式布局
+      styleResponsive () {
+        return this.$store.state.theme.styleResponsive;
+      }
+    },
+    methods: {
+      /* 搜索 */
+      search () {
+        console.log(this.current);
+        this.$emit('search', {
+          ...this.params
+        });
+      },
+      /*  重置 */
+      reset () {
+        this.params = { ...this.defaultParams };
+        this.search();
+      }
+    }
+  };
+</script>

+ 232 - 0
src/views/saleManage/contact/edit.vue

@@ -0,0 +1,232 @@
+<template>
+  <div class="ele-body">111
+    <el-card shadow="never">
+      <el-form label-width="120px" ref="manageForm" :model="form" :rules="rules">
+        <headerTitle title="基本信息">
+          <el-button @click="cancel">返回</el-button>
+          <el-button type="primary" @click="submit" :loading="loading">保存</el-button>
+        </headerTitle>
+
+        <el-row :gutter="24">
+          <el-col :span="8">
+            <el-form-item label="分类" prop="categoryLevelName">
+              <el-input v-model="form.categoryLevelName" @click.native="openCategory" />
+            </el-form-item>
+          </el-col>
+
+          
+        <el-col :span="8" v-if="ruleCode == '自定义'"  key="1" >
+            <el-form-item label="编码" prop="code">
+              <el-input v-model="form.code" readonly @click.native="openCode" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8"  v-else  key="2">
+            <el-form-item label="编码" prop="code">
+              <el-input v-model="form.code"  />
+            </el-form-item>
+          </el-col> 
+
+
+ 
+
+          <el-col :span="8">
+            <el-form-item label="名称" prop="name">
+              <el-input v-model="form.name" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="牌号" prop="brandNum">
+              <el-input v-model="form.brandNum" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="型号" prop="modelType">
+              <el-input v-model="form.modelType" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="规格" prop="specification">
+              <el-input v-model="form.specification" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="计量单位" prop="measuringUnit">
+              <DictSelection dictName="计量单位" clearable v-model="form.measuringUnit">
+              </DictSelection>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="重量单位" prop="weightUnit">
+              <DictSelection dictName="重量单位" clearable v-model="form.weightUnit">
+              </DictSelection>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="8">
+            <el-form-item label="包装单位" prop="packingUnit">
+              <DictSelection dictName="包装单位" clearable v-model="form.packingUnit">
+              </DictSelection>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="体积">
+              <div class="form-line">
+                <el-input v-model="form.volume" />
+                <DictSelection class="line-select" dictName="体积单位" clearable v-model="form.volumeUnit">
+                </DictSelection>
+              </div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="毛重">
+              <div class="form-line">
+                <el-input v-model="form.roughWeight" />
+
+              </div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="净重">
+              <div class="form-line">
+                <el-input v-model="form.netWeight" />
+              </div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="所属部门">
+              <deptSelect v-model="form.deptLeaderId" @changeGroup="searchDeptNodeClick" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="负责人">
+              <personSelect ref="directorRef" v-model="form.deptId" @selfChange="handleDirectorChange" :init="false" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+
+
+  </div>
+</template>
+
+<script>
+
+export default {
+  name: 'contactEdit',
+  components: {
+    
+  },
+  data() {
+    return {
+      loading: false,
+      form: {
+        categoryLevelGroupName: '',
+        categoryLevelName: ''
+      },
+      remarkform: {
+        remarkAttach: []
+      },
+      categoryAps: {},
+      categoryMes: {},
+      categoryMold: {},
+      categoryPallet: {},
+      categoryQms: {},
+      categoryVehicle: {},
+      categoryWms: {
+        isUnpack: 1
+      },
+      // 表单验证规则
+      rules: {
+        categoryLevelGroupName: [
+          { required: true, message: '请选择所属物料组', trigger: 'change' }
+        ],
+        code: [{ required: true, message: '请输入编码', trigger: 'blur' }],
+        name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+        categoryLevelName: [
+          { required: true, message: '请选择所属分类', trigger: 'change' }
+        ],
+        measuringUnit: [
+          { required: true, message: '请选择计量单位', trigger: 'change' }
+        ]
+      },
+      PathInfo: {},
+      id: null,
+
+      ruleCode: null,
+      codeShow: false
+    };
+  },
+  async created() {
+
+
+  },
+  methods: {
+    
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.ele-page-header {
+  border: none;
+}
+
+.body-top {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background: #fff;
+
+  .top-left {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    margin-left: -25px;
+
+    .el-form-item {
+      margin-bottom: 0;
+    }
+  }
+}
+
+.divider {
+  margin: 20px 0;
+
+  .title {
+    display: flex;
+    align-items: center;
+    margin-bottom: 10px;
+
+    div {
+      width: 8px;
+      height: 20px;
+      margin-right: 10px;
+    }
+
+    span {
+      font-size: 20px;
+    }
+  }
+
+  .ele-width {
+    width: 100%;
+    height: 2px;
+  }
+}
+
+.form-line {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+
+  .line-select {
+    margin-left: 15px;
+  }
+}
+</style>

+ 279 - 0
src/views/saleManage/contact/index.vue

@@ -0,0 +1,279 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never" v-loading="loading">
+      <ele-split-layout
+        width="300px"
+        allow-collapse
+        :right-style="{ overflow: 'hidden' }"
+      >
+        <div class="ele-border-lighter sys-organization-list">
+          <el-tree
+            :data="treeList"
+            :props="defaultProps"
+            ref="treeRef"
+            default-expand-all
+            :highlight-current="true"
+            node-key="id"
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </div>
+
+        <template v-slot:content>
+          <div class="ele-border-lighter form-content" v-loading="loading">
+            <contact-search @search="reload"> </contact-search>
+
+            <!-- 数据表格 -->
+            <ele-pro-table
+              ref="table"
+              :columns="columns"
+              :datasource="datasource"
+              cache-key="systemRoleTable"
+            >
+              <!-- 表头工具栏 -->
+              <template v-slot:toolbar>
+                <el-button
+                  size="small"
+                  type="primary"
+                  icon="el-icon-plus"
+                  class="ele-btn-icon"
+                  @click="openEdit('新建客户',{})"
+                >
+                  新建
+                </el-button>
+              </template>
+              <template v-slot:code="{ row }">
+                <el-link
+                  type="primary"
+                  :underline="false"
+                  @click="goDetail(row)"
+                >
+                  {{ row.code }}
+                </el-link>
+              </template>
+              <!-- 操作列 -->
+              <template v-slot:action="{ row }">
+                <el-link
+                  type="primary"
+                  :underline="false"
+                  icon="el-icon-edit"
+                  @click="openEdit('编辑客户',row)"
+                >
+                  修改
+                </el-link>
+                <el-popconfirm
+                  class="ele-action"
+                  title="确定要删除此客户信息吗?"
+                  @confirm="remove(row)"
+                >
+                  <template v-slot:reference>
+                    <el-link
+                      type="danger"
+                      :underline="false"
+                      icon="el-icon-delete"
+                    >
+                      删除
+                    </el-link>
+                  </template>
+                </el-popconfirm>
+              </template>
+            </ele-pro-table>
+          </div>
+        </template>
+      </ele-split-layout>
+    </el-card>
+    <AddContactDialog ref="addContactDialogRef" :categoryId="curNode.id" @done="reload"></AddContactDialog>
+
+  </div>
+</template>
+
+<script>
+  import ContactSearch from './components/contactSearch.vue';
+  import AddContactDialog from './components/addContactDialog.vue';
+  import {
+    contactPage,
+    contactDelete,
+    contactTypeTree
+  } from '@/api/saleManage/contact';
+  import dictMixins from '@/mixins/dictMixins';
+
+  export default {
+    mixins: [dictMixins],
+    components: {
+      ContactSearch,
+      AddContactDialog
+    },
+    data() {
+      return {
+        // 加载状态
+        loading: false,
+        columns: [
+          {
+            prop: 'name',
+            label: '客户名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'simpleName',
+            label: '客户简称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'addressName',
+            label: '地址名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'unifiedSocialCreditCode',
+            label: '统一社会信用代码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'status',
+            label: '状态',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (_row, _column, cellValue) => {
+              return this.getDictValue('客户状态', _row.status);
+            }
+          },
+          {
+            prop: 'createUserName',
+            label: '创建人',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'createTime',
+            label: '创建时间',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (_row, _column, cellValue) => {
+              return this.$util.toDateString(cellValue);
+            }
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 150,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true
+          }
+        ],
+        current: {},
+        curNode: {},
+        treeList: [],
+        treeLoading: false,
+        formData: {},
+        rootTreeId: null,
+        defaultProps: {
+          children: 'children',
+          label: 'name'
+        },
+        showEdit: true
+      };
+    },
+    computed: {},
+    created() {
+      this.requestDict('客户状态');
+      this.getTreeData();
+    },
+    methods: {
+      /* 表格数据源 */
+      datasource({ page, limit, where, order }) {
+        return contactPage({
+          pageNum: page,
+          size: limit,
+          ...where
+        });
+      },
+      async getTreeData() {
+        try {
+          this.treeLoading = true;
+
+          const res = await contactTypeTree({ type: 17 });
+          this.treeLoading = false;
+          if (res?.code === '0') {
+            this.treeList = res.data;
+            if(this.treeList && this.treeList[0]) {
+              this.curNode = this.treeList[0]
+            }
+            this.$nextTick(() => {
+              // 默认高亮第一级树节点
+              // if (this.treeList[0]) {
+              //   this.rootTreeId = this.treeList[0].id;
+              //   this.getDetail(this.treeList[0].id);
+              // }
+            });
+            return this.treeList;
+          }
+        } catch (error) {}
+        this.treeLoading = false;
+      },
+      handleNodeClick(data, node) {
+        this.curNode = node;
+        this.pathList = this.findParent([], data, this.treeList);
+        this.rootTreeId = null;
+        if (this.pathList.length == 0) {
+          this.rootTreeId = data.id;
+        } else {
+          this.rootTreeId =
+            this.pathList[this.pathList.length - 1] &&
+            this.pathList[this.pathList.length - 1].id;
+        }
+
+        this.reload({ categoryId: data.id });
+      },
+      // parents:用于返回的数组,childNode:要查询的节点,treeList:json树形数据
+      findParent(parents, childNode, treeList) {
+        for (let i = 0; i < treeList.length; i++) {
+          // 父节点查询条件
+          if (treeList[i].id === childNode.parentId) {
+            // 如果找到结果,保存当前节点
+            parents.push(treeList[i]);
+            // 用当前节点再去原数据查找当前节点的父节点
+            this.findParent(parents, treeList[i], this.treeList);
+            break;
+          } else {
+            if (treeList[i].children instanceof Array) {
+              //	没找到,遍历该节点的子节点
+              this.findParent(parents, childNode, treeList[i].children);
+            }
+          }
+        }
+        return parents;
+      },
+      /* 刷新表格 */
+      reload(where) {
+        this.$refs.table.reload({ page: 1, where });
+      },
+      openEdit(row, type) {
+        this.current = row;
+        this.showEdit = true;
+        this.$refs.addContactDialogRef.open(row, type);
+        this.$refs.addContactDialogRef.$refs.form &&
+        this.$refs.addContactDialogRef.$refs.form.clearValidate();
+      },
+      remove(row) {
+        contactDelete([row.id]).then((res) => {
+          this.$message.success('删除成功!');
+          this.reload();
+        });
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped></style>