Explorar o código

尔泓客户管理,销售合同、采购合同新功能

cz hai 1 ano
pai
achega
38c11aa808

BIN=BIN
src/assets/images/custom-1.png


BIN=BIN
src/assets/images/custom-2.png


BIN=BIN
src/assets/images/custom-3.png


BIN=BIN
src/assets/images/custom-4.png


BIN=BIN
src/assets/images/money1.png


BIN=BIN
src/assets/images/money2.png


BIN=BIN
src/assets/images/money3.png


+ 5 - 5
src/components/TitleInfo/index.vue

@@ -22,14 +22,14 @@ export default {
   height: 24px;
   line-height: 24px;
   .vertical-bar {
-    width: 3px;
-    height: 18px;
+    width: 4px;
+    height: 19px;
     background-color: #0084ff;
-    margin-right: 7px;
-    margin-top: 1px;
+    margin-right: 9px;
+    // margin-top: 1px;
   }
   .content {
-    font-size: 13px;
+    font-size: 14px;
     font-weight: 600;
     color: #333333;
   }

+ 69 - 206
src/components/byForm/index.vue

@@ -1,20 +1,9 @@
 <template>
   <div class="by-form">
-    <el-form
-      :model="formData"
-      :label-width="formOption.labelWidth"
-      :inline="formOption.inline || false"
-      :rules="rules"
-      :labelPosition="formOption.labelPosition || 'top'"
-      ref="byForm"
-      :disabled="formOption.disabled || false"
-    >
+    <el-form :model="formData" :label-width="formOption.labelWidth" :inline="formOption.inline || false" :rules="rules"
+             :labelPosition="formOption.labelPosition || 'top'" ref="byForm" :disabled="formOption.disabled || false">
       <template v-for="i in formConfig" :key="i.model">
-        <el-form-item
-          :label="i.label"
-          :prop="i.prop"
-          v-if="i.isShow || i.isShow == undefined"
-          :style="
+        <el-form-item :label="i.label" :prop="i.prop" v-if="i.isShow || i.isShow == undefined" :style="
             i.type == 'title'
               ? 'width:100%'
               : i.itemWidth
@@ -22,8 +11,7 @@
               : formOption.itemWidth
               ? 'width:' + formOption.itemWidth + '%'
               : '100%'
-          "
-          :class="
+          " :class="
             i.type == 'json'
               ? i.isHide
                 ? 'by-form-json dn'
@@ -31,219 +19,99 @@
               : i.isHide
               ? 'dn'
               : ''
-          "
-        >
-          <el-input
-            v-if="i.type == 'input'"
-            v-model="formData[i.prop]"
-            :placeholder="i.placeholder || $t('common.pleaseEnter')"
-            @input="(e) => commonsEmit(e, i)"
-            @change="(e) => commonsEmitChange(e, i)"
-            :type="i.itemType ? i.itemType : 'text'"
-            :disabled="i.disabled ? i.disabled : false"
-            :max="i.max"
-            :min="i.min"
-            :maxlength="i.maxlength"
-            :readonly="i.readonly ? i.readonly : false"
-            :style="i.style"
-          />
-          <el-input
-            v-if="i.type == 'selectInput'"
-            v-model="formData[i.prop]"
-            :placeholder="i.placeholder || $t('common.pleaseEnter')"
-            @input="(e) => commonsEmit(e, i)"
-            :type="i.itemType ? i.itemType : 'text'"
-            :disabled="i.disabled ? i.disabled : false"
-            :max="i.max"
-            :min="i.min"
-            :maxlength="i.maxlength"
-            :readonly="i.readonly ? i.readonly : false"
-          >
+          ">
+          <el-input v-if="i.type == 'input'" v-model="formData[i.prop]" :placeholder="i.placeholder || $t('common.pleaseEnter')"
+                    @input="(e) => commonsEmit(e, i)" @change="(e) => commonsEmitChange(e, i)" :type="i.itemType ? i.itemType : 'text'"
+                    :disabled="i.disabled ? i.disabled : false" :max="i.max" :min="i.min" :maxlength="i.maxlength"
+                    :readonly="i.readonly ? i.readonly : false" :style="i.style" />
+          <el-input v-if="i.type == 'selectInput'" v-model="formData[i.prop]" :placeholder="i.placeholder || $t('common.pleaseEnter')"
+                    @input="(e) => commonsEmit(e, i)" :type="i.itemType ? i.itemType : 'text'" :disabled="i.disabled ? i.disabled : false"
+                    :max="i.max" :min="i.min" :maxlength="i.maxlength" :readonly="i.readonly ? i.readonly : false">
             <template #prepend>
-              <el-select
-                v-model="formData[i.selectProp]"
-                :placeholder="i.selectPlaceholder || $t('common.pleaseSelect')"
-                @change="(e) => commonsEmit(e, i)"
-                :disabled="i.disabled ? i.disabled : false"
-                :readonly="i.readonly ? i.readonly : false"
-                style="width: 80px"
-              >
-                <el-option
-                  :label="j.title || j.name || j.label"
-                  :value="j.id || j.value"
-                  v-for="j in i.data"
-                  :key="j.id"
-                >
+              <el-select v-model="formData[i.selectProp]" :placeholder="i.selectPlaceholder || $t('common.pleaseSelect')"
+                         @change="(e) => commonsEmit(e, i)" :disabled="i.disabled ? i.disabled : false" :readonly="i.readonly ? i.readonly : false"
+                         style="width: 80px">
+                <el-option :label="j.title || j.name || j.label" :value="j.id || j.value" v-for="j in i.data" :key="j.id">
                 </el-option>
               </el-select>
             </template>
           </el-input>
-          <el-select
-            v-model="formData[i.prop]"
-            :multiple="i.multiple || false"
-            v-else-if="i.type == 'select'"
-            :placeholder="i.placeholder || $t('common.pleaseSelect')"
-            @change="(e) => commonsEmit(e, i)"
-            :disabled="i.disabled ? i.disabled : false"
-            :clearable="i.clearable ? i.clearable : false"
-            :filterable="i.filterable ? true : false"
-            :style="i.style"
-            :readonly="i.readonly ? i.readonly : false"
-          >
-            <el-option
-              :label="j.title || j.name || j.label"
-              :value="j.id || j.value"
-              v-for="j in i.data"
-              :key="j.id"
-            >
+          <el-select v-model="formData[i.prop]" :multiple="i.multiple || false" v-else-if="i.type == 'select'"
+                     :placeholder="i.placeholder || $t('common.pleaseSelect')" @change="(e) => commonsEmit(e, i)"
+                     :disabled="i.disabled ? i.disabled : false" :clearable="i.clearable ? i.clearable : false"
+                     :filterable="i.filterable ? true : false" :style="i.style" :readonly="i.readonly ? i.readonly : false">
+            <el-option :label="j.title || j.name || j.label" :value="j.id || j.value" v-for="j in i.data" :key="j.id">
             </el-option>
           </el-select>
-          <el-tree-select
-            v-model="formData[i.prop]"
-            v-else-if="i.type == 'treeSelect'"
-            :data="i.data"
-            :readonly="i.readonly ? i.readonly : false"
-            :props="{
+          <el-tree-select v-model="formData[i.prop]" v-else-if="i.type == 'treeSelect'" :data="i.data" :readonly="i.readonly ? i.readonly : false"
+                          :props="{
               value: i.propsTreeValue || 'id',
               label: i.propsTreeLabel || 'label',
               children: i.propsTreeChildren || 'children',
-            }"
-            value-key="id"
-            :placeholder="i.placeholder || $t('common.pleaseSelect')"
-            :disabled="i.disabled ? i.disabled : false"
-            check-strictly
-            :style="i.style"
-          />
-          <el-date-picker
-            v-model="formData[i.prop]"
-            :readonly="i.readonly ? i.readonly : false"
-            v-else-if="i.type == 'date'"
-            :type="i.itemType"
-            :placeholder="i.placeholder || $t('common.pleaseSelectTime')"
-            @change="(e) => commonsEmit(e, i)"
-            :disabled="i.disabled ? i.disabled : false"
-            :format="i.format ? i.format : dateFormatInit(i.itemType)"
-            :value-format="i.format ? i.format : dateFormatInit(i.itemType)"
-          />
-          <el-switch
-            :disabled="i.disabled ? i.disabled : false"
-            v-else-if="i.type == 'switch'"
-            :readonly="i.readonly ? i.readonly : false"
-            v-model="formData[i.prop]"
-          />
-          <el-checkbox-group
-            v-else-if="i.type == 'checkbox'"
-            v-model="formData[i.prop]"
-            :readonly="i.readonly ? i.readonly : false"
-            :disabled="i.disabled ? i.disabled : false"
-          >
-            <el-checkbox
-              v-for="j in i.data"
-              :key="j.id || j.value"
-              :label="j.id || j.value"
-              name="type"
-            >
+            }" value-key="id" :placeholder="i.placeholder || $t('common.pleaseSelect')" :disabled="i.disabled ? i.disabled : false" check-strictly
+                          :style="i.style" />
+          <el-date-picker v-model="formData[i.prop]" :readonly="i.readonly ? i.readonly : false" v-else-if="i.type == 'date'" :type="i.itemType"
+                          :placeholder="i.placeholder || $t('common.pleaseSelectTime')" @change="(e) => commonsEmit(e, i)"
+                          :disabled="i.disabled ? i.disabled : false" :format="i.format ? i.format : dateFormatInit(i.itemType)"
+                          :value-format="i.format ? i.format : dateFormatInit(i.itemType)" />
+          <el-switch :disabled="i.disabled ? i.disabled : false" v-else-if="i.type == 'switch'" :readonly="i.readonly ? i.readonly : false"
+                     v-model="formData[i.prop]" />
+          <el-checkbox-group v-else-if="i.type == 'checkbox'" v-model="formData[i.prop]" :readonly="i.readonly ? i.readonly : false"
+                             :disabled="i.disabled ? i.disabled : false">
+            <el-checkbox v-for="j in i.data" :key="j.id || j.value" :label="j.id || j.value" name="type">
               {{ j.name || j.label }}
             </el-checkbox>
           </el-checkbox-group>
-          <el-radio-group
-            v-else-if="i.type == 'radio'"
-            v-model="formData[i.prop]"
-            :readonly="i.readonly ? i.readonly : false"
-            :disabled="i.disabled ? i.disabled : false"
-          >
-            <el-radio
-              :border="i.border ? i.border : false"
-              v-for="j in i.data"
-              :key="j.id || j.value"
-              :label="j.id || j.value"
-              name="type"
-            >
+          <el-radio-group v-else-if="i.type == 'radio'" v-model="formData[i.prop]" :readonly="i.readonly ? i.readonly : false"
+                          :disabled="i.disabled ? i.disabled : false">
+            <el-radio :border="i.border ? i.border : false" v-for="j in i.data" :key="j.id || j.value" :label="j.id || j.value" name="type">
               {{ j.name || j.label }}
             </el-radio>
           </el-radio-group>
-          <el-input-number
-            v-else-if="i.type == 'number'"
-            v-model="formData[i.prop]"
-            :readonly="i.readonly ? i.readonly : false"
-            :placeholder="i.placeholder || $t('common.pleaseEnter')"
-            @change="(e) => commonsEmit(e, i)"
-            :disabled="i.disabled ? i.disabled : false"
-            :min="i.min ? i.min : 0"
-            :max="i.max ? i.max : 9999999999"
-            :step="i.step ? i.step : 1"
-            :precision="i.precision !== '' ? i.precision : 2"
-            :controls="i.controls === false ? false : true"
-            :style="i.style"
-            onmousewheel="return false;"
-          >
+          <el-input-number v-else-if="i.type == 'number'" v-model="formData[i.prop]" :readonly="i.readonly ? i.readonly : false"
+                           :placeholder="i.placeholder || $t('common.pleaseEnter')" @change="(e) => commonsEmit(e, i)"
+                           :disabled="i.disabled ? i.disabled : false" :min="i.min ? i.min : 0" :max="i.max ? i.max : 9999999999"
+                           :step="i.step ? i.step : 1" :precision="i.precision !== '' ? i.precision : 2"
+                           :controls="i.controls === false ? false : true" :style="i.style" onmousewheel="return false;">
           </el-input-number>
-          <el-tree
-            v-else-if="i.type == 'tree'"
-            :data="i.data"
-            :props="i.props"
-            :readonly="i.readonly ? i.readonly : false"
-            :show-checkbox="i.showCheckbox || true"
-          >
+          <el-tree v-else-if="i.type == 'tree'" :data="i.data" :props="i.props" :readonly="i.readonly ? i.readonly : false"
+                   :show-checkbox="i.showCheckbox || true">
           </el-tree>
-          <el-cascader
-            v-else-if="i.type == 'cascader'"
-            :options="i.data"
-            :props="i.props"
-            :readonly="i.readonly ? i.readonly : false"
-            :placeholder="i.placeholder || $t('common.pleaseSelect')"
-            @change="(e) => commonsEmit(e, i)"
-            :disabled="i.disabled ? i.disabled : false"
-            :style="i.style"
-          >
+          <el-cascader v-else-if="i.type == 'cascader'" :options="i.data" :props="i.props" :readonly="i.readonly ? i.readonly : false"
+                       :placeholder="i.placeholder || $t('common.pleaseSelect')" @change="(e) => commonsEmit(e, i)"
+                       :disabled="i.disabled ? i.disabled : false" :style="i.style">
           </el-cascader>
-          <div class="form-title" v-else-if="i.type == 'title'">
+          <!-- <div class="form-title" v-else-if="i.type == 'title'">
             {{ i.title }}
+          </div> -->
+
+          <div v-else-if="i.type == 'title'" style="width:100%">
+            <div v-if="i.haveLine" style="width:calc(100% + 90px);margin-left:-45px;background:#F0F2F5;height:15px"></div>
+            <div :style="{marginTop:i.haveLine?'15px':''}" style="margin-left:-15px;">
+              <TitleInfo :content="i.title"></TitleInfo>
+            </div>
           </div>
+
           <slot :name="i.slotName" v-else-if="i.type == 'slot'">
             {{ i.slotName }}插槽占位符
           </slot>
           <div class="upload" v-else-if="i.type == 'upload'">
-            <el-upload
-              v-model="formData[i.prop]"
-              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
-              :data="uploadData"
-              list-type="picture-card"
-              :on-remove="handleRemove"
-              :on-success="handleSuccess"
-              :before-upload="handleBeforeUpload"
-            >
+            <el-upload v-model="formData[i.prop]" action="https://winfaster.obs.cn-south-1.myhuaweicloud.com" :data="uploadData"
+                       list-type="picture-card" :on-remove="handleRemove" :on-success="handleSuccess" :before-upload="handleBeforeUpload">
               <!-- <el-icon class="el-icon--upload"><upload-filled /></el-icon> -->
-              <el-icon><Plus /></el-icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
             </el-upload>
           </div>
-          <div
-            v-else-if="i.type == 'table'"
-            class="by-form-table"
-            style="width: 100%"
-          >
+          <div v-else-if="i.type == 'table'" class="by-form-table" style="width: 100%">
             <el-table :data="formData[i.prop]" style="width: 100%">
-              <el-table-column
-                :prop="j.prop"
-                :label="j.label"
-                :width="i.width"
-                v-for="(j, jindex) in i.column"
-              >
+              <el-table-column :prop="j.prop" :label="j.label" :width="i.width" v-for="(j, jindex) in i.column">
                 <template #default="scope" v-if="j.type">
-                  <component
-                    @change="(e) => formTableChange(e, scope, j)"
-                    v-model="scope.row[j.prop]"
-                    :is="formTableObj[j.type]"
-                    :placeholder="j.placeholder || $t('common.pleaseEnter')"
-                    :type="j.type == 'number' ? 'number' : 'text'"
-                  >
-                    <el-option
-                      :label="n.title || n.name || n.label"
-                      :value="n.id || n.value"
-                      v-for="n in j.data"
-                      :key="n.id"
-                      v-if="j.type == 'select'"
-                    >
+                  <component @change="(e) => formTableChange(e, scope, j)" v-model="scope.row[j.prop]" :is="formTableObj[j.type]"
+                             :placeholder="j.placeholder || $t('common.pleaseEnter')" :type="j.type == 'number' ? 'number' : 'text'">
+                    <el-option :label="n.title || n.name || n.label" :value="n.id || n.value" v-for="n in j.data" :key="n.id"
+                               v-if="j.type == 'select'">
                     </el-option>
                   </component>
                 </template>
@@ -251,13 +119,7 @@
             </el-table>
           </div>
           <div v-else-if="i.type == 'json'">
-            <byForm
-              :formConfig="i.json"
-              :formOption="formOption"
-              v-model="formData[i.prop]"
-              ref="byform"
-              :rules="rules"
-            >
+            <byForm :formConfig="i.json" :formOption="formOption" v-model="formData[i.prop]" ref="byform" :rules="rules">
             </byForm>
           </div>
         </el-form-item>
@@ -273,6 +135,7 @@ export default {
 <script  setup>
 import { set } from "@vueuse/shared";
 import { reactive } from "vue";
+import TitleInfo from "@/components/TitleInfo/index.vue";
 defineProps({
   modelValue: {
     type: Object,

+ 35 - 127
src/components/byTable/index.vue

@@ -1,30 +1,15 @@
 <template>
-  <div class="header-actions" v-if="getActionList.length != 0">
+  <div class="header-actions" v-if="getActionList.length != 0 &&getActionList.length>1">
     <div class="overflow-box">
-      <el-button
-        v-for="(item, index) in getActionList"
-        :key="index"
-        :type="item.type || 'primary'"
-        :plain="item.plain || false"
-        v-bind="getHeaderActions(item)"
-        @click="item.action"
-        :disabled="item.disabled || false"
-      >
+      <el-button v-for="(item, index) in getActionList" :key="index" :type="item.type || 'primary'" :plain="item.plain || false"
+                 v-bind="getHeaderActions(item)" @click="item.action" :disabled="item.disabled || false">
         {{ item.text }}
       </el-button>
     </div>
   </div>
-  <div
-    class="stat-warp"
-    v-if="statConfig.length != 0"
-    :class="statWarpHeight > 200 && isMore ? 'show-more' : ''"
-  >
+  <div class="stat-warp" v-if="statConfig.length != 0" :class="statWarpHeight > 200 && isMore ? 'show-more' : ''">
     <div class="title">
-      <select
-        v-model="statSelectVal"
-        v-if="statConfig.length > 1"
-        @change="changeStatData"
-      >
+      <select v-model="statSelectVal" v-if="statConfig.length > 1" @change="changeStatData">
         <option :value="index" v-for="(i, index) in statConfig" :key="index">
           {{ i.label }}
         </option>
@@ -38,26 +23,13 @@
       </span>
     </div>
     <ul id="statWarp">
-      <li
-        v-show="!i.data"
-        :class="'theme' + i.type"
-        v-for="(i, index) in statConfig[statSelectVal].data"
-        :key="index"
-        @click="i.click ? i.click(i, index) : ''"
-        :style="i.click ? 'cursor: pointer' : ''"
-      >
+      <li v-show="!i.data" :class="'theme' + i.type" v-for="(i, index) in statConfig[statSelectVal].data" :key="index"
+          @click="i.click ? i.click(i, index) : ''" :style="i.click ? 'cursor: pointer' : ''">
         <div class="label">{{ i.label }}</div>
         <div class="num">{{ i.num }}</div>
       </li>
-      <li
-        v-show="i.data"
-        v-for="(i, index) in statConfig[statSelectVal].data"
-        :key="index"
-        class="multi-data"
-        :class="'theme' + i.type"
-        @click="i.click ? i.click(i, index) : ''"
-        :style="i.click ? 'cursor: pointer' : ''"
-      >
+      <li v-show="i.data" v-for="(i, index) in statConfig[statSelectVal].data" :key="index" class="multi-data" :class="'theme' + i.type"
+          @click="i.click ? i.click(i, index) : ''" :style="i.click ? 'cursor: pointer' : ''">
         <div class="label">{{ i.label }}</div>
         <div class="num-warp">
           <div class="num-box" v-for="(j, jindex) in i.data" :key="jindex">
@@ -77,12 +49,13 @@
     </header>
     <div class="by-search" v-if="!hideSearch">
       <div style="display: flex">
-        <div
-          class="by-dropdown"
-          v-for="(i, index) in selectConfigCopy"
-          :key="i.prop"
-          style="margin-right: 10px"
-        >
+        <div v-if="getActionList.length != 0 &&getActionList.length==1" style="margin-right:10px">
+          <el-button v-for="(item, index) in getActionList" :key="index" :type="item.type || 'primary'" :plain="item.plain || false"
+                     v-bind="getHeaderActions(item)" @click="item.action" :disabled="item.disabled || false">
+            {{ item.text }}
+          </el-button>
+        </div>
+        <div class="by-dropdown" v-for="(i, index) in selectConfigCopy" :key="i.prop" style="margin-right: 10px">
           <div class="by-dropdown-title">
             {{
               pagination[i.prop]
@@ -95,19 +68,10 @@
             <i style="margin-left: 5px" class="iconfont icon-iconm_xialan1"></i>
           </div>
           <ul class="by-dropdown-lists">
-            <li
-              @click="searchItemSelct('all', i, index)"
-              v-if="i.isShowAll === false ? i.isShowAll : true"
-              style=""
-            >
+            <li @click="searchItemSelct('all', i, index)" v-if="i.isShowAll === false ? i.isShowAll : true" style="">
               {{ $t("common.all") }}
             </li>
-            <li
-              v-for="j in i.data"
-              :key="j.value"
-              @click="searchItemSelct(j, i)"
-              style=""
-            >
+            <li v-for="j in i.data" :key="j.value" @click="searchItemSelct(j, i)" style="">
               {{ j.label }}
             </li>
           </ul>
@@ -115,26 +79,11 @@
       </div>
 
       <div style="display: flex">
-        <el-input
-          :placeholder="$t('common.pleaseEnterKeywords')"
-          suffix-icon="search"
-          size="mini"
-          v-model="pagination.keyword"
-          @keyup.enter="searchFn"
-        >
+        <el-input :placeholder="$t('common.pleaseEnterKeywords')" suffix-icon="search" size="mini" v-model="pagination.keyword"
+                  @keyup.enter="searchFn">
         </el-input>
-        <el-button
-          type="primary"
-          style="margin-left: 10px"
-          size="default"
-          @click="searchFn"
-          >{{ $t("common.search") }}</el-button
-        >
-        <div
-          class="more-icon"
-          @click="retrievalModalFn"
-          v-if="$attrs.onMoreSearch"
-        >
+        <el-button type="primary" style="margin-left: 10px" size="default" @click="searchFn">{{ $t("common.search") }}</el-button>
+        <div class="more-icon" @click="retrievalModalFn" v-if="$attrs.onMoreSearch">
           <i class="iconfont icon-iconx_saixuan"></i>
         </div>
       </div>
@@ -144,60 +93,30 @@
         <slot />
       </div>
 
-      <el-table
-        ref="hocElTable"
-        :data="source"
-        v-if="!hideTable"
-        style="width: 100%"
-        v-bind="$attrs"
-        v-on="tableEvents"
-        row-key="id"
-        :tree-props="{
+      <el-table ref="hocElTable" :data="source" v-if="!hideTable" style="width: 100%" v-bind="$attrs" v-on="tableEvents" row-key="id" :tree-props="{
           children: 'children',
           hasChildren: 'hasChildren',
-        }"
-        :height="tableHeight"
-      >
-        <el-table-column
-          v-for="(item, index) in configData"
-          :key="index"
-          v-bind="getAttrsValue(item)"
-          :type="item.type || ''"
-          :selectable="
+        }" :height="tableHeight">
+        <el-table-column v-for="(item, index) in configData" :key="index" v-bind="getAttrsValue(item)" :type="item.type || ''" :selectable="
             (rowData, rowIndex) => isSelectable(rowData, rowIndex, item)
-          "
-        >
+          ">
           <template #header v-if="item.attrs.isNeedHeaderSlot">
-            <slot
-              :name="item.attrs.headerSlot"
-              :headerLabel="item.attrs.label"
-              v-if="item.attrs.headerSlot"
-            >
+            <slot :name="item.attrs.headerSlot" :headerLabel="item.attrs.label" v-if="item.attrs.headerSlot">
               头部插槽占位符
             </slot>
           </template>
 
           <template #default="scope" v-if="!item.type">
-            <slot
-              :name="item.attrs.slot"
-              :item="scope.row"
-              :headerLabel="item.attrs.label"
-              v-if="item.attrs.slot"
-            >
+            <slot :name="item.attrs.slot" :item="scope.row" :headerLabel="item.attrs.label" v-if="item.attrs.slot">
               插槽占位符
             </slot>
             <div v-else-if="isFunction(getValue(scope, item))">
-              <component
-                :is="renderTypeList[getMatchRenderFunction(item)].target"
-                :cell-list="getValue(scope, item)()"
-                :row="scope.row"
-                :parent="getParent"
-                @click="
+              <component :is="renderTypeList[getMatchRenderFunction(item)].target" :cell-list="getValue(scope, item)()" :row="scope.row"
+                         :parent="getParent" @click="
                   ($event) => {
                     handleNativeClick(getAttrsValue(item), $event, item);
                   }
-                "
-              />
+                " />
             </div>
             <div v-else>
               {{ getValue(scope, item) }}
@@ -206,21 +125,10 @@
         </el-table-column>
       </el-table>
 
-      <el-row
-        v-if="!hidePagination"
-        class="table-pagination"
-        justify="end"
-        type="flex"
-      >
-        <el-pagination
-          background
-          layout="total, sizes, prev, pager, next, jumper"
-          :current-page="getPagination.pageNum"
-          :page-size="getPagination.pageSize"
-          :total="getPagination.total"
-          @size-change="handleSizeChange"
-          @current-change="handlePageChange"
-        />
+      <el-row v-if="!hidePagination" class="table-pagination" justify="end" type="flex">
+        <el-pagination background layout="total, sizes, prev, pager, next, jumper" :current-page="getPagination.pageNum"
+                       :page-size="getPagination.pageSize" :total="getPagination.total" @size-change="handleSizeChange"
+                       @current-change="handlePageChange" />
       </el-row>
     </component>
   </div>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 167 - 527
src/components/process/EHSD/Contract.vue


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 219 - 505
src/components/process/EHSD/ContractChange.vue


+ 188 - 5
src/components/process/EHSD/Purchase.vue

@@ -4,7 +4,7 @@
       <template #btn>
         <div>
           <el-button type="primary" v-if="
-              [30].includes(route.query.processType) || !route.query.processType
+              route.query.processType==30 || !route.query.processType
             " @click="clickCopy">复制采购合同</el-button>
         </div>
       </template>
@@ -60,7 +60,7 @@
               <el-select v-model="formData.data.sellCorporationId" style="width: 100%" filterable remote reserve-keyword placeholder="请输入关键字"
                          remote-show-suffix :remote-method="remoteMethod" :loading="loadingSearch" @input="remoteMethod" @change="changeSupplier"
                          v-if="
-                  [30].includes(route.query.processType) ||
+                 route.query.processType==30 ||
                   !route.query.processType
                 ">
                 <el-option v-for="item in supplierList" :key="item.value" :label="item.label" :value="item.value" />
@@ -245,7 +245,7 @@
                       </el-table-column>
                       <el-table-column label="产品名称" prop="productName" min-width="180" />
                       <el-table-column label="尺寸" prop="productModel" min-width="140" />
-                      <el-table-column label="单价" width="140">
+                      <el-table-column label="单价" width="160">
                         <template #default="{ row, $index }">
                           <div style="width: 100%" class="removePadding">
                             <el-form-item :prop="
@@ -259,8 +259,58 @@
                                   return changeProductMaterial(index, $index);
                                 }
                               ">
-                              <el-input-number onmousewheel="return false;" v-model="row.price" placeholder="请输入单价" :precision="2" :controls="false"
-                                               :min="0" />
+                              <div style="display:flex">
+                                <el-input-number onmousewheel="return false;" v-model="row.price" style="width: 100%" placeholder="请输入单价"
+                                                 :precision="2" :controls="false" :min="0" />
+                                <el-popover placement="top-start" :width="400" trigger="hover" @show="showEcharts(row,$index)">
+                                  <template #default>
+                                    <div>
+                                      <div>
+                                        <img src="@/assets/images/money1.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 该供应商近期采购单价:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.supplyPurchaseProductPriceList" :key="index">
+                                            <span>{{item.createTime.slice(0,10)}} </span>
+                                            <span style="margin-left:40px">CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+
+                                      <div style="margin-top:15px">
+                                        <img src="@/assets/images/money2.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 供货推荐:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.purchaseProductPriceList   " :key="index">
+                                            <span>供应商名称 </span>
+                                            <span style="margin-left:40px"> CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+
+                                      <div style="margin-top:15px">
+                                        <img src="@/assets/images/money3.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 近期采购单价:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.purchaseProductPriceList   " :key="index">
+                                            <span>{{item.createTime.slice(0,10)}} </span>
+                                            <span style="margin-left:40px"> CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+                                      <div :ref="row.productId+$index" :id="row.productId+$index" style="height:180px">
+                                      </div>
+                                    </div>
+                                  </template>
+                                  <template #reference>
+                                    <div style="margin-left:10px;cursor:pointer;position:relative;top:4px">
+                                      <el-icon :size="20" color="#85c1a6">
+                                        <WarningFilled />
+                                      </el-icon>
+                                    </div>
+                                  </template>
+                                </el-popover>
+                              </div>
+
                             </el-form-item>
                           </div>
                         </template>
@@ -472,6 +522,8 @@ import selectCity from "@/components/selectCity/index.vue";
 import Editor from "@/components/Editor/index.vue";
 import { useRoute } from "vue-router";
 import useUserStore from "@/store/modules/user";
+import * as echarts from "echarts";
+
 const userInfo = useUserStore();
 const route = useRoute();
 const { proxy } = getCurrentInstance();
@@ -836,7 +888,50 @@ const getBuyCityName = (val, provinceId) => {
     return;
   }
 };
+const changeProductPrice = () => {
+  let productIds = formData.data.purchaseProductList.map((x) => x.productId);
+  if (productIds && productIds.length > 0) {
+    proxy
+      .post("/ehsdPurchase/getProductPriceInfo", {
+        productIds: productIds,
+        sellCorporationId: formData.data.sellCorporationId
+          ? formData.data.sellCorporationId
+          : "",
+      })
+      .then((resOne) => {
+        for (let i = 0; i < formData.data.purchaseProductList.length; i++) {
+          const iele = formData.data.purchaseProductList[i];
+          for (const key in resOne) {
+            if (iele.productId == key) {
+              iele.purchaseProductMountingsList[0].purchaseProductPriceList =
+                resOne[key].purchaseProductList
+                  .map((x) => ({
+                    createTime: x.createTime,
+                    price: x.price,
+                  }))
+                  .filter((y, index) => index < 3);
+              iele.purchaseProductMountingsList[0].purchaseProductPriceListOne =
+                resOne[key].purchaseProductList.map((x) => ({
+                  createTime: x.createTime,
+                  price: x.price,
+                }));
+
+              iele.purchaseProductMountingsList[0].supplyPurchaseProductPriceList =
+                resOne[key].supplyPurchaseProductList
+                  .map((x) => ({
+                    createTime: x.createTime,
+                    price: x.price,
+                  }))
+                  .filter((y, index) => index < 3);
+            }
+          }
+        }
+      });
+  }
+};
+
 const changeSupplier = (val) => {
+  changeProductPrice();
   formData.data.countryId = "";
   formData.data.provinceId = "";
   formData.data.cityId = "";
@@ -1249,6 +1344,8 @@ onMounted(() => {
               }
             }
           });
+        // 单价提示
+        changeProductPrice();
       } else {
         formData.data.purchaseProductList = [];
       }
@@ -1258,6 +1355,21 @@ onMounted(() => {
   if (route.query && route.query.processType) {
     let businessId = route.query.businessId;
     proxy.post("/ehsdPurchase/detail", { id: businessId }).then((res) => {
+      // 退回回显
+      if (route.query.processType == 30) {
+        proxy
+          .post("/supplierInfo/page", { keyword: res.sellCorporationName })
+          .then((res) => {
+            supplierList.value = res.rows.map((item) => {
+              return {
+                ...item,
+                label: item.name,
+                value: item.id,
+              };
+            });
+          });
+      }
+
       res.purchaseProductList = res.ehsdPurchaseProductList || [];
       if (!res.fileList) {
         res.fileList = [];
@@ -1314,6 +1426,7 @@ onMounted(() => {
               }
             }
           });
+        changeProductPrice();
       }
       if (formData.data.countryId) {
         getCityData(formData.data.countryId, "20");
@@ -1746,6 +1859,7 @@ const selectPurchase = (businessId) => {
               }
             }
           });
+        changeProductPrice();
       }
       if (formData.data.countryId) {
         getCityData(formData.data.countryId, "20");
@@ -1761,6 +1875,69 @@ const selectPurchase = (businessId) => {
     });
   }
 };
+
+const optionTwo = reactive({
+  data: {
+    tooltip: {
+      trigger: "axis",
+    },
+    // legend: {
+    //   data: ["价格"],
+    // },
+    grid: {
+      left: "3%",
+      right: "4%",
+      top: "10%",
+      bottom: "3%",
+      containLabel: true,
+    },
+    // toolbox: {
+    //   feature: {
+    //     saveAsImage: {},
+    //   },
+    // },
+    xAxis: {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+    yAxis: {
+      type: "value",
+    },
+    series: [
+      {
+        name: "价格",
+        type: "line",
+        data: [],
+      },
+    ],
+  },
+});
+const showEcharts = (row, index) => {
+  let myChart = null;
+  myChart = echarts.init(document.getElementById(row.productId + index));
+  window.addEventListener("resize", () => {
+    myChart.resize();
+  });
+  if (
+    row.purchaseProductPriceListOne &&
+    row.purchaseProductPriceListOne.length > 0
+  ) {
+    optionTwo.data.xAxis.data = row.purchaseProductPriceListOne.map((item) => {
+      return item.createTime.slice(0, 10);
+    });
+    optionTwo.data.series[0].data = row.purchaseProductPriceListOne.map(
+      (item) => {
+        return item.price;
+      }
+    );
+  } else {
+    optionTwo.data.xAxis.data = [];
+    optionTwo.data.series[0].data = [];
+  }
+  myChart.setOption(optionTwo.data);
+  myChart.resize();
+};
 </script>
 
 <style lang="scss" scoped>
@@ -1805,4 +1982,10 @@ const selectPurchase = (businessId) => {
     padding-right: 0 !important;
   }
 }
+.img {
+  object-fit: contain;
+  width: 16px;
+  height: 16px;
+  vertical-align: middle;
+}
 </style>

+ 168 - 3
src/components/process/EHSD/PurchaseChange.vue

@@ -230,7 +230,7 @@
                       </el-table-column>
                       <el-table-column label="产品名称" prop="productName" min-width="180" />
                       <el-table-column label="尺寸" prop="productModel" min-width="140" />
-                      <el-table-column label="单价" width="140">
+                      <el-table-column label="单价" width="160">
                         <template #default="{ row, $index }">
                           <div style="width: 100%" class="removePadding">
                             <el-form-item :prop="
@@ -244,8 +244,57 @@
                                   return changeProductMaterial(index, $index);
                                 }
                               ">
-                              <el-input-number onmousewheel="return false;" v-model="row.price" placeholder="请输入单价" :precision="2" :controls="false"
-                                               :min="0" />
+                              <div style="display:flex">
+                                <el-input-number onmousewheel="return false;" v-model="row.price" style="width: 100%" placeholder="请输入单价"
+                                                 :precision="2" :controls="false" :min="0" />
+                                <el-popover placement="top-start" :width="400" trigger="hover" @show="showEcharts(row,$index)">
+                                  <template #default>
+                                    <div>
+                                      <div>
+                                        <img src="@/assets/images/money1.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 该供应商近期采购单价:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.supplyPurchaseProductPriceList" :key="index">
+                                            <span>{{item.createTime.slice(0,10)}} </span>
+                                            <span style="margin-left:40px">CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+
+                                      <div style="margin-top:15px">
+                                        <img src="@/assets/images/money2.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 供货推荐:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.purchaseProductPriceList   " :key="index">
+                                            <span>供应商名称 </span>
+                                            <span style="margin-left:40px"> CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+
+                                      <div style="margin-top:15px">
+                                        <img src="@/assets/images/money3.png" alt="" class="img" />
+                                        <span style="font-size:14px;font-weight:700;color:#000"> 近期采购单价:</span>
+                                        <div style="padding:5px 0px 0px 20px">
+                                          <div v-for="(item,index) in row.purchaseProductPriceList   " :key="index">
+                                            <span>{{item.createTime.slice(0,10)}} </span>
+                                            <span style="margin-left:40px"> CNY {{moneyFormat(item.price,2)}} </span>
+                                          </div>
+                                        </div>
+                                      </div>
+                                      <div :ref="row.productId+$index" :id="row.productId+$index" style="height:180px">
+                                      </div>
+                                    </div>
+                                  </template>
+                                  <template #reference>
+                                    <div style="margin-left:10px;cursor:pointer;position:relative;top:4px">
+                                      <el-icon :size="20" color="#85c1a6">
+                                        <WarningFilled />
+                                      </el-icon>
+                                    </div>
+                                  </template>
+                                </el-popover>
+                              </div>
                             </el-form-item>
                           </div>
                         </template>
@@ -452,6 +501,8 @@ import selectCity from "@/components/selectCity/index.vue";
 import Editor from "@/components/Editor/index.vue";
 import { useRoute } from "vue-router";
 import useUserStore from "@/store/modules/user";
+import * as echarts from "echarts";
+
 const userInfo = useUserStore();
 
 const route = useRoute();
@@ -811,7 +862,49 @@ const getBuyCityName = (val, provinceId) => {
     return;
   }
 };
+const changeProductPrice = () => {
+  let productIds = formData.data.purchaseProductList.map((x) => x.productId);
+  if (productIds && productIds.length > 0) {
+    proxy
+      .post("/ehsdPurchase/getProductPriceInfo", {
+        productIds: productIds,
+        sellCorporationId: formData.data.sellCorporationId
+          ? formData.data.sellCorporationId
+          : "",
+      })
+      .then((resOne) => {
+        for (let i = 0; i < formData.data.purchaseProductList.length; i++) {
+          const iele = formData.data.purchaseProductList[i];
+          for (const key in resOne) {
+            if (iele.productId == key) {
+              iele.purchaseProductMountingsList[0].purchaseProductPriceList =
+                resOne[key].purchaseProductList
+                  .map((x) => ({
+                    createTime: x.createTime,
+                    price: x.price,
+                  }))
+                  .filter((y, index) => index < 3);
+              iele.purchaseProductMountingsList[0].purchaseProductPriceListOne =
+                resOne[key].purchaseProductList.map((x) => ({
+                  createTime: x.createTime,
+                  price: x.price,
+                }));
+
+              iele.purchaseProductMountingsList[0].supplyPurchaseProductPriceList =
+                resOne[key].supplyPurchaseProductList
+                  .map((x) => ({
+                    createTime: x.createTime,
+                    price: x.price,
+                  }))
+                  .filter((y, index) => index < 3);
+            }
+          }
+        }
+      });
+  }
+};
 const changeSupplier = (val) => {
+  changeProductPrice();
   formData.data.countryId = "";
   formData.data.provinceId = "";
   formData.data.cityId = "";
@@ -1220,6 +1313,7 @@ onMounted(() => {
               }
             }
           });
+        changeProductPrice();
       } else {
         formData.data.purchaseProductList = [];
       }
@@ -1285,6 +1379,7 @@ onMounted(() => {
               }
             }
           });
+        changeProductPrice();
       }
       if (formData.data.countryId) {
         getCityData(formData.data.countryId, "20");
@@ -1369,6 +1464,7 @@ onMounted(() => {
               }
             }
           });
+        changeProductPrice();
       }
       if (formData.data.countryId) {
         getCityData(formData.data.countryId, "20");
@@ -1730,6 +1826,69 @@ const remoteMethod = (keyword) => {
   }
   return;
 };
+
+const optionTwo = reactive({
+  data: {
+    tooltip: {
+      trigger: "axis",
+    },
+    // legend: {
+    //   data: ["价格"],
+    // },
+    grid: {
+      left: "3%",
+      right: "4%",
+      top: "10%",
+      bottom: "3%",
+      containLabel: true,
+    },
+    // toolbox: {
+    //   feature: {
+    //     saveAsImage: {},
+    //   },
+    // },
+    xAxis: {
+      type: "category",
+      boundaryGap: false,
+      data: [],
+    },
+    yAxis: {
+      type: "value",
+    },
+    series: [
+      {
+        name: "价格",
+        type: "line",
+        data: [],
+      },
+    ],
+  },
+});
+const showEcharts = (row, index) => {
+  let myChart = null;
+  myChart = echarts.init(document.getElementById(row.productId + index));
+  window.addEventListener("resize", () => {
+    myChart.resize();
+  });
+  if (
+    row.purchaseProductPriceListOne &&
+    row.purchaseProductPriceListOne.length > 0
+  ) {
+    optionTwo.data.xAxis.data = row.purchaseProductPriceListOne.map((item) => {
+      return item.createTime.slice(0, 10);
+    });
+    optionTwo.data.series[0].data = row.purchaseProductPriceListOne.map(
+      (item) => {
+        return item.price;
+      }
+    );
+  } else {
+    optionTwo.data.xAxis.data = [];
+    optionTwo.data.series[0].data = [];
+  }
+  myChart.setOption(optionTwo.data);
+  myChart.resize();
+};
 </script>
 
 <style lang="scss" scoped>
@@ -1774,4 +1933,10 @@ const remoteMethod = (keyword) => {
     padding-right: 0 !important;
   }
 }
+.img {
+  object-fit: contain;
+  width: 16px;
+  height: 16px;
+  vertical-align: middle;
+}
 </style>

+ 1 - 1
src/utils/request.js

@@ -92,7 +92,7 @@ service.interceptors.request.use(config => {
       const s_url = sessionObj.url; // 请求地址
       const s_data = sessionObj.data; // 请求数据
       const s_time = sessionObj.time; // 请求时间
-      const interval = 500; // 间隔时间(ms),小于此时间视为重复提交
+      const interval = 300; // 间隔时间(ms),小于此时间视为重复提交
       if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
         const message = '数据正在处理,请勿重复提交';
         // console.warn(`[${s_url}]: ` + message)

+ 250 - 87
src/views/customer/file/index.vue

@@ -1,106 +1,182 @@
 <template>
   <div class="tenant">
-    <div style="padding: 20px; background: #fff; margin-bottom: 20px">
+    <!-- <div style="padding: 20px; background: #fff; margin-bottom: 20px">
       <el-button type="primary" style="margin-left: 10px" @click="openModal()">添加客户</el-button>
-    </div>
+    </div> -->
 
-    <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="statConfig"
-             :selectConfig="selectConfig" highlight-current-row @moreSearch="moreSearch" @get-list="getList" ref="table">
-      <template #isTop="{ item }">
-        <div>
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
-               v-if="item.isTop === 1" />
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
-        </div>
-      </template>
-      <template #address="{ item }">
-        <span>{{ item.countryName }}</span>
-        <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
-        <span v-if="item.cityName"> ,{{ item.cityName }}</span>
-      </template>
-      <template #name="{ item }">
-        <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
-          {{ item.name }}
+    <div style="display:flex;height:calc(100vh - 140px);">
+      <div style="width:240px;height:100%;overflow:auto;background:#fff;padding:10px">
+        <div style="margin-bottom:10px">
+          <el-button type="primary" style="width:100%" @click="handleSearch()">全部客户</el-button>
         </div>
-      </template>
-      <template #tags="{ item }">
-        <div style="width: 100%">
-          <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
-            {{ dictValueLabel(tag, customerTag) }}
-          </el-tag>
-          <template v-if="item.tag.length !== customerTag.length">
-            <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
+        <el-collapse v-model="activeNames">
+          <el-collapse-item title="客户跟进" name="1">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-1.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户跟进
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" @click="handleSearchOne(1)">
+                <span>超7天未跟进</span> <span>{{leftDataFour[7]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(2)">
+                <span>超30天未跟进</span> <span>{{leftDataFour[30]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(3)">
+                <span>超半年未跟进</span> <span>{{leftDataFour[180]}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户来源" name="2">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-2.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户来源
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerSource" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'source')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'source')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户类型" name="3">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-4.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户类型
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerStatus" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'status')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'status')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户标签" name="4">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-3.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户标签
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerTag" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'tag')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'tag')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div style="width:10px"></div>
+      <div style="width:calc(100% - 250px);height:100%;overflow:auto">
+        <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="[]"
+                 :selectConfig="selectConfig" highlight-current-row :action-list="[
+          {
+            text: '添加客户',
+            action: () => openModal(),
+          },
+        ]" @moreSearch="moreSearch" @get-list="getList" ref="table">
+          <template #isTop="{ item }">
+            <div>
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
+                   v-if="item.isTop === 1" />
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
+            </div>
+          </template>
+          <template #address="{ item }">
+            <span>{{ item.countryName }}</span>
+            <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+            <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+          </template>
+          <template #name="{ item }">
+            <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
+              {{ item.name }}
+            </div>
+          </template>
+          <template #tags="{ item }">
+            <div style="width: 100%">
+              <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
+                {{ dictValueLabel(tag, customerTag) }}
+              </el-tag>
+              <template v-if="item.tag.length !== customerTag.length">
+                <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
                 (val) => {
                   return changeTag(val, item);
                 }
               ">
-              <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
-                         :disabled="judgeTagSelect(item.tag, tag.value)" />
-            </el-select>
-            <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
-              +
-            </el-tag>
+                  <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
+                             :disabled="judgeTagSelect(item.tag, tag.value)" />
+                </el-select>
+                <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
+                  +
+                </el-tag>
+              </template>
+            </div>
           </template>
-        </div>
-      </template>
-      <template #follow="{ item }">
-        <div :class="'getWidth' + item.id" style="width: 100%">
-          <div style="width: 100%; display: flex">
-            <template v-if="
+          <template #follow="{ item }">
+            <div :class="'getWidth' + item.id" style="width: 100%">
+              <div style="width: 100%; display: flex">
+                <template v-if="
                 item.customerFollowRecordsList &&
                 item.customerFollowRecordsList.length > 0
               ">
-              <div :style="
+                  <div :style="
                   index > 2
                     ? 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer; display: none'
                     : 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer'
                 " v-for="(record, index) in item.customerFollowRecordsList" :key="record.id">
-                <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
-                  <template #reference>
-                    <div>
-                      <span v-if="record.date">{{
+                    <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
+                      <template #reference>
+                        <div>
+                          <span v-if="record.date">{{
                         record.date.substr(0, 10)
                       }}</span>
-                      <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
-                        <DeleteFilled />
-                      </el-icon>
-                    </div>
-                  </template>
-                  <template #default>
-                    <div style="width: 100%">
-                      <div style="color: #909399; margin: 8px 0">
-                        跟进时间: {{ record.date }}
-                      </div>
-                      <div style="margin-top: 8px">
-                        跟进人:
-                        {{ dictValueLabel(record.createUser, userList) }}
-                      </div>
-                      <!-- <div style="margin-top: 8px">
+                          <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
+                            <DeleteFilled />
+                          </el-icon>
+                        </div>
+                      </template>
+                      <template #default>
+                        <div style="width: 100%">
+                          <div style="color: #909399; margin: 8px 0">
+                            跟进时间: {{ record.date }}
+                          </div>
+                          <div style="margin-top: 8px">
+                            跟进人:
+                            {{ dictValueLabel(record.createUser, userList) }}
+                          </div>
+                          <!-- <div style="margin-top: 8px">
                         跟进内容: {{ record.content }}
                       </div> -->
 
-                      <div v-if="record.type == '30'">
-                        <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
-                        <div v-else>跟进记录:</div>
-                      </div>
-                      <div v-else>
-                        <div style="word-wrap: break-word; margin: 8px 0">
-                          {{ getContent(record) }}
-                        </div>
-                      </div>
-                      <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
-                        <div style="width: 36px">附件:</div>
-                        <div style="width: calc(100% - 36px)">
-                          <div v-for="(file, index) in record.fileList" :key="index">
-                            <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                          <div v-if="record.type == '30'">
+                            <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
+                            <div v-else>跟进记录:</div>
+                          </div>
+                          <div v-else>
+                            <div style="word-wrap: break-word; margin: 8px 0">
+                              {{ getContent(record) }}
+                            </div>
+                          </div>
+                          <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
+                            <div style="width: 36px">附件:</div>
+                            <div style="width: calc(100% - 36px)">
+                              <div v-for="(file, index) in record.fileList" :key="index">
+                                <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                              </div>
+                            </div>
                           </div>
                         </div>
-                      </div>
-                    </div>
-                  </template>
-                </el-popover>
-              </div>
-              <div style="
+                      </template>
+                    </el-popover>
+                  </div>
+                  <div style="
                   line-height: 32px;
                   margin-right: 8px;
                   padding: 0 8px;
@@ -108,13 +184,15 @@
                   border-radius: 4px;
                   cursor: pointer;
                 " @click="clickMore(item)" v-if="item.customerFollowRecordsList.length >= 3">
-                更多
+                    更多
+                  </div>
+                </template>
               </div>
-            </template>
-          </div>
-        </div>
-      </template>
-    </byTable>
+            </div>
+          </template>
+        </byTable>
+      </div>
+    </div>
 
     <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-if="dialogVisible" v-model="dialogVisible" width="800" v-loading="loadingOperation">
       <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
@@ -373,6 +451,7 @@ import useUserStore from "@/store/modules/user";
 import selectCity from "@/components/selectCity/index.vue";
 
 const { proxy } = getCurrentInstance();
+const activeNames = ref(["1", "2", "3", "4"]);
 const loading = ref(false);
 const loadingOperation = ref(false);
 const submitLoading = ref(false);
@@ -1296,14 +1375,98 @@ const statisticalData = ref({
   countAmount: 0,
   customerList: [],
 });
+const leftData = ref({});
+const leftDataFour = ref({
+  0: 0,
+  7: 0,
+  180: 0,
+});
+
 const obtainStatisticalData = () => {
   proxy
-    .post("/customer/sourceStatistics", sourceList.value.paginationTwo)
+    .post("/customer/sourceStatistics", { statisticsType: 1, type: null })
+    .then((res) => {
+      leftData.value.source = res;
+    });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 2, type: null })
+    .then((res) => {
+      leftData.value.status = res;
+    });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 4, type: null })
     .then((res) => {
-      statisticalData.value = res;
+      leftData.value.tag = res;
     });
+  proxy.post("/customer/followStatistics", { type: null }).then((res) => {
+    leftDataFour.value = res;
+  });
 };
 obtainStatisticalData();
+const showLeftData = (dictKey, att) => {
+  const data = leftData.value[att];
+  if (data && data.customerList.length > 0) {
+    const current = data.customerList.find((x) => x[att] == dictKey);
+    if (current && current.count) {
+      return current.count;
+    } else {
+      return 0;
+    }
+  }
+};
+const handleSearch = (value, att) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (value || att) {
+    sourceList.value.pagination[att] = value;
+  }
+  getList();
+};
+const handleSearchOne = (type) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (type == 1) {
+    sourceList.value.pagination.beginDay = 7;
+    sourceList.value.pagination.endDay = 30;
+  } else if (type == 2) {
+    sourceList.value.pagination.beginDay = 30;
+    sourceList.value.pagination.endDay = 180;
+  } else {
+    sourceList.value.pagination.beginDay = 180;
+    sourceList.value.pagination.endDay = "";
+  }
+  getList();
+};
 const getNum = (val) => {
   let num = 0;
   if (

+ 248 - 85
src/views/customer/highseas/index.vue

@@ -1,102 +1,178 @@
 <template>
   <div class="tenant">
-    <div style="padding: 20px; background: #fff; margin-bottom: 20px">
+    <!-- <div style="padding: 20px; background: #fff; margin-bottom: 20px">
       <el-button type="primary" style="margin-left: 10px" @click="openModal()">添加客户</el-button>
-    </div>
+    </div> -->
 
-    <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="statConfig"
-             :selectConfig="selectConfig" highlight-current-row @moreSearch="moreSearch" @get-list="getList" ref="table">
-      <template #isTop="{ item }">
-        <div>
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
-               v-if="item.isTop === 1" />
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
-        </div>
-      </template>
-      <template #address="{ item }">
-        <span>{{ item.countryName }}</span>
-        <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
-        <span v-if="item.cityName"> ,{{ item.cityName }}</span>
-      </template>
-      <template #name="{ item }">
-        <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
-          {{ item.name }}
+    <div style="display:flex;height:calc(100vh - 140px);">
+      <div style="width:240px;height:100%;overflow:auto;background:#fff;padding:10px">
+        <div style="margin-bottom:10px">
+          <el-button type="primary" style="width:100%" @click="handleSearch()">全部客户</el-button>
         </div>
-      </template>
-      <template #tags="{ item }">
-        <div style="width: 100%">
-          <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
-            {{ dictValueLabel(tag, customerTag) }}
-          </el-tag>
-          <template v-if="item.tag.length !== customerTag.length">
-            <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
+        <el-collapse v-model="activeNames">
+          <el-collapse-item title="客户跟进" name="1">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-1.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户跟进
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" @click="handleSearchOne(1)">
+                <span>超7天未跟进</span> <span>{{leftDataFour[7]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(2)">
+                <span>超30天未跟进</span> <span>{{leftDataFour[30]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(3)">
+                <span>超半年未跟进</span> <span>{{leftDataFour[180]}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户来源" name="2">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-2.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户来源
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerSource" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'source')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'source')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户类型" name="3">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-4.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户类型
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerStatus" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'status')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'status')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户标签" name="4">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-3.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户标签
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerTag" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'tag')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'tag')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div style="width:10px"></div>
+      <div style="width:calc(100% - 250px);height:100%;overflow:auto">
+        <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="[]"
+                 :selectConfig="selectConfig" highlight-current-row :action-list="[
+          {
+            text: '添加客户',
+            action: () => openModal(),
+          },
+        ]" @moreSearch="moreSearch" @get-list="getList" ref="table">
+          <template #isTop="{ item }">
+            <div>
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
+                   v-if="item.isTop === 1" />
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
+            </div>
+          </template>
+          <template #address="{ item }">
+            <span>{{ item.countryName }}</span>
+            <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+            <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+          </template>
+          <template #name="{ item }">
+            <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
+              {{ item.name }}
+            </div>
+          </template>
+          <template #tags="{ item }">
+            <div style="width: 100%">
+              <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
+                {{ dictValueLabel(tag, customerTag) }}
+              </el-tag>
+              <template v-if="item.tag.length !== customerTag.length">
+                <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
                 (val) => {
                   return changeTag(val, item);
                 }
               ">
-              <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
-                         :disabled="judgeTagSelect(item.tag, tag.value)" />
-            </el-select>
-            <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
-              +
-            </el-tag>
+                  <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
+                             :disabled="judgeTagSelect(item.tag, tag.value)" />
+                </el-select>
+                <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
+                  +
+                </el-tag>
+              </template>
+            </div>
           </template>
-        </div>
-      </template>
-      <template #follow="{ item }">
-        <div :class="'getWidth' + item.id" style="width: 100%">
-          <div style="width: 100%; display: flex">
-            <template v-if="
+          <template #follow="{ item }">
+            <div :class="'getWidth' + item.id" style="width: 100%">
+              <div style="width: 100%; display: flex">
+                <template v-if="
                 item.customerFollowRecordsList &&
                 item.customerFollowRecordsList.length > 0
               ">
-              <div :style="
+                  <div :style="
                   index > 2
                     ? 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer; display: none'
                     : 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer'
                 " v-for="(record, index) in item.customerFollowRecordsList" :key="record.id">
-                <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
-                  <template #reference>
-                    <div>
-                      <span v-if="record.date">{{
+                    <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
+                      <template #reference>
+                        <div>
+                          <span v-if="record.date">{{
                         record.date.substr(0, 10)
                       }}</span>
-                      <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
-                        <DeleteFilled />
-                      </el-icon>
-                    </div>
-                  </template>
-                  <template #default>
-                    <div style="width: 100%">
-                      <div style="color: #909399; margin: 8px 0">
-                        跟进时间: {{ record.date }}
-                      </div>
-                      <div style="margin: 8px 0">
-                        跟进人:
-                        {{ dictValueLabel(record.createUser, userList) }}
-                      </div>
-                      <div v-if="record.type == '30'">
-                        <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
-                        <div v-else>跟进记录:</div>
-                      </div>
-                      <div v-else>
-                        <div style="word-wrap: break-word; margin: 8px 0">
-                          {{ getContent(record) }}
+                          <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
+                            <DeleteFilled />
+                          </el-icon>
                         </div>
-                      </div>
-                      <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
-                        <div style="width: 36px">附件:</div>
-                        <div style="width: calc(100% - 36px)">
-                          <div v-for="(file, index) in record.fileList" :key="index">
-                            <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                      </template>
+                      <template #default>
+                        <div style="width: 100%">
+                          <div style="color: #909399; margin: 8px 0">
+                            跟进时间: {{ record.date }}
+                          </div>
+                          <div style="margin: 8px 0">
+                            跟进人:
+                            {{ dictValueLabel(record.createUser, userList) }}
+                          </div>
+                          <div v-if="record.type == '30'">
+                            <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
+                            <div v-else>跟进记录:</div>
+                          </div>
+                          <div v-else>
+                            <div style="word-wrap: break-word; margin: 8px 0">
+                              {{ getContent(record) }}
+                            </div>
+                          </div>
+                          <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
+                            <div style="width: 36px">附件:</div>
+                            <div style="width: calc(100% - 36px)">
+                              <div v-for="(file, index) in record.fileList" :key="index">
+                                <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                              </div>
+                            </div>
                           </div>
                         </div>
-                      </div>
-                    </div>
-                  </template>
-                </el-popover>
-              </div>
-              <div style="
+                      </template>
+                    </el-popover>
+                  </div>
+                  <div style="
                   line-height: 32px;
                   margin-right: 8px;
                   padding: 0 8px;
@@ -104,13 +180,15 @@
                   border-radius: 4px;
                   cursor: pointer;
                 " @click="clickMore(item)" v-if="item.customerFollowRecordsList.length >= 3">
-                更多
+                    更多
+                  </div>
+                </template>
               </div>
-            </template>
-          </div>
-        </div>
-      </template>
-    </byTable>
+            </div>
+          </template>
+        </byTable>
+      </div>
+    </div>
 
     <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-if="dialogVisible" v-model="dialogVisible" width="800" v-loading="loadingOperation">
       <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
@@ -360,6 +438,7 @@ import useUserStore from "@/store/modules/user";
 import selectCity from "@/components/selectCity/index.vue";
 
 const { proxy } = getCurrentInstance();
+const activeNames = ref(["1", "2", "3", "4"]);
 const loading = ref(false);
 const loadingOperation = ref(false);
 const submitLoading = ref(false);
@@ -1197,14 +1276,98 @@ const statisticalData = ref({
   countAmount: 0,
   customerList: [],
 });
+const leftData = ref({});
+const leftDataFour = ref({
+  0: 0,
+  7: 0,
+  180: 0,
+});
+
 const obtainStatisticalData = () => {
   proxy
-    .post("/customer/sourceStatistics", sourceList.value.paginationTwo)
+    .post("/customer/sourceStatistics", { statisticsType: 1, type: "0" })
+    .then((res) => {
+      leftData.value.source = res;
+    });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 2, type: "0" })
     .then((res) => {
-      statisticalData.value = res;
+      leftData.value.status = res;
     });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 4, type: "0" })
+    .then((res) => {
+      leftData.value.tag = res;
+    });
+  proxy.post("/customer/followStatistics", { type: "0" }).then((res) => {
+    leftDataFour.value = res;
+  });
 };
 obtainStatisticalData();
+const showLeftData = (dictKey, att) => {
+  const data = leftData.value[att];
+  if (data && data.customerList.length > 0) {
+    const current = data.customerList.find((x) => x[att] == dictKey);
+    if (current && current.count) {
+      return current.count;
+    } else {
+      return 0;
+    }
+  }
+};
+const handleSearch = (value, att) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (value || att) {
+    sourceList.value.pagination[att] = value;
+  }
+  getList();
+};
+const handleSearchOne = (type) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (type == 1) {
+    sourceList.value.pagination.beginDay = 7;
+    sourceList.value.pagination.endDay = 30;
+  } else if (type == 2) {
+    sourceList.value.pagination.beginDay = 30;
+    sourceList.value.pagination.endDay = 180;
+  } else {
+    sourceList.value.pagination.beginDay = 180;
+    sourceList.value.pagination.endDay = "";
+  }
+  getList();
+};
 const getNum = (val) => {
   let num = 0;
   if (

+ 248 - 85
src/views/customer/privatesea/index.vue

@@ -1,102 +1,178 @@
 <template>
   <div class="tenant">
-    <div style="padding: 20px; background: #fff; margin-bottom: 20px">
+    <!-- <div style="padding: 20px; background: #fff; margin-bottom: 20px">
       <el-button type="primary" style="margin-left: 10px" @click="openModal()">添加客户</el-button>
-    </div>
+    </div> -->
 
-    <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="statConfig"
-             :selectConfig="selectConfig" highlight-current-row @moreSearch="moreSearch" @get-list="getList" ref="table">
-      <template #isTop="{ item }">
-        <div>
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
-               v-if="item.isTop === 1" />
-          <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
-        </div>
-      </template>
-      <template #address="{ item }">
-        <span>{{ item.countryName }}</span>
-        <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
-        <span v-if="item.cityName"> ,{{ item.cityName }}</span>
-      </template>
-      <template #name="{ item }">
-        <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
-          {{ item.name }}
+    <div style="display:flex;height:calc(100vh - 140px);">
+      <div style="width:240px;height:100%;overflow:auto;background:#fff;padding:10px">
+        <div style="margin-bottom:10px">
+          <el-button type="primary" style="width:100%" @click="handleSearch()">全部客户</el-button>
         </div>
-      </template>
-      <template #tags="{ item }">
-        <div style="width: 100%">
-          <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
-            {{ dictValueLabel(tag, customerTag) }}
-          </el-tag>
-          <template v-if="item.tag.length !== customerTag.length">
-            <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
+        <el-collapse v-model="activeNames">
+          <el-collapse-item title="客户跟进" name="1">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-1.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户跟进
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" @click="handleSearchOne(1)">
+                <span>超7天未跟进</span> <span>{{leftDataFour[7]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(2)">
+                <span>超30天未跟进</span> <span>{{leftDataFour[30]}}</span>
+              </div>
+              <div style="display:flex;justify-content:space-between;cursor:pointer;margin-top:5px" @click="handleSearchOne(3)">
+                <span>超半年未跟进</span> <span>{{leftDataFour[180]}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户来源" name="2">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-2.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户来源
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerSource" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'source')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'source')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户类型" name="3">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-4.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户类型
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerStatus" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'status')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'status')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+          <el-collapse-item title="客户标签" name="4">
+            <template #title>
+              <div>
+                <img src="@/assets/images/custom-3.png" alt="" style="object-fit: contain;width: 20px;height: 20px;vertical-align: middle;" />
+                客户标签
+              </div>
+            </template>
+            <div style="padding-left:25px;padding-right:10px">
+              <div style="display:flex;justify-content:space-between;cursor:pointer" v-for="(item,index) in customerTag" :key="item.value"
+                   :style="{marginTop:index!=0?'15px':'0px'}" @click="handleSearch(item.value,'tag')">
+                <span>{{item.label}}</span> <span>{{showLeftData(item.value,'tag')}}</span>
+              </div>
+            </div>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div style="width:10px"></div>
+      <div style="width:calc(100% - 250px);height:100%;overflow:auto">
+        <byTable :source="sourceList.data" :pagination="sourceList.pagination" :config="config" :loading="loading" :statConfig="[]"
+                 :selectConfig="selectConfig" highlight-current-row :action-list="[
+          {
+            text: '添加客户',
+            action: () => openModal(),
+          },
+        ]" @moreSearch="moreSearch" @get-list="getList" ref="table">
+          <template #isTop="{ item }">
+            <div>
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/isTop.png'" @click="deleteTop(item)"
+                   v-if="item.isTop === 1" />
+              <img style="cursor: pointer; width: 20px; transform: translateY(5px)" :src="'/img/noTop.png'" @click="addTop(item)" v-else />
+            </div>
+          </template>
+          <template #address="{ item }">
+            <span>{{ item.countryName }}</span>
+            <span v-if="item.provinceName"> ,{{ item.provinceName }}</span>
+            <span v-if="item.cityName"> ,{{ item.cityName }}</span>
+          </template>
+          <template #name="{ item }">
+            <div style="cursor: pointer; color: #409eff; word-break: break-all" @click="handleClickName(item)">
+              {{ item.name }}
+            </div>
+          </template>
+          <template #tags="{ item }">
+            <div style="width: 100%">
+              <el-tag style="margin-right: 8px" type="success" v-for="(tag, index) in item.tag" closable :key="index" @close="tagClose(tag, item)">
+                {{ dictValueLabel(tag, customerTag) }}
+              </el-tag>
+              <template v-if="item.tag.length !== customerTag.length">
+                <el-select v-if="item.addTagShow" v-model="addTag" style="width: 100%" @change="
                 (val) => {
                   return changeTag(val, item);
                 }
               ">
-              <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
-                         :disabled="judgeTagSelect(item.tag, tag.value)" />
-            </el-select>
-            <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
-              +
-            </el-tag>
+                  <el-option v-for="tag in customerTag" :key="tag.value" :label="tag.label" :value="tag.value"
+                             :disabled="judgeTagSelect(item.tag, tag.value)" />
+                </el-select>
+                <el-tag style="cursor: pointer" type="success" @click="showSelect(item)" v-else>
+                  +
+                </el-tag>
+              </template>
+            </div>
           </template>
-        </div>
-      </template>
-      <template #follow="{ item }">
-        <div :class="'getWidth' + item.id" style="width: 100%">
-          <div style="width: 100%; display: flex">
-            <template v-if="
+          <template #follow="{ item }">
+            <div :class="'getWidth' + item.id" style="width: 100%">
+              <div style="width: 100%; display: flex">
+                <template v-if="
                 item.customerFollowRecordsList &&
                 item.customerFollowRecordsList.length > 0
               ">
-              <div :style="
+                  <div :style="
                   index > 2
                     ? 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer; display: none'
                     : 'line-height: 32px; margin-right: 8px; padding: 0 8px; background-color: #eeeeee; border-radius: 4px; cursor: pointer'
                 " v-for="(record, index) in item.customerFollowRecordsList" :key="record.id">
-                <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
-                  <template #reference>
-                    <div>
-                      <span v-if="record.date">{{
+                    <el-popover placement="bottom" :width="300" trigger="hover" @show="recordShow(record)">
+                      <template #reference>
+                        <div>
+                          <span v-if="record.date">{{
                         record.date.substr(0, 10)
                       }}</span>
-                      <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
-                        <DeleteFilled />
-                      </el-icon>
-                    </div>
-                  </template>
-                  <template #default>
-                    <div style="width: 100%">
-                      <div style="color: #909399; margin: 8px 0">
-                        跟进时间: {{ record.date }}
-                      </div>
-                      <div style="margin: 8px 0">
-                        跟进人:
-                        {{ dictValueLabel(record.createUser, userList) }}
-                      </div>
-                      <div v-if="record.type == '30'">
-                        <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
-                        <div v-else>跟进记录:</div>
-                      </div>
-                      <div v-else>
-                        <div style="word-wrap: break-word; margin: 8px 0">
-                          {{ getContent(record) }}
+                          <el-icon style="margin-left: 8px; transform: translateY(2px)" @click="deleteFollow(record)">
+                            <DeleteFilled />
+                          </el-icon>
                         </div>
-                      </div>
-                      <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
-                        <div style="width: 36px">附件:</div>
-                        <div style="width: calc(100% - 36px)">
-                          <div v-for="(file, index) in record.fileList" :key="index">
-                            <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                      </template>
+                      <template #default>
+                        <div style="width: 100%">
+                          <div style="color: #909399; margin: 8px 0">
+                            跟进时间: {{ record.date }}
+                          </div>
+                          <div style="margin: 8px 0">
+                            跟进人:
+                            {{ dictValueLabel(record.createUser, userList) }}
+                          </div>
+                          <div v-if="record.type == '30'">
+                            <div style="word-wrap: break-word; margin: 8px 0" v-html="getStyle(record.content)" v-if="record.content"></div>
+                            <div v-else>跟进记录:</div>
+                          </div>
+                          <div v-else>
+                            <div style="word-wrap: break-word; margin: 8px 0">
+                              {{ getContent(record) }}
+                            </div>
+                          </div>
+                          <div style="margin: 8px 0; display: flex" v-if="record.fileList && record.fileList.length > 0">
+                            <div style="width: 36px">附件:</div>
+                            <div style="width: calc(100% - 36px)">
+                              <div v-for="(file, index) in record.fileList" :key="index">
+                                <a style="color: #409eff; cursor: pointer" @click="openFile(file.fileUrl)">{{ file.fileName }}</a>
+                              </div>
+                            </div>
                           </div>
                         </div>
-                      </div>
-                    </div>
-                  </template>
-                </el-popover>
-              </div>
-              <div style="
+                      </template>
+                    </el-popover>
+                  </div>
+                  <div style="
                   line-height: 32px;
                   margin-right: 8px;
                   padding: 0 8px;
@@ -104,13 +180,15 @@
                   border-radius: 4px;
                   cursor: pointer;
                 " @click="clickMore(item)" v-if="item.customerFollowRecordsList.length >= 3">
-                更多
+                    更多
+                  </div>
+                </template>
               </div>
-            </template>
-          </div>
-        </div>
-      </template>
-    </byTable>
+            </div>
+          </template>
+        </byTable>
+      </div>
+    </div>
 
     <el-dialog :title="modalType == 'add' ? '新增' : '编辑'" v-if="dialogVisible" v-model="dialogVisible" width="800" v-loading="loadingOperation">
       <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
@@ -360,6 +438,7 @@ import useUserStore from "@/store/modules/user";
 import selectCity from "@/components/selectCity/index.vue";
 
 const { proxy } = getCurrentInstance();
+const activeNames = ref(["1", "2", "3", "4"]);
 const route = useRoute();
 const loading = ref(false);
 const loadingOperation = ref(false);
@@ -1199,14 +1278,98 @@ const statisticalData = ref({
   countAmount: 0,
   customerList: [],
 });
+const leftData = ref({});
+const leftDataFour = ref({
+  0: 0,
+  7: 0,
+  180: 0,
+});
+
 const obtainStatisticalData = () => {
   proxy
-    .post("/customer/sourceStatistics", sourceList.value.paginationTwo)
+    .post("/customer/sourceStatistics", { statisticsType: 1, type: "1" })
+    .then((res) => {
+      leftData.value.source = res;
+    });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 2, type: "1" })
     .then((res) => {
-      statisticalData.value = res;
+      leftData.value.status = res;
     });
+  proxy
+    .post("/customer/sourceStatistics", { statisticsType: 4, type: "1" })
+    .then((res) => {
+      leftData.value.tag = res;
+    });
+  proxy.post("/customer/followStatistics", { type: "1" }).then((res) => {
+    leftDataFour.value = res;
+  });
 };
 obtainStatisticalData();
+const showLeftData = (dictKey, att) => {
+  const data = leftData.value[att];
+  if (data && data.customerList.length > 0) {
+    const current = data.customerList.find((x) => x[att] == dictKey);
+    if (current && current.count) {
+      return current.count;
+    } else {
+      return 0;
+    }
+  }
+};
+const handleSearch = (value, att) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (value || att) {
+    sourceList.value.pagination[att] = value;
+  }
+  getList();
+};
+const handleSearchOne = (type) => {
+  sourceList.value.pagination = {
+    total: 0,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+    type: "",
+    source: "",
+    status: "",
+    name: "",
+    countryId: "",
+    provinceId: "",
+    cityId: "",
+    customerCode: "",
+    userId: "",
+    tag: "",
+    tags: [],
+  };
+  if (type == 1) {
+    sourceList.value.pagination.beginDay = 7;
+    sourceList.value.pagination.endDay = 30;
+  } else if (type == 2) {
+    sourceList.value.pagination.beginDay = 30;
+    sourceList.value.pagination.endDay = 180;
+  } else {
+    sourceList.value.pagination.beginDay = 180;
+    sourceList.value.pagination.endDay = "";
+  }
+  getList();
+};
 const getNum = (val) => {
   let num = 0;
   if (

+ 3 - 3
src/views/process/processApproval/index.vue

@@ -129,7 +129,7 @@
                 <el-button type="primary" v-if="approvalRecordData.buttonInfoList.length == 0" @click="handleSubmit"
                            :loading="btnLoading">提交</el-button>
                 <el-button type="primary" v-else v-for="i in approvalRecordData.buttonInfoList" :key="i.type" :loading="btnLoading"
-                           @click="handleSubmit(i.type)">{{ i.name }}</el-button>
+                           @click="handleSubmit(i.type)" style="margin-bottom:10px">{{ i.name }}</el-button>
               </el-form-item>
             </el-form>
           </div>
@@ -742,7 +742,7 @@ const getFlowName = () => {
     border-radius: 4px;
     // padding: 20px;
     // flex: 1;
-    width: calc(100% - 400px - 20px);
+    width: calc(100% - 350px - 20px);
     margin-right: 20px;
     display: flex;
     flex-direction: column;
@@ -770,7 +770,7 @@ const getFlowName = () => {
     background: #fff;
     border-radius: 4px;
     padding: 0 20px 20px;
-    width: 400px;
+    width: 350px;
     box-sizing: border-box;
     .flow-chart {
       overflow: auto;

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio