Selaa lähdekoodia

feature(客户详情):客户详情联调,代码格式化

wanglj 3 vuotta sitten
vanhempi
commit
a5f4e63d6f

+ 47 - 0
src/api/customer/follow.js

@@ -0,0 +1,47 @@
+/*
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2022-12-29 09:40:35
+ * @LastEditors: wanglj
+ * @LastEditTime: 2022-12-29 16:17:12
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\api\customer\follow.js
+ */
+import micro_request from '@/utils/micro_request'
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  // 跟进记录列表
+  getList(query) {
+    return micro_request.postRequest(basePath, 'FollowUp', 'GetList', query)
+  },
+  // 跟进记录详情
+  getListByDay(query) {
+    return micro_request.postRequest(
+      basePath,
+      'FollowUp',
+      'GetListByDay',
+      query
+    )
+  },
+  // 添加跟进记录
+  addFollowUp(query) {
+    return micro_request.postRequest(basePath, 'FollowUp', 'Create', query)
+  },
+  // 评论列表
+  getComment(query) {
+    return micro_request.postRequest(
+      basePath,
+      'FollowUpComment',
+      'GetList',
+      query
+    )
+  },
+  // 添加评论
+  addComment(query) {
+    return micro_request.postRequest(
+      basePath,
+      'FollowUpComment',
+      'Create',
+      query
+    )
+  },
+}

+ 13 - 4
src/api/customer/index.js

@@ -2,7 +2,7 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-27 11:10:39
  * @LastEditors: wanglj
- * @LastEditTime: 2022-12-28 14:49:40
+ * @LastEditTime: 2022-12-30 11:24:07
  * @Description: file content
  * @FilePath: \opms_frontend\src\api\customer\index.js
  */
@@ -18,6 +18,15 @@ export default {
       query
     )
   },
+  // 客户详情内容摘要
+  getAbstract(query) {
+    return micro_request.postRequest(
+      basePath,
+      'Customer',
+      'CustAbstract',
+      query
+    )
+  },
   // 客户编辑
   updateCostomer(query) {
     return micro_request.postRequest(basePath, 'Customer', 'UpdateById', query)
@@ -83,9 +92,9 @@ export default {
     return micro_request.postRequest(basePath, 'Customer', 'DeleteById', query)
   },
   // 客户领取
-  receiveCustomer(query) {
-    // return micro_request.postRequest(basePath, 'Contant', 'Create', query)
-  },
+  // receiveCustomer(query) {
+  // return micro_request.postRequest(basePath, 'Contant', 'Create', query)
+  // },
   // 省份
   getProvinceInfo(query) {
     return micro_request.postRequest(

+ 297 - 292
src/vab/components/VabColumnBar/index.vue

@@ -1,26 +1,29 @@
 <template>
-  <el-scrollbar class="vab-column-bar-container"
-                :class="{
+  <el-scrollbar
+    class="vab-column-bar-container"
+    :class="{
       'is-collapse': collapse,
       ['vab-column-bar-container-' + theme.columnStyle]: true,
     }">
     <vab-logo />
-    <el-tabs v-model="extra.first"
-             tab-position="left"
-             @tab-click="handleTabClick">
+    <el-tabs
+      v-model="extra.first"
+      tab-position="left"
+      @tab-click="handleTabClick">
       <template v-for="(route, index) in handleRoutes">
-        <el-tab-pane :key="index + route.name"
-                     :name="route.name">
+        <el-tab-pane :key="index + route.name" :name="route.name">
           <template slot="label">
-            <div class="vab-column-grid"
-                 :class="{
+            <div
+              class="vab-column-grid"
+              :class="{
                 ['vab-column-grid-' + theme.columnStyle]: true,
               }"
-                 :title="translateTitle(route.meta.title)">
+              :title="translateTitle(route.meta.title)">
               <div>
-                <vab-icon v-if="route.meta.icon"
-                          :icon="route.meta.icon"
-                          :is-custom-svg="route.meta.isCustomSvg" />
+                <vab-icon
+                  v-if="route.meta.icon"
+                  :icon="route.meta.icon"
+                  :is-custom-svg="route.meta.isCustomSvg" />
                 <span>
                   {{ translateTitle(route.meta.title) }}
                 </span>
@@ -31,373 +34,375 @@
       </template>
     </el-tabs>
 
-    <el-menu :background-color="variables['column-second-menu-background']"
-             :default-active="activeMenu"
-             :default-openeds="defaultOpeneds"
-             mode="vertical"
-             :unique-opened="uniqueOpened">
+    <el-menu
+      :background-color="variables['column-second-menu-background']"
+      :default-active="activeMenu"
+      :default-openeds="defaultOpeneds"
+      mode="vertical"
+      :unique-opened="uniqueOpened">
       <el-divider>
         {{ translateTitle(handleGroupTitle) }}
       </el-divider>
       <template v-for="route in handlePartialRoutes">
-        <vab-menu v-if="route.meta && !route.meta.hidden"
-                  :key="route.path"
-                  :item="route" />
+        <vab-menu
+          v-if="route.meta && !route.meta.hidden"
+          :key="route.path"
+          :item="route" />
       </template>
     </el-menu>
   </el-scrollbar>
 </template>
 
 <script>
-import { translateTitle } from '@/utils/i18n'
-import variables from '@/vab/styles/variables/variables.scss'
-import { mapActions, mapGetters } from 'vuex'
-import { defaultOpeneds, openFirstMenu, uniqueOpened } from '@/config'
-import { handleActivePath, handleMatched } from '@/utils/routes'
-
-export default {
-  name: 'VabColumnBar',
-  data () {
-    return {
-      activeMenu: '',
-      groupTitle: '',
-      defaultOpeneds,
-      uniqueOpened,
-      variables,
-    }
-  },
-  computed: {
-    ...mapGetters({
-      collapse: 'settings/collapse',
-      routes: 'routes/routes',
-      activeName: 'routes/activeName',
-      theme: 'settings/theme',
-      extra: 'settings/extra',
-    }),
-    handleRoutes () {
-      return this.routes.filter(
-        (route) => route.meta && route.meta.hidden !== true
-      )
-    },
-    handleActiveMenu () {
-      return this.routes.find((route) => route.name === this.extra.first)
+  import { translateTitle } from '@/utils/i18n'
+  import variables from '@/vab/styles/variables/variables.scss'
+  import { mapActions, mapGetters } from 'vuex'
+  import { defaultOpeneds, openFirstMenu, uniqueOpened } from '@/config'
+  import { handleActivePath, handleMatched } from '@/utils/routes'
+
+  export default {
+    name: 'VabColumnBar',
+    data() {
+      return {
+        activeMenu: '',
+        groupTitle: '',
+        defaultOpeneds,
+        uniqueOpened,
+        variables,
+      }
     },
-    handlePartialRoutes () {
-      const activeMenu = this.handleActiveMenu
-      return activeMenu ? activeMenu.children : []
+    computed: {
+      ...mapGetters({
+        collapse: 'settings/collapse',
+        routes: 'routes/routes',
+        activeName: 'routes/activeName',
+        theme: 'settings/theme',
+        extra: 'settings/extra',
+      }),
+      handleRoutes() {
+        return this.routes.filter(
+          (route) => route.meta && route.meta.hidden !== true
+        )
+      },
+      handleActiveMenu() {
+        return this.routes.find((route) => route.name === this.extra.first)
+      },
+      handlePartialRoutes() {
+        const activeMenu = this.handleActiveMenu
+        return activeMenu ? activeMenu.children : []
+      },
+      handleGroupTitle() {
+        const activeMenu = this.handleActiveMenu
+        return activeMenu ? activeMenu.meta.title : ''
+      },
     },
-    handleGroupTitle () {
-      const activeMenu = this.handleActiveMenu
-      return activeMenu ? activeMenu.meta.title : ''
+    watch: {
+      $route: {
+        handler(route) {
+          this.handleNoColumn()
+          this.activeMenu = handleActivePath(route)
+          const firstMenu = route.matched[0].name
+          if (this.extra.first !== firstMenu) {
+            this.extra.first = firstMenu
+            this.handleTabClick(true)
+          }
+        },
+        immediate: true,
+      },
+      activeName: {
+        handler(val) {
+          const matched = handleMatched(this.routes, val)
+          this.extra.first = matched[0].name
+          this.activeMenu = matched[matched.length - 1].path
+        },
+      },
     },
-  },
-  watch: {
-    $route: {
-      handler (route) {
+    methods: {
+      ...mapActions({
+        openSideBar: 'settings/openSideBar',
+        foldSideBar: 'settings/foldSideBar',
+      }),
+      translateTitle,
+      handleTabClick(handler) {
         this.handleNoColumn()
-        this.activeMenu = handleActivePath(route)
-        const firstMenu = route.matched[0].name
-        if (this.extra.first !== firstMenu) {
-          this.extra.first = firstMenu
-          this.handleTabClick(true)
-        }
+        if (handler !== true && openFirstMenu)
+          this.$router.push(this.handleActiveMenu)
       },
-      immediate: true,
-    },
-    activeName: {
-      handler (val) {
-        const matched = handleMatched(this.routes, val)
-        this.extra.first = matched[0].name
-        this.activeMenu = matched[matched.length - 1].path
+      handleNoColumn() {
+        this.$nextTick(() => {
+          if (this.theme.layout === 'column' && this.$route.meta.noColumn) {
+            this.foldSideBar()
+            if (document.querySelector('.fold-unfold'))
+              document.querySelector('.fold-unfold').style = 'display:none'
+          } else {
+            this.openSideBar()
+            if (document.querySelector('.fold-unfold'))
+              document.querySelector('.fold-unfold').style = ''
+          }
+        })
       },
     },
-  },
-  methods: {
-    ...mapActions({
-      openSideBar: 'settings/openSideBar',
-      foldSideBar: 'settings/foldSideBar',
-    }),
-    translateTitle,
-    handleTabClick (handler) {
-      this.handleNoColumn()
-      if (handler !== true && openFirstMenu)
-        this.$router.push(this.handleActiveMenu)
-    },
-    handleNoColumn () {
-      this.$nextTick(() => {
-        if (this.theme.layout === 'column' && this.$route.meta.noColumn) {
-          this.foldSideBar()
-          if (document.querySelector('.fold-unfold'))
-            document.querySelector('.fold-unfold').style = 'display:none'
-        } else {
-          this.openSideBar()
-          if (document.querySelector('.fold-unfold'))
-            document.querySelector('.fold-unfold').style = ''
-        }
-      })
-    },
-  },
-}
+  }
 </script>
 
 <style lang="scss" scoped>
-@mixin active {
-  &:hover {
-    color: $base-color-blue;
-    background-color: $base-column-second-menu-background-active !important;
-
-    i,
-    svg {
+  @mixin active {
+    &:hover {
       color: $base-color-blue;
+      background-color: $base-column-second-menu-background-active !important;
+
+      i,
+      svg {
+        color: $base-color-blue;
+      }
     }
-  }
 
-  &.is-active {
-    color: $base-color-blue;
-    background-color: $base-column-second-menu-background-active !important;
-  }
-}
-
-.vab-column-bar-container {
-  position: fixed;
-  top: 0;
-  bottom: 0;
-  left: 0;
-  width: $base-left-menu-width;
-  height: 100vh;
-  overflow: hidden;
-  background: $base-column-second-menu-background;
-  box-shadow: $base-box-shadow;
-
-  ::v-deep {
-    * {
-      transition: $base-transition;
+    &.is-active {
+      color: $base-color-blue;
+      background-color: $base-column-second-menu-background-active !important;
     }
   }
 
-  &-vertical,
-  &-card,
-  &-arrow {
+  .vab-column-bar-container {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    width: $base-left-menu-width;
+    height: 100vh;
+    overflow: hidden;
+    background: $base-column-second-menu-background;
+    box-shadow: $base-box-shadow;
+
     ::v-deep {
-      .el-tabs + .el-menu {
-        left: $base-left-menu-width-min;
-        width: $base-left-menu-width - $base-left-menu-width-min;
-        border: 0;
+      * {
+        transition: $base-transition;
       }
     }
-  }
 
-  &-horizontal {
-    ::v-deep {
-      .logo-container-column {
-        .logo {
-          width: $base-left-menu-width-min * 1.3 !important;
-        }
-
-        .title {
-          margin-left: $base-left-menu-width-min * 1.3 !important;
+    &-vertical,
+    &-card,
+    &-arrow {
+      ::v-deep {
+        .el-tabs + .el-menu {
+          left: $base-left-menu-width-min;
+          width: $base-left-menu-width - $base-left-menu-width-min;
+          border: 0;
         }
       }
-
-      .el-tabs + .el-menu {
-        left: $base-left-menu-width-min * 1.3;
-        width: $base-left-menu-width - $base-left-menu-width-min * 1.3;
-        border: 0;
-      }
     }
-  }
-
-  &-card {
-    ::v-deep {
-      .el-tabs {
-        .el-tabs__item {
-          padding: 5px !important;
 
-          .vab-column-grid {
-            width: $base-left-menu-width-min - 10 !important;
-            height: $base-left-menu-width-min - 10 !important;
-            border-radius: 5px;
+    &-horizontal {
+      ::v-deep {
+        .logo-container-column {
+          .logo {
+            width: $base-left-menu-width-min * 1.3 !important;
           }
 
-          &.is-active {
-            background: transparent !important;
-
-            .vab-column-grid {
-              background: $base-color-blue;
-            }
+          .title {
+            margin-left: $base-left-menu-width-min * 1.3 !important;
           }
         }
-      }
 
-      .el-tabs + .el-menu {
-        left: $base-left-menu-width-min + 10;
-        width: $base-left-menu-width - $base-left-menu-width-min - 20;
-      }
-
-      .el-submenu .el-submenu__title,
-      .el-menu-item {
-        min-width: 180px;
-        border-radius: 5px;
+        .el-tabs + .el-menu {
+          left: $base-left-menu-width-min * 1.3;
+          width: $base-left-menu-width - $base-left-menu-width-min * 1.3;
+          border: 0;
+        }
       }
     }
-  }
 
-  &-arrow {
-    ::v-deep {
-      .el-tabs {
-        .el-tabs__item {
-          &.is-active {
-            background: transparent !important;
+    &-card {
+      ::v-deep {
+        .el-tabs {
+          .el-tabs__item {
+            padding: 5px !important;
 
             .vab-column-grid {
+              width: $base-left-menu-width-min - 10 !important;
+              height: $base-left-menu-width-min - 10 !important;
+              border-radius: 5px;
+            }
+
+            &.is-active {
               background: transparent !important;
 
-              &:after {
-                position: absolute;
-                right: 0;
-                width: 0;
-                height: 0;
-                overflow: hidden;
-                content: '';
-                border-color: transparent #{$base-color-white} transparent transparent;
-                border-style: solid dashed dashed;
-                border-width: 8px;
+              .vab-column-grid {
+                background: $base-color-blue;
               }
             }
           }
         }
-      }
 
-      .el-tabs + .el-menu {
-        left: $base-left-menu-width-min + 10;
-        width: $base-left-menu-width - $base-left-menu-width-min - 20;
-      }
+        .el-tabs + .el-menu {
+          left: $base-left-menu-width-min + 10;
+          width: $base-left-menu-width - $base-left-menu-width-min - 20;
+        }
 
-      .el-submenu .el-submenu__title,
-      .el-menu-item {
-        min-width: 180px;
-        border-radius: 5px;
+        .el-submenu .el-submenu__title,
+        .el-menu-item {
+          min-width: 180px;
+          border-radius: 5px;
+        }
       }
     }
-  }
-
-  .vab-column-grid {
-    display: flex;
-    align-items: center;
-    width: $base-left-menu-width-min;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    word-break: break-all;
-    white-space: nowrap;
 
-    &-vertical,
-    &-card,
     &-arrow {
-      justify-content: center;
-      height: $base-left-menu-width-min;
-
-      > div {
-        svg {
-          position: relative;
-          top: 8px;
-          display: block;
-          width: $base-font-size-default + 4;
-          height: $base-font-size-default + 4;
+      ::v-deep {
+        .el-tabs {
+          .el-tabs__item {
+            &.is-active {
+              background: transparent !important;
+
+              .vab-column-grid {
+                background: transparent !important;
+
+                &:after {
+                  position: absolute;
+                  right: 0;
+                  width: 0;
+                  height: 0;
+                  overflow: hidden;
+                  content: '';
+                  border-color: transparent #{$base-color-white} transparent transparent;
+                  border-style: solid dashed dashed;
+                  border-width: 8px;
+                }
+              }
+            }
+          }
+        }
+
+        .el-tabs + .el-menu {
+          left: $base-left-menu-width-min + 10;
+          width: $base-left-menu-width - $base-left-menu-width-min - 20;
         }
 
-        [class*='ri-'] {
-          display: block;
-          height: 20px;
+        .el-submenu .el-submenu__title,
+        .el-menu-item {
+          min-width: 180px;
+          border-radius: 5px;
         }
       }
     }
 
-    &-horizontal {
-      justify-content: left;
-      width: $base-left-menu-width-min * 1.3;
-      height: $base-left-menu-width-min / 1.3;
-      padding-left: $base-padding / 2;
-    }
-  }
+    .vab-column-grid {
+      display: flex;
+      align-items: center;
+      width: $base-left-menu-width-min;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      word-break: break-all;
+      white-space: nowrap;
+
+      &-vertical,
+      &-card,
+      &-arrow {
+        justify-content: center;
+        height: $base-left-menu-width-min;
+
+        > div {
+          svg {
+            position: relative;
+            top: 8px;
+            display: block;
+            width: $base-font-size-default + 4;
+            height: $base-font-size-default + 4;
+          }
+
+          [class*='ri-'] {
+            display: block;
+            height: 20px;
+          }
+        }
+      }
 
-  ::v-deep {
-    .el-scrollbar__wrap {
-      overflow-x: hidden;
+      &-horizontal {
+        justify-content: left;
+        width: $base-left-menu-width-min * 1.3;
+        height: $base-left-menu-width-min / 1.3;
+        padding-left: $base-padding / 2;
+      }
     }
 
-    .el-tabs {
-      position: fixed;
+    ::v-deep {
+      .el-scrollbar__wrap {
+        overflow-x: hidden;
+      }
 
-      .el-tabs__header.is-left {
-        margin-right: 0 !important;
+      .el-tabs {
+        position: fixed;
 
-        .el-tabs__nav-wrap.is-left {
+        .el-tabs__header.is-left {
           margin-right: 0 !important;
-          background: $base-column-first-menu-background;
 
-          .el-tabs__nav-scroll {
-            height: 100%;
-            overflow-y: auto;
+          .el-tabs__nav-wrap.is-left {
+            margin-right: 0 !important;
+            background: $base-column-first-menu-background;
 
-            &::-webkit-scrollbar {
-              width: 0px;
-              height: 0px;
+            .el-tabs__nav-scroll {
+              height: 100%;
+              overflow-y: auto;
+
+              &::-webkit-scrollbar {
+                width: 0px;
+                height: 0px;
+              }
             }
           }
         }
-      }
 
-      .el-tabs__nav {
-        height: calc(100vh - #{$base-logo-height});
-        background: $base-column-first-menu-background;
-      }
+        .el-tabs__nav {
+          height: calc(100vh - #{$base-logo-height});
+          background: $base-column-first-menu-background;
+        }
 
-      .el-tabs__item {
-        height: auto;
-        padding: 0;
-        color: $base-color-white;
+        .el-tabs__item {
+          height: auto;
+          padding: 0;
+          color: $base-color-white;
 
-        &.is-active {
-          background: $base-color-blue;
+          &.is-active {
+            background: $base-color-blue;
+          }
         }
       }
-    }
 
-    .el-tabs__active-bar.is-left,
-    .el-tabs--left .el-tabs__nav-wrap.is-left::after {
-      display: none;
-    }
+      .el-tabs__active-bar.is-left,
+      .el-tabs--left .el-tabs__nav-wrap.is-left::after {
+        display: none;
+      }
 
-    .el-menu {
-      border: 0;
+      .el-menu {
+        border: 0;
 
-      .el-divider {
-        margin: 0 0 $base-margin 0;
-        background-color: #f6f6f6;
+        .el-divider {
+          margin: 0 0 $base-margin 0;
+          background-color: #f6f6f6;
 
-        &__text {
-          color: $base-color-black;
+          &__text {
+            color: $base-color-black;
+          }
         }
-      }
 
-      .el-menu-item,
-      .el-submenu__title {
-        height: $base-menu-item-height;
-        overflow: hidden;
-        line-height: $base-menu-item-height;
-        text-overflow: ellipsis;
-        white-space: nowrap;
-        vertical-align: middle;
+        .el-menu-item,
+        .el-submenu__title {
+          height: $base-menu-item-height;
+          overflow: hidden;
+          line-height: $base-menu-item-height;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          vertical-align: middle;
 
-        @include active;
+          @include active;
+        }
       }
     }
-  }
 
-  &.is-collapse {
-    ::v-deep {
-      width: 0;
+    &.is-collapse {
+      ::v-deep {
+        width: 0;
+      }
     }
   }
-}
 </style>

+ 26 - 26
src/vab/components/VabFooter/index.vue

@@ -17,36 +17,36 @@
 </template>
 
 <script>
-import { title } from '@/config'
+  import { title } from '@/config'
 
-export default {
-  name: 'VabFooter',
-  data () {
-    return {
-      fullYear: new Date().getFullYear(),
-      title,
-    }
-  },
-}
+  export default {
+    name: 'VabFooter',
+    data() {
+      return {
+        fullYear: new Date().getFullYear(),
+        title,
+      }
+    },
+  }
 </script>
 
 <style lang="scss" scoped>
-.vab-footer {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  min-height: 40px;
-  margin-top: 10px;
-  margin-left: -$base-padding;
-  margin-right: -$base-padding;
-  border-left: 1px solid rgba(40, 44, 52, 0.1);
-  padding: 0 $base-padding 0 $base-padding;
-  color: rgba(0, 0, 0, 0.45);
-  background: $base-color-white;
-  // border-top: 1px dashed $base-border-color;
+  .vab-footer {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 40px;
+    margin-top: 10px;
+    margin-left: -$base-padding;
+    margin-right: -$base-padding;
+    border-left: 1px solid rgba(40, 44, 52, 0.1);
+    padding: 0 $base-padding 0 $base-padding;
+    color: rgba(0, 0, 0, 0.45);
+    background: $base-color-white;
+    // border-top: 1px dashed $base-border-color;
 
-  i {
-    margin: 0 5px;
+    i {
+      margin: 0 5px;
+    }
   }
-}
 </style>

+ 121 - 129
src/vab/components/VabNav/index.vue

@@ -1,39 +1,31 @@
 <template>
   <div class="vab-nav">
     <el-row :gutter="15">
-      <el-col :lg="12"
-              :md="12"
-              :sm="12"
-              :xl="12"
-              :xs="4">
+      <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="4">
         <div class="left-panel">
           <vab-fold v-if="layout !== 'float'" />
-          <el-tabs v-if="layout === 'comprehensive'"
-                   v-model="extra.first"
-                   tab-position="top"
-                   @tab-click="handleTabClick">
+          <el-tabs
+            v-if="layout === 'comprehensive'"
+            v-model="extra.first"
+            tab-position="top"
+            @tab-click="handleTabClick">
             <template v-for="(route, index) in handleRoutes">
-              <el-tab-pane :key="index + route.name"
-                           :name="route.name">
+              <el-tab-pane :key="index + route.name" :name="route.name">
                 <span slot="label">
-                  <vab-icon v-if="route.meta.icon"
-                            :icon="route.meta.icon"
-                            :is-custom-svg="route.meta.isCustomSvg"
-                            style="min-width: 16px" />
+                  <vab-icon
+                    v-if="route.meta.icon"
+                    :icon="route.meta.icon"
+                    :is-custom-svg="route.meta.isCustomSvg"
+                    style="min-width: 16px" />
                   {{ translateTitle(route.meta.title) }}
                 </span>
               </el-tab-pane>
             </template>
           </el-tabs>
-          <vab-breadcrumb v-else
-                          class="hidden-xs-only" />
+          <vab-breadcrumb v-else class="hidden-xs-only" />
         </div>
       </el-col>
-      <el-col :lg="12"
-              :md="12"
-              :sm="12"
-              :xl="12"
-              :xs="20">
+      <el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="20">
         <div class="right-panel">
           <vab-error-log />
           <vab-full-screen />
@@ -47,145 +39,145 @@
 </template>
 
 <script>
-import { translateTitle } from '@/utils/i18n'
-import { mapGetters } from 'vuex'
-import { openFirstMenu } from '@/config'
+  import { translateTitle } from '@/utils/i18n'
+  import { mapGetters } from 'vuex'
+  import { openFirstMenu } from '@/config'
 
-export default {
-  name: 'VabNav',
-  props: {
-    layout: {
-      type: String,
-      default: '',
-    },
-  },
-  data () {
-    return {
-      firstMenu: '',
-    }
-  },
-  computed: {
-    ...mapGetters({
-      extra: 'settings/extra',
-      routes: 'routes/routes',
-    }),
-    handleRoutes () {
-      return this.routes.filter(
-        (route) => route.meta && route.meta.hidden !== true
-      )
+  export default {
+    name: 'VabNav',
+    props: {
+      layout: {
+        type: String,
+        default: '',
+      },
     },
-    handleActiveMenu () {
-      return this.routes.find((route) => route.name === this.extra.first)
+    data() {
+      return {
+        firstMenu: '',
+      }
     },
-    handlePartialRoutes () {
-      const activeMenu = this.handleActiveMenu
-      return activeMenu ? activeMenu.children : []
+    computed: {
+      ...mapGetters({
+        extra: 'settings/extra',
+        routes: 'routes/routes',
+      }),
+      handleRoutes() {
+        return this.routes.filter(
+          (route) => route.meta && route.meta.hidden !== true
+        )
+      },
+      handleActiveMenu() {
+        return this.routes.find((route) => route.name === this.extra.first)
+      },
+      handlePartialRoutes() {
+        const activeMenu = this.handleActiveMenu
+        return activeMenu ? activeMenu.children : []
+      },
     },
-  },
-  watch: {
-    $route: {
-      handler (route) {
-        const firstMenu = route.matched[0].name
-        if (this.extra.first !== firstMenu) {
-          this.extra.first = firstMenu
-          this.handleTabClick(true)
-        }
+    watch: {
+      $route: {
+        handler(route) {
+          const firstMenu = route.matched[0].name
+          if (this.extra.first !== firstMenu) {
+            this.extra.first = firstMenu
+            this.handleTabClick(true)
+          }
+        },
+        immediate: true,
       },
-      immediate: true,
     },
-  },
-  methods: {
-    translateTitle,
-    handleTabClick (handler) {
-      if (handler !== true && openFirstMenu)
-        this.$router.push(this.handleActiveMenu)
+    methods: {
+      translateTitle,
+      handleTabClick(handler) {
+        if (handler !== true && openFirstMenu)
+          this.$router.push(this.handleActiveMenu)
+      },
     },
-  },
-}
+  }
 </script>
 
 <style lang="scss" scoped>
-.vab-nav {
-  position: relative;
-  height: $base-nav-height;
-  padding-right: $base-padding;
-  padding-left: $base-padding;
-  overflow: hidden;
-  user-select: none;
-  background: $base-color-white;
-  box-shadow: $base-box-shadow;
-
-  .left-panel {
-    display: flex;
-    align-items: center;
-    justify-items: center;
+  .vab-nav {
+    position: relative;
     height: $base-nav-height;
+    padding-right: $base-padding;
+    padding-left: $base-padding;
+    overflow: hidden;
+    user-select: none;
+    background: $base-color-white;
+    box-shadow: $base-box-shadow;
 
-    ::v-deep {
-      .fold-unfold {
-        margin-right: $base-margin;
-      }
+    .left-panel {
+      display: flex;
+      align-items: center;
+      justify-items: center;
+      height: $base-nav-height;
 
-      .el-tabs {
-        width: 100%;
-        margin-left: $base-margin;
+      ::v-deep {
+        .fold-unfold {
+          margin-right: $base-margin;
+        }
+
+        .el-tabs {
+          width: 100%;
+          margin-left: $base-margin;
 
-        .el-tabs__header {
-          margin: 0;
+          .el-tabs__header {
+            margin: 0;
 
-          > .el-tabs__nav-wrap {
-            display: flex;
-            align-items: center;
+            > .el-tabs__nav-wrap {
+              display: flex;
+              align-items: center;
 
-            .el-icon-arrow-left,
-            .el-icon-arrow-right {
-              font-weight: 600;
-              color: $base-color-grey;
+              .el-icon-arrow-left,
+              .el-icon-arrow-right {
+                font-weight: 600;
+                color: $base-color-grey;
+              }
             }
           }
-        }
 
-        .el-tabs__item {
-          text-align: center;
-          > div {
-            display: flex;
-            align-items: center;
+          .el-tabs__item {
+            text-align: center;
+            > div {
+              display: flex;
+              align-items: center;
 
-            i {
-              margin-right: 3px;
+              i {
+                margin-right: 3px;
+              }
             }
           }
         }
-      }
 
-      .el-tabs__nav-wrap::after {
-        display: none;
+        .el-tabs__nav-wrap::after {
+          display: none;
+        }
       }
     }
-  }
 
-  .right-panel {
-    display: flex;
-    align-content: center;
-    align-items: center;
-    justify-content: flex-end;
-    height: $base-nav-height;
-
-    ::v-deep {
-      [class*='ri-'] {
-        margin-left: $base-margin;
-        color: $base-color-grey;
-        cursor: pointer;
-      }
+    .right-panel {
+      display: flex;
+      align-content: center;
+      align-items: center;
+      justify-content: flex-end;
+      height: $base-nav-height;
 
-      button {
+      ::v-deep {
         [class*='ri-'] {
-          margin-left: 0;
-          color: $base-color-white;
+          margin-left: $base-margin;
+          color: $base-color-grey;
           cursor: pointer;
         }
+
+        button {
+          [class*='ri-'] {
+            margin-left: 0;
+            color: $base-color-white;
+            cursor: pointer;
+          }
+        }
       }
     }
   }
-}
 </style>

+ 55 - 55
src/views/customer/components/Allocate.vue

@@ -2,77 +2,77 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-26 14:34:34
  * @LastEditors: wanglj
- * @LastEditTime: 2022-12-26 17:24:04
+ * @LastEditTime: 2022-12-29 11:26:07
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\components\allocate.vue
 -->
 <template>
-  <el-dialog :visible.sync="visible"
-             width="30%"
-             @close="handleClose"
-             title="分配客户">
-    <Transfer ref="transfer"></Transfer>
-    <el-form :model="form"
-             label-width="80px">
+  <el-dialog
+    title="分配客户"
+    :visible.sync="visible"
+    width="30%"
+    @close="handleClose">
+    <Transfer ref="transfer" />
+    <el-form label-width="80px" :model="form">
       <el-form-item label="销售代表">
-        <el-input v-model="form.allocate"
-                  readonly>
-          <el-button slot="append"
-                     icon="el-icon-search"
-                     @click="choose"></el-button>
+        <el-input v-model="form.allocate" readonly>
+          <el-button slot="append" icon="el-icon-search" @click="choose" />
         </el-input>
       </el-form-item>
     </el-form>
     <span slot="footer">
-      <el-button type="primary"
-                 size="mini">保存</el-button>
-      <el-button size="mini"
-                 @click="visible = false">取消</el-button>
+      <el-button size="mini" type="primary">保存</el-button>
+      <el-button size="mini" @click="visible = false">取消</el-button>
     </span>
   </el-dialog>
 </template>
 
 <script>
-import Transfer from './Transfer.vue'
-export default {
-  data () {
-    const generateData = _ => {
-      const data = [];
-      const cities = ['上海', '北京', '广州', '深圳', '南京', '西安', '成都'];
-      const pinyin = ['shanghai', 'beijing', 'guangzhou', 'shenzhen', 'nanjing', 'xian', 'chengdu'];
-      cities.forEach((city, index) => {
-        data.push({
-          label: city,
-          key: index,
-          pinyin: pinyin[index]
-        });
-      });
-      return data;
-    };
-    return {
-      visible: false,
-      innerVisible: false,
-      form: {
-        allocate: ''
+  import Transfer from './Transfer.vue'
+  export default {
+    components: {
+      Transfer,
+    },
+    data() {
+      const generateData = () => {
+        const data = []
+        const cities = ['上海', '北京', '广州', '深圳', '南京', '西安', '成都']
+        const pinyin = [
+          'shanghai',
+          'beijing',
+          'guangzhou',
+          'shenzhen',
+          'nanjing',
+          'xian',
+          'chengdu',
+        ]
+        cities.forEach((city, index) => {
+          data.push({
+            label: city,
+            key: index,
+            pinyin: pinyin[index],
+          })
+        })
+        return data
+      }
+      return {
+        visible: false,
+        innerVisible: false,
+        form: {
+          allocate: '',
+        },
+        allocate: [],
+        data: generateData(),
+        options: [],
+      }
+    },
+    methods: {
+      handleClose() {},
+      choose() {
+        this.$refs.transfer.innerVisible = true
       },
-      allocate: [],
-      data: generateData(),
-      options: []
-    }
-  },
-  components: {
-    Transfer
-  },
-  methods: {
-    handleClose () {
-
     },
-    choose () {
-      this.$refs.transfer.innerVisible = true
-    }
   }
-}
 </script>
 
-<style>
-</style>
+<style></style>

+ 103 - 110
src/views/customer/components/Contact.vue

@@ -1,66 +1,54 @@
 <template>
   <!-- 新建联系人弹窗 -->
-  <el-dialog :visible.sync="contactVisible"
-             @close="contactClose"
-             :title="title">
-    <el-form ref="contactForm"
-             :model="contactForm"
-             :rules="contactRules">
+  <el-dialog
+    :title="title"
+    :visible.sync="contactVisible"
+    @close="contactClose">
+    <el-form ref="contactForm" :model="contactForm" :rules="contactRules">
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="姓名"
-                        prop="cuctName">
-            <el-input placeholder="请输入姓名"
-                      v-model="contactForm.cuctName"></el-input>
+          <el-form-item label="姓名" prop="cuctName">
+            <el-input v-model="contactForm.cuctName" placeholder="请输入姓名" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="客户名称"
-                        prop="custName">
-            <el-input placeholder="请输入客户名称"
-                      disabled
-                      v-model="contactForm.custName"></el-input>
+          <el-form-item label="客户名称" prop="custName">
+            <el-input
+              v-model="contactForm.custName"
+              disabled
+              placeholder="请输入客户名称" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="手机号码"
-                        prop="telephone">
-            <el-input placeholder="请输入手机号码"
-                      v-model="contactForm.telephone">
-            </el-input>
+          <el-form-item label="手机号码" prop="telephone">
+            <el-input
+              v-model="contactForm.telephone"
+              placeholder="请输入手机号码" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="微信"
-                        prop="wechat">
-            <el-input placeholder="请输入微信"
-                      v-model="contactForm.wechat"></el-input>
+          <el-form-item label="微信" prop="wechat">
+            <el-input v-model="contactForm.wechat" placeholder="请输入微信" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="邮箱"
-                        prop="email">
-            <el-input placeholder="请输入邮箱"
-                      v-model="contactForm.email"></el-input>
+          <el-form-item label="邮箱" prop="email">
+            <el-input v-model="contactForm.email" placeholder="请输入邮箱" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="职务"
-                        prop="postion">
-            <el-input placeholder="请输入职务"
-                      v-model="contactForm.postion"></el-input>
-            </el-date-picker>
+          <el-form-item label="职务" prop="postion">
+            <el-input v-model="contactForm.postion" placeholder="请输入职务" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="是否关键决策人"
-                        prop="policy">
+          <el-form-item label="是否关键决策人" prop="policy">
             <el-radio-group v-model="contactForm.policy">
               <el-radio :label="0">否</el-radio>
               <el-radio :label="1">是</el-radio>
@@ -68,8 +56,7 @@
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="性别"
-                        prop="cuctGender">
+          <el-form-item label="性别" prop="cuctGender">
             <el-radio-group v-model="contactForm.cuctGender">
               <el-radio label="10">男</el-radio>
               <el-radio label="20">女</el-radio>
@@ -77,92 +64,98 @@
           </el-form-item>
         </el-col>
       </el-row>
-      <el-form-item label="备注"
-                    prop="remark">
-        <el-input type="textarea"
-                  placeholder="请输入备注"
-                  v-model="contactForm.remark"
-                  resize="none"
-                  :rows="5"
-                  maxlength="500"
-                  show-word-limit></el-input>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          v-model="contactForm.remark"
+          maxlength="500"
+          placeholder="请输入备注"
+          resize="none"
+          :rows="5"
+          show-word-limit
+          type="textarea" />
       </el-form-item>
     </el-form>
     <span slot="footer">
-      <el-button v-show="contactForm.id"
-                 type="primary"
-                 @click="contactEdit">保存</el-button>
-      <el-button v-show="!contactForm.id"
-                 type="primary"
-                 @click="contactSave">保存</el-button>
+      <el-button v-show="contactForm.id" type="primary" @click="contactEdit">
+        保存
+      </el-button>
+      <el-button v-show="!contactForm.id" type="primary" @click="contactSave">
+        保存
+      </el-button>
       <el-button @click="contactVisible = false">取消</el-button>
     </span>
   </el-dialog>
 </template>
 
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-export default {
-  data () {
-    var validateTel = (rule, value, callback) => {
-      const reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
-      if (value === '') {
-        callback(new Error('请输入手机号码'));
-      } else if (!reg.test(value)) {
-        callback(new Error('请输入正确手机号码'));
-      } else {
-        callback();
+  import to from 'await-to-js'
+  import api from '@/api/customer'
+  export default {
+    data() {
+      var validateTel = (rule, value, callback) => {
+        const reg =
+          /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
+        if (value === '') {
+          callback(new Error('请输入手机号码'))
+        } else if (!reg.test(value)) {
+          callback(new Error('请输入正确手机号码'))
+        } else {
+          callback()
+        }
       }
-    };
-    return {
-      title: '新增联系人',
-      contactVisible: false,
-      contactForm: {
-        custId: '',//客户ID  (必传) 
-        custName: '',//客户名称      
-        cuctName: '',//联系人名字
-        cuctGender: '10',//性别(10男20女)
-        postion: '',//职位
-        telephone: '',//电话
-        wechat: '',//微信
-        email: '',//邮箱
-        remark: '',//备注
-        policy: 0//是否决策人 0 不是  1 是
-      },
-      contactRules: {
-        cuctName: [{ required: true, trigger: 'blur', message: '请输入联系人姓名' }],
-        custName: [{ required: true, trigger: 'blur', message: '请输入客户名称' }],
-        telephone: [{ required: true, trigger: 'blur', validator: validateTel }]
+      return {
+        title: '新增联系人',
+        contactVisible: false,
+        contactForm: {
+          custId: '', //客户ID  (必传)
+          custName: '', //客户名称
+          cuctName: '', //联系人名字
+          cuctGender: '10', //性别(10男20女)
+          postion: '', //职位
+          telephone: '', //电话
+          wechat: '', //微信
+          email: '', //邮箱
+          remark: '', //备注
+          policy: 0, //是否决策人 0 不是  1 是
+        },
+        contactRules: {
+          cuctName: [
+            { required: true, trigger: 'blur', message: '请输入联系人姓名' },
+          ],
+          custName: [
+            { required: true, trigger: 'blur', message: '请输入客户名称' },
+          ],
+          telephone: [
+            { required: true, trigger: 'blur', validator: validateTel },
+          ],
+        },
       }
-    }
-  },
-  methods: {
-    // 联系人新建
-    async contactSave () {
-      let params = { ...this.contactForm }
-      const [err, res] = await to(api.createContact(params))
-      if (err) return
-      this.$message.success(res.msg)
-      this.contactVisible = false
-      this.$emit('contactSave')
-    },
-    // 联系人编辑
-    async contactEdit () {
-      let params = { ...this.contactForm }
-      const [err, res] = await to(api.updateContact(params))
-      if (err) return
-      this.$message.success(res.msg)
-      this.contactVisible = false
-      this.$emit('contactSave')
     },
-    contactClose () {
-      this.$refs.contactForm.resetFields()
-      this.contactForm.custId = ''
+    methods: {
+      // 联系人新建
+      async contactSave() {
+        let params = { ...this.contactForm }
+        const [err, res] = await to(api.createContact(params))
+        if (err) return
+        this.$message.success(res.msg)
+        this.contactVisible = false
+        this.$emit('contactSave')
+      },
+      // 联系人编辑
+      async contactEdit() {
+        let params = { ...this.contactForm }
+        const [err, res] = await to(api.updateContact(params))
+        if (err) return
+        this.$message.success(res.msg)
+        this.contactVisible = false
+        this.$emit('contactSave')
+      },
+      contactClose() {
+        this.$refs.contactForm.resetFields()
+        this.contactForm.custId = ''
+      },
     },
   }
-}
 </script>
 
-<style>
-</style>
+<style></style>

+ 277 - 252
src/views/customer/components/Edit.vue

@@ -1,315 +1,340 @@
 <template>
-  <el-dialog :visible.sync="editVisible"
-             @close="handleClose"
-             :title="title">
-    <el-form ref="editForm"
-             :model="editForm"
-             :rules="editRules">
+  <el-dialog :title="title" :visible.sync="editVisible" @close="handleClose">
+    <el-form ref="editForm" :model="editForm" :rules="editRules">
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="客户名称"
-                        prop="custName">
-            <el-input placeholder="请输入客户名称"
-                      v-model="editForm.custName"></el-input>
+          <el-form-item label="客户名称" prop="custName">
+            <el-input
+              v-model="editForm.custName"
+              placeholder="请输入客户名称" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="助记名"
-                        prop="abbrName">
-            <el-input placeholder="请输入助记名"
-                      v-model="editForm.abbrName"></el-input>
+          <el-form-item label="助记名" prop="abbrName">
+            <el-input v-model="editForm.abbrName" placeholder="请输入助记名" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="客户行业"
-                        prop="custIndustry">
-            <el-select style="width:100%"
-                       placeholder="请选择客户行业"
-                       v-model="editForm.custIndustry">
-              <el-option v-for="item in industryOptions"
-                         :key="item.value"
-                         :label="item.value"
-                         :value="item.value"></el-option>
+          <el-form-item label="客户行业" prop="custIndustry">
+            <el-select
+              v-model="editForm.custIndustry"
+              placeholder="请选择客户行业"
+              style="width: 100%">
+              <el-option
+                v-for="item in industryOptions"
+                :key="item.value"
+                :label="item.value"
+                :value="item.value" />
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="客户级别"
-                        prop="custLevel">
-            <el-select style="width:100%"
-                       placeholder="请选择客户级别"
-                       v-model="editForm.custLevel">
-              <el-option v-for="item in levelOptions"
-                         :key="item.value"
-                         :label="item.value"
-                         :value="item.value"></el-option>
+          <el-form-item label="客户级别" prop="custLevel">
+            <el-select
+              v-model="editForm.custLevel"
+              placeholder="请选择客户级别"
+              style="width: 100%">
+              <el-option
+                v-for="item in levelOptions"
+                :key="item.value"
+                :label="item.value"
+                :value="item.value" />
             </el-select>
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="客户来源"
-                        prop="source">
-            <el-select style="width:100%"
-                       placeholder="请选择客户来源"
-                       v-model="editForm.source">
-              <el-option v-for="item in sourceOptions"
-                         :key="item.value"
-                         :label="item.value"
-                         :value="item.value"></el-option>
+          <el-form-item label="客户来源" prop="source">
+            <el-select
+              v-model="editForm.source"
+              placeholder="请选择客户来源"
+              style="width: 100%">
+              <el-option
+                v-for="item in sourceOptions"
+                :key="item.value"
+                :label="item.value"
+                :value="item.value" />
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="下次联系时间"
-                        prop="followUpDate">
-            <el-date-picker style="width:100%"
-                            type="date"
-                            value-format="yyyy-MM-dd"
-                            placeholder="选择下次联系时间"
-                            v-model="editForm.followUpDate">
-            </el-date-picker>
+          <el-form-item label="下次联系时间" prop="followUpDate">
+            <el-date-picker
+              v-model="editForm.followUpDate"
+              placeholder="选择下次联系时间"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="所在地区"
-                        required>
-            <el-row style="width:100%;padding-top:32px"
-                    :gutter="4">
+          <el-form-item label="所在地区" required>
+            <el-row :gutter="4" style="width: 100%; padding-top: 32px">
               <el-col :span="8">
-                <el-select placeholder="省"
-                           value-key="id"
-                           @change="provinceChange"
-                           v-model="editForm.province">
-                  <el-option v-for="item in provinceOptions"
-                             :key="item.id"
-                             :label="item.distName"
-                             :value="item"></el-option>
+                <el-select
+                  v-model="editForm.province"
+                  placeholder="省"
+                  value-key="id"
+                  @change="provinceChange">
+                  <el-option
+                    v-for="item in provinceOptions"
+                    :key="item.id"
+                    :label="item.distName"
+                    :value="item" />
                 </el-select>
               </el-col>
               <el-col :span="8">
-                <el-select placeholder="市"
-                           value-key="id"
-                           @change="cityChange"
-                           v-model="editForm.city">
-                  <el-option v-for="item in (editForm.province ? editForm.province.children : [])"
-                             :key="item.id"
-                             :label="item.distName"
-                             :value="item"></el-option>
+                <el-select
+                  v-model="editForm.city"
+                  placeholder="市"
+                  value-key="id"
+                  @change="cityChange">
+                  <el-option
+                    v-for="item in editForm.province
+                      ? editForm.province.children
+                      : []"
+                    :key="item.id"
+                    :label="item.distName"
+                    :value="item" />
                 </el-select>
               </el-col>
               <el-col :span="8">
-                <el-select placeholder="区"
-                           value-key="id"
-                           @change="$forceUpdate()"
-                           v-model="editForm.region">
-                  <el-option v-for="item in (editForm.city ? editForm.city.children : [])"
-                             :key="item.id"
-                             :label="item.distName"
-                             :value="item"></el-option>
+                <el-select
+                  v-model="editForm.region"
+                  placeholder="区"
+                  value-key="id"
+                  @change="$forceUpdate()">
+                  <el-option
+                    v-for="item in editForm.city ? editForm.city.children : []"
+                    :key="item.id"
+                    :label="item.distName"
+                    :value="item" />
                 </el-select>
               </el-col>
             </el-row>
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="详细地址"
-                        prop="custAddress">
-            <el-input placeholder="请输入详细地址"
-                      v-model="editForm.custAddress"></el-input>
+          <el-form-item label="详细地址" prop="custAddress">
+            <el-input
+              v-model="editForm.custAddress"
+              placeholder="请输入详细地址" />
           </el-form-item>
         </el-col>
       </el-row>
-      <el-form-item label="备注"
-                    prop="remark">
-        <el-input type="textarea"
-                  placeholder="请输入备注"
-                  v-model="editForm.remark"
-                  resize="none"
-                  :rows="5"
-                  maxlength="500"
-                  show-word-limit></el-input>
+      <el-form-item label="备注" prop="remark">
+        <el-input
+          v-model="editForm.remark"
+          maxlength="500"
+          placeholder="请输入备注"
+          resize="none"
+          :rows="5"
+          show-word-limit
+          type="textarea" />
       </el-form-item>
     </el-form>
     <span slot="footer">
-      <el-button type="primary"
-                 @click="customerSave"
-                 v-show="!editForm.id">保存</el-button>
-      <el-button type="primary"
-                 @click="customerEdit"
-                 v-show="editForm.id">保存</el-button>
-      <el-button @click="createContact"
-                 v-if="!editForm.id">保存并新建联系人</el-button>
+      <el-button v-show="!editForm.id" type="primary" @click="customerSave">
+        保存
+      </el-button>
+      <el-button v-show="editForm.id" type="primary" @click="customerEdit">
+        保存
+      </el-button>
+      <el-button v-if="!editForm.id" @click="createContact">
+        保存并新建联系人
+      </el-button>
       <el-button @click="editVisible = false">取消</el-button>
     </span>
   </el-dialog>
 </template>
 
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-export default {
-  data () {
-    return {
-      title: '新增客户信息',
-      // 新增编辑客户弹窗
-      editVisible: false,
-      editForm: {
-        custName: '',    // 客户名称
-        abbrName: '',     // 助记名
-        custLocation: '', // 所在地区
-        custAddress: '',  // 详细地址
-        custStatus: '',   // 客户状态(10正常20)
-        followUpDate: '', // 最后跟进时间
-        custIndustry: '',  // 客户行业  (没数据)
-        custLevel: '',    // 客户级别  (没数据)
-        source: '',//客户来源
-        province: '',//省
-        city: '',//市
-        region: '', //区
-      },
-      editRules: {
-        custName: [{ required: true, trigger: 'blur', message: '请输入客户名称' }],
-        custIndustry: [{ required: true, trigger: 'change', message: '请选择客户行业' }],
-        custLevel: [{ required: true, trigger: 'change', message: '请选择客户级别' }],
-        source: [{ required: true, trigger: 'change', message: '请选择客户级别' }],
-      },
-      provinceOptions: [],
-      provinceDetail: [],
-      industryOptions: [], //客户行业
-      levelOptions: [],//客户级别
-      sourceOptions: [],//客户来源
-
-    }
-  },
-  mounted () {
-    this.getOptions()
-  },
-  methods: {
-    async init (ids) {
-      if (!ids) {
-        this.title = '新增客户信息'
-        this.editVisible = true
-        return
-      }
-      this.title = '编辑客户'
-      const [err, res] = await to(api.getDetail({ ids }))
-      if (err) return
-      if (res.data.list[0]) this.editForm = res.data.list[0]
-      else return
-      this.editVisible = true
-      this.showLocation()
-    },
-    getOptions () {
-      Promise.all([
-        api.getProvinceDetail(),
-        this.getDicts('CustomerLevel'),
-        this.getDicts('CustomerIndustry'),
-        this.getDicts('CustomerSource'),
-      ]).then(([province, level, industry, source]) => {
-        this.provinceOptions = province.data.list || []
-        this.levelOptions = level.data.values || []
-        this.industryOptions = industry.data.values || []
-        this.sourceOptions = source.data.values || []
-      }).catch(err => console.log(err))
-    },
-    // 保存客户
-    async customerSave () {
-      let params = { ...this.editForm }
-      const [valid] = await to(this.$refs.editForm.validate())
-      if (valid == false) return
-      if (!params.province.id) return this.$message.warning('请选择所在地区')
-      let arr = []
-      arr.push(params.province.distName)
-      if (params.city.id) arr.push(params.city.distName)
-      if (params.region.id) arr.push(params.region.distName)
-      params.custLocation = arr.join('/')
-      if (!params.followUpDate) params.followUpDate = null
-      const [err, res] = await to(api.createCustomer(params))
-      if (err) return
-      if (res.code == 200) this.$message.success(res.msg)
-      else return
-      this.editVisible = false
-      this.$emit('customerSave')
+  import to from 'await-to-js'
+  import api from '@/api/customer'
+  export default {
+    data() {
       return {
-        id: res.data.lastId,
-        name: params.custName
-      }
-    },
-    // 编辑客户
-    async customerEdit () {
-      let params = { ...this.editForm }
-      const [valid] = await to(this.$refs.editForm.validate())
-      if (valid == false) return
-      if (!params.province.id) return this.$message.warning('请选择所在地区')
-      let arr = []
-      arr.push(params.province.distName)
-      if (params.city.id) arr.push(params.city.distName)
-      if (params.region.id) arr.push(params.region.distName)
-      params.custLocation = arr.join('/')
-      if (!params.followUpDate) params.followUpDate = null
-      const [err, res] = await to(api.updateCostomer(params))
-      if (err) return
-      if (res.code == 200) this.$message.success(res.msg)
-      else return
-      this.editVisible = false
-      this.$emit('customerSave')
-    },
-    // 联系人弹窗
-    async createContact () {
-      const res = await this.customerSave()
-      if (!res) return
-      this.$emit('createContact', res)
-    },
-    // 省份改变
-    async handleProvinceChange (val) {
-      const [err, res] = await to(api.getProvinceDetail({ Id: val.id }))
-      if (err) return
-      this.provinceDetail = res.data.list || []
-    },
-    handleClose () {
-      this.editForm = {
-        custName: '',    // 客户名称
-        abbrName: '',     // 助记名
-        custLocation: '', // 所在地区
-        custAddress: '',  // 详细地址
-        custStatus: '',   // 客户状态(10正常20)
-        followUpDate: '', // 最后跟进时间
-        custIndustry: '',  // 客户行业  (没数据)
-        custLevel: '',    // 客户级别  (没数据)
-        source: '',//客户来源
-        province: '',//省
-        city: '',//市
-        region: '', //区
+        title: '新增客户信息',
+        // 新增编辑客户弹窗
+        editVisible: false,
+        editForm: {
+          custName: '', // 客户名称
+          abbrName: '', // 助记名
+          custLocation: '', // 所在地区
+          custAddress: '', // 详细地址
+          custStatus: '', // 客户状态(10正常20)
+          followUpDate: '', // 最后跟进时间
+          custIndustry: '', // 客户行业  (没数据)
+          custLevel: '', // 客户级别  (没数据)
+          source: '', //客户来源
+          province: {}, //省
+          city: {}, //市
+          region: {}, //区
+        },
+        editRules: {
+          custName: [
+            { required: true, trigger: 'blur', message: '请输入客户名称' },
+          ],
+          custIndustry: [
+            { required: true, trigger: 'change', message: '请选择客户行业' },
+          ],
+          custLevel: [
+            { required: true, trigger: 'change', message: '请选择客户级别' },
+          ],
+          source: [
+            { required: true, trigger: 'change', message: '请选择客户级别' },
+          ],
+        },
+        provinceOptions: [],
+        provinceDetail: [],
+        industryOptions: [], //客户行业
+        levelOptions: [], //客户级别
+        sourceOptions: [], //客户来源
       }
-      this.$refs.editForm.resetFields()
     },
-    showLocation () {
-      const arr = this.editForm.custLocation.split('/')
-      console.log(arr, 'arr');
-      if (!arr.length) return
-      this.editForm.province = this.provinceOptions.find(item => item.distName == arr[0])
-      if (arr[1]) this.editForm.city = this.editForm.province.children.find(item => item.distName == arr[1])
-      if (arr[2]) this.editForm.region = this.editForm.city.children.find(item => item.distName == arr[2])
+    mounted() {
+      this.getOptions()
     },
-    provinceChange (val) {
-      this.editForm.city = {}
-      this.editForm.region = {}
-      this.editForm.custDistCode = val.id
-      this.$forceUpdate()
+    methods: {
+      async init(ids) {
+        if (!ids) {
+          this.title = '新增客户信息'
+          this.editVisible = true
+          return
+        }
+        this.title = '编辑客户'
+        const [err, res] = await to(api.getDetail({ ids }))
+        if (err) return
+        if (res.data.list[0]) this.editForm = res.data.list[0]
+        else return
+        this.editVisible = true
+        this.showLocation()
+      },
+      getOptions() {
+        Promise.all([
+          api.getProvinceDetail(),
+          this.getDicts('CustomerLevel'),
+          this.getDicts('CustomerIndustry'),
+          this.getDicts('CustomerSource'),
+        ])
+          .then(([province, level, industry, source]) => {
+            this.provinceOptions = province.data.list || []
+            this.levelOptions = level.data.values || []
+            this.industryOptions = industry.data.values || []
+            this.sourceOptions = source.data.values || []
+          })
+          .catch((err) => console.log(err))
+      },
+      // 保存客户
+      async customerSave() {
+        let params = { ...this.editForm }
+        const [valid] = await to(this.$refs.editForm.validate())
+        if (valid == false) return
+        if (!params.province.id) return this.$message.warning('请选择所在地区')
+        let arr = []
+        arr.push(params.province.distName)
+        if (params.city.id) arr.push(params.city.distName)
+        if (params.region.id) arr.push(params.region.distName)
+        params.custLocation = arr.join('/')
+        if (!params.followUpDate) params.followUpDate = null
+        const [err, res] = await to(api.createCustomer(params))
+        if (err) return
+        if (res.code == 200) this.$message.success(res.msg)
+        else return
+        this.editVisible = false
+        this.$emit('customerSave')
+        return {
+          id: res.data.lastId,
+          name: params.custName,
+        }
+      },
+      // 编辑客户
+      async customerEdit() {
+        let params = { ...this.editForm }
+        const [valid] = await to(this.$refs.editForm.validate())
+        if (valid == false) return
+        if (!params.province.id) return this.$message.warning('请选择所在地区')
+        let arr = []
+        arr.push(params.province.distName)
+        if (params.city.id) arr.push(params.city.distName)
+        if (params.region.id) arr.push(params.region.distName)
+        params.custLocation = arr.join('/')
+        if (!params.followUpDate) params.followUpDate = null
+        const [err, res] = await to(api.updateCostomer(params))
+        if (err) return
+        if (res.code == 200) this.$message.success(res.msg)
+        else return
+        this.editVisible = false
+        this.$emit('customerSave')
+      },
+      // 联系人弹窗
+      async createContact() {
+        const res = await this.customerSave()
+        if (!res) return
+        this.$emit('createContact', res)
+      },
+      // 省份改变
+      async handleProvinceChange(val) {
+        const [err, res] = await to(api.getProvinceDetail({ Id: val.id }))
+        if (err) return
+        this.provinceDetail = res.data.list || []
+      },
+      handleClose() {
+        this.editForm = {
+          custName: '', // 客户名称
+          abbrName: '', // 助记名
+          custLocation: '', // 所在地区
+          custAddress: '', // 详细地址
+          custStatus: '', // 客户状态(10正常20)
+          followUpDate: '', // 最后跟进时间
+          custIndustry: '', // 客户行业  (没数据)
+          custLevel: '', // 客户级别  (没数据)
+          source: '', //客户来源
+          province: {}, //省
+          city: {}, //市
+          region: {}, //区
+        }
+        this.$refs.editForm.resetFields()
+      },
+      showLocation() {
+        const arr = this.editForm.custLocation.split('/')
+        if (!arr.length) return
+        this.editForm.province = this.provinceOptions.find(
+          (item) => item.distName == arr[0]
+        )
+        if (arr[1]) {
+          this.editForm.city = this.editForm.province.children.find(
+            (item) => item.distName == arr[1]
+          )
+        } else {
+          this.editForm.city = {}
+        }
+
+        if (arr[2]) {
+          this.editForm.region = this.editForm.city.children.find(
+            (item) => item.distName == arr[2]
+          )
+        } else {
+          this.editForm.region = {}
+        }
+      },
+      provinceChange(val) {
+        this.editForm.city = {}
+        this.editForm.region = {}
+        this.editForm.custDistCode = val.id
+        this.$forceUpdate()
+      },
+      cityChange() {
+        this.editForm.region = {}
+        this.$forceUpdate()
+      },
     },
-    cityChange () {
-      this.editForm.region = {}
-      this.$forceUpdate()
-    }
   }
-}
 </script>
 
-<style lang="scss" scoped>
-</style>
+<style lang="scss" scoped></style>

+ 91 - 0
src/views/customer/components/FollowDetail.vue

@@ -0,0 +1,91 @@
+<!--
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2022-12-29 18:00:08
+ * @LastEditors: wanglj
+ * @LastEditTime: 2022-12-30 09:03:39
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\views\customer\components\FollowDetail.vue
+-->
+<template>
+  <el-dialog title="跟进详情" :visible.sync="visible">
+    <el-descriptions border class="margin-top" :column="1" size="medium">
+      <el-descriptions-item label="跟进类型">
+        {{ formatType(form.followType) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="跟进时间">
+        {{ form.followDate }}
+      </el-descriptions-item>
+      <el-descriptions-item label="跟进描述">
+        {{ form.followDate }}
+      </el-descriptions-item>
+      <el-descriptions-item label="客户名称">
+        {{ form.custName }}
+      </el-descriptions-item>
+      <el-descriptions-item label="联系人">
+        {{ form.contactsName }}
+      </el-descriptions-item>
+      <el-descriptions-item label="相关附件">
+        {{ form.files }}
+      </el-descriptions-item>
+      <el-descriptions-item label="评论数量">
+        {{ form.commentNumber }}
+      </el-descriptions-item>
+      <el-descriptions-item label="创建时间">
+        {{ form.createdTime }}
+      </el-descriptions-item>
+    </el-descriptions>
+    <span slot="footer">
+      <!-- <el-button>编辑跟进</el-button>
+        <el-button>删除跟进</el-button> -->
+      <el-button @click="visible = false">关闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        visible: false,
+        form: {
+          id: '',
+          followType: '',
+          followDate: '',
+          followContent: '',
+          targetId: '',
+          targetType: '',
+          targetName: '',
+          custId: '',
+          custName: '',
+          contactsId: 0,
+          contactsName: '',
+          reminders: '',
+          nextTime: '',
+          remark: '',
+          createdBy: '',
+          createdName: '',
+          createdTime: '',
+          updatedBy: '',
+          updatedName: '',
+          updatedTime: '',
+          deletedTime: '',
+        },
+      }
+    },
+    methods: {
+      init(form) {
+        this.form = form
+        this.visible = true
+      },
+      formatType(val) {
+        let str = ''
+        if (val == 10) str = '电话'
+        else if (val == 20) str = '邮件'
+        else if (val == 30) str = '拜访'
+        return str
+      },
+    },
+  }
+</script>
+
+<style></style>

+ 119 - 134
src/views/customer/components/Merge.vue

@@ -7,183 +7,168 @@
  * @FilePath: \opms_frontend\src\views\customer\components\Merge.vue
 -->
 <template>
-  <el-dialog :visible.sync="visible"
-             @close="handleClose"
-             title="合并客户">
-    <el-alert type="warning"
-              :closable="false"
-              show-icon>
+  <el-dialog title="合并客户" :visible.sync="visible" @close="handleClose">
+    <el-alert :closable="false" show-icon type="warning">
       <div slot="title">
         <h3>特别提示</h3>
-        <p>1、合并后系统只保留目标客户,同时将另一个客户的联系人、商机、订单、附件、销售动态等迁移到目标客户。</p>
+        <p>
+          1、合并后系统只保留目标客户,同时将另一个客户的联系人、商机、订单、附件、销售动态等迁移到目标客户。
+        </p>
         <p>2、红色字段表示两个客户该字段值不同。</p>
       </div>
     </el-alert>
     <div class="merge">
       <ul class="title">
-        <li :class="{differ:flag.custName}">目标客户</li>
-        <li :class="{differ:flag.abbrName}">助记名</li>
-        <li :class="{differ:flag.custIndustry}">客户行业</li>
-        <li :class="{differ:flag.custLevel}">客户级别</li>
-        <li :class="{differ:flag.source}">客户来源</li>
-        <li :class="{differ:flag.followUpDate}">下次联系时间</li>
-        <li :class="{differ:flag.custLocation}">所在地区</li>
-        <li :class="{differ:flag.custAddress}">详细地址</li>
-        <li :class="{differ:flag.remark}">备注</li>
+        <li :class="{ differ: flag.custName }">目标客户</li>
+        <li :class="{ differ: flag.abbrName }">助记名</li>
+        <li :class="{ differ: flag.custIndustry }">客户行业</li>
+        <li :class="{ differ: flag.custLevel }">客户级别</li>
+        <li :class="{ differ: flag.source }">客户来源</li>
+        <li :class="{ differ: flag.followUpDate }">下次联系时间</li>
+        <li :class="{ differ: flag.custLocation }">所在地区</li>
+        <li :class="{ differ: flag.custAddress }">详细地址</li>
+        <li :class="{ differ: flag.remark }">备注</li>
       </ul>
-      <ul class="each"
-          v-for="(item, index) in list"
-          :key="index">
+      <ul v-for="(item, index) in list" :key="index" class="each">
         <li>
-          <el-radio v-model="form.custName"
-                    :label="item.custName"
-                    @change="targetChange(item)"></el-radio>
+          <el-radio
+            v-model="form.custName"
+            :label="item.custName"
+            @change="targetChange(item)" />
         </li>
         <li>
-          <el-radio v-model="form.abbrName"
-                    :label="item.abbrName"></el-radio>
+          <el-radio v-model="form.abbrName" :label="item.abbrName" />
         </li>
         <li>
-          <el-radio v-model="form.custIndustry"
-                    :label="item.custIndustry"></el-radio>
+          <el-radio v-model="form.custIndustry" :label="item.custIndustry" />
         </li>
         <li>
-          <el-radio v-model="form.custLevel"
-                    :label="item.custLevel"></el-radio>
+          <el-radio v-model="form.custLevel" :label="item.custLevel" />
         </li>
         <li>
-          <el-radio v-model="form.source"
-                    :label="item.source"></el-radio>
+          <el-radio v-model="form.source" :label="item.source" />
         </li>
         <li>
-          <el-radio v-model="form.followUpDate"
-                    :label="item.followUpDate"></el-radio>
+          <el-radio v-model="form.followUpDate" :label="item.followUpDate" />
         </li>
         <li>
-          <el-radio v-model="form.custLocation"
-                    :label="item.custLocation"></el-radio>
+          <el-radio v-model="form.custLocation" :label="item.custLocation" />
         </li>
         <li>
-          <el-radio v-model="form.custAddress"
-                    :label="item.custAddress"></el-radio>
+          <el-radio v-model="form.custAddress" :label="item.custAddress" />
         </li>
         <li>
-          <el-radio v-model="form.remark"
-                    :label="item.remark"></el-radio>
+          <el-radio v-model="form.remark" :label="item.remark" />
         </li>
       </ul>
     </div>
     <span slot="footer">
       <el-button @click="visible = false">取消</el-button>
-      <el-button type="primary"
-                 @click="handleConfirm">合并</el-button>
+      <el-button type="primary" @click="handleConfirm">合并</el-button>
     </span>
   </el-dialog>
 </template>
 
 <script>
-import api from '@/api/customer'
-import to from 'await-to-js'
-export default {
-  data () {
-    return {
-      visible: false,
-      ids: [],
-      form: {
-        id: '',
-        custName: '',    // 客户名称
-        abbrName: '',     // 助记名
-        custLocation: '', // 所在地区
-        custAddress: '',  // 详细地址
-        custStatus: '',   // 客户状态(10正常20)
-        followUpDate: '', // 最后跟进时间
-        custIndustry: '',  // 客户行业  (没数据)
-        custLevel: '',    // 客户级别  (没数据)
-        source: '',//客户来源
-      },
-      flag: {
-        custName: false,    // 客户名称
-        abbrName: false,     // 助记名
-        custLocation: false, // 所在地区
-        custAddress: false,  // 详细地址
-        custStatus: false,   // 客户状态(10正常20)
-        followUpDate: false, // 最后跟进时间
-        custIndustry: false,  // 客户行业  (没数据)
-        custLevel: false,    // 客户级别  (没数据)
-        source: false,//客户来源
-      },
-      list: []
-    }
-  },
-  mounted () {
-  },
-  methods: {
-    init (res, ids) {
-      this.list = res.data.list
-      this.ids = ids
-      this.form = { ...res.data.list[0] }
-      this.form.ChooseId = this.ids.filter(item => item != this.form.id)
-      for (const key in this.form) {
-        const arr = this.list.filter(it => it[key] == this.form[key])
-        if (arr.length !== this.list.length) this.flag[key] = true
+  import api from '@/api/customer'
+  import to from 'await-to-js'
+  export default {
+    data() {
+      return {
+        visible: false,
+        ids: [],
+        form: {
+          id: '',
+          custName: '', // 客户名称
+          abbrName: '', // 助记名
+          custLocation: '', // 所在地区
+          custAddress: '', // 详细地址
+          custStatus: '', // 客户状态(10正常20)
+          followUpDate: '', // 最后跟进时间
+          custIndustry: '', // 客户行业  (没数据)
+          custLevel: '', // 客户级别  (没数据)
+          source: '', //客户来源
+        },
+        flag: {
+          custName: false, // 客户名称
+          abbrName: false, // 助记名
+          custLocation: false, // 所在地区
+          custAddress: false, // 详细地址
+          custStatus: false, // 客户状态(10正常20)
+          followUpDate: false, // 最后跟进时间
+          custIndustry: false, // 客户行业  (没数据)
+          custLevel: false, // 客户级别  (没数据)
+          source: false, //客户来源
+        },
+        list: [],
       }
-      this.visible = true
     },
-    targetChange (row) {
-      this.form.id = row.id
-      this.form.ChooseId = this.ids.filter(item => item != row.id)
-    },
-    handleClose () {
-
+    mounted() {},
+    methods: {
+      init(res, ids) {
+        this.list = res.data.list
+        this.ids = ids
+        this.form = { ...res.data.list[0] }
+        this.form.ChooseId = this.ids.filter((item) => item != this.form.id)
+        for (const key in this.form) {
+          const arr = this.list.filter((it) => it[key] == this.form[key])
+          if (arr.length !== this.list.length) this.flag[key] = true
+        }
+        this.visible = true
+      },
+      targetChange(row) {
+        this.form.id = row.id
+        this.form.ChooseId = this.ids.filter((item) => item != row.id)
+      },
+      handleClose() {},
+      async handleConfirm() {
+        let params = { ...this.form }
+        const [err, res] = await to(api.mergeCustomer(params))
+        if (err) return
+        this.$message.success(res.msg)
+        this.visible = false
+        this.$emit('refresh')
+      },
     },
-    async handleConfirm () {
-      let params = { ...this.form }
-      const [err, res] = await to(api.mergeCustomer(params))
-      if (err) return
-      this.$message.success(res.msg)
-      this.visible = false
-      this.$emit('refresh')
-    }
   }
-}
 </script>
 
 <style lang="scss" scoped>
-.differ {
-  color: #f56c6c;
-}
-::v-deep .el-alert {
-  p {
-    margin: 0;
-    font-weight: normal;
+  .differ {
+    color: #f56c6c;
   }
-  .el-alert__icon {
-    font-size: 28px;
-    width: 28px;
-  }
-}
-.merge {
-  height: 361px;
-  overflow-y: auto;
-  display: flex;
-  ul {
-    border: 1px solid #dcdfe6;
-    & + ul {
-      border-left: none;
+  ::v-deep .el-alert {
+    p {
+      margin: 0;
+      font-weight: normal;
+    }
+    .el-alert__icon {
+      font-size: 28px;
+      width: 28px;
     }
   }
-  li {
-    height: 40px;
-    line-height: 40px;
-    border-bottom: 1px solid #dcdfe6;
-  }
-  .title {
-    width: 100px;
-    text-align: center;
-  }
-  .each {
-    flex: 1;
-    text-align: center;
+  .merge {
+    height: 361px;
+    overflow-y: auto;
+    display: flex;
+    ul {
+      border: 1px solid #dcdfe6;
+      & + ul {
+        border-left: none;
+      }
+    }
+    li {
+      height: 40px;
+      line-height: 40px;
+      border-bottom: 1px solid #dcdfe6;
+    }
+    .title {
+      width: 100px;
+      text-align: center;
+    }
+    .each {
+      flex: 1;
+      text-align: center;
+    }
   }
-}
-</style>
+</style>

+ 52 - 58
src/views/customer/components/Shift.vue

@@ -7,78 +7,72 @@
  * @FilePath: \opms_frontend\src\views\customer\components\Shift.vue
 -->
 <template>
-  <el-dialog :visible.sync="visible"
-             width="30%"
-             @close="handleClose"
-             title="转移客户">
-    <Transfer ref="transfer"></Transfer>
-    <el-form :model="form"
-             :rules="rules"
-             ref="form"
-             label-width="80px">
-      <el-form-item label="接收对象"
-                    prop="allocate">
-        <el-input readonly
-                  v-model="form.allocate">
-          <el-button slot="append"
-                     icon="el-icon-search"
-                     @click="choose"></el-button>
+  <el-dialog
+    title="转移客户"
+    :visible.sync="visible"
+    width="30%"
+    @close="handleClose">
+    <Transfer ref="transfer" />
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-form-item label="接收对象" prop="allocate">
+        <el-input v-model="form.allocate" readonly>
+          <el-button slot="append" icon="el-icon-search" @click="choose" />
         </el-input>
       </el-form-item>
-      <el-form-item label="转移相关"
-                    prop="about">
+      <el-form-item label="转移相关" prop="about">
         <el-checkbox-group v-model="form.about">
-          <el-checkbox label="客户"></el-checkbox>
+          <el-checkbox label="客户" />
         </el-checkbox-group>
       </el-form-item>
-      <el-form-item label="备注信息"
-                    prop="remark">
-        <el-input v-model="form.remark"
-                  type="textarea"
-                  :rows="5"
-                  maxlength="500"
-                  show-word-limit
-                  resize="none"></el-input>
+      <el-form-item label="备注信息" prop="remark">
+        <el-input
+          v-model="form.remark"
+          maxlength="500"
+          resize="none"
+          :rows="5"
+          show-word-limit
+          type="textarea" />
       </el-form-item>
     </el-form>
     <span slot="footer">
-      <el-button size="mini"
-                 @click="visible = false">取消</el-button>
-      <el-button size="mini"
-                 type="primary">确定</el-button>
+      <el-button size="mini" @click="visible = false">取消</el-button>
+      <el-button size="mini" type="primary">确定</el-button>
     </span>
   </el-dialog>
 </template>
 <script>
-import Transfer from './Transfer.vue'
-export default {
-  data () {
-    return {
-      visible: false,
-      form: {
-        allocate: '',
-        about: ['客户'],
-        remark: ''
-      },
-      rules: {
-        allocate: [{ required: true, message: '请选择接收对象', trigger: 'change' }],
-        about: [{ required: true, message: '请选择转移相关', trigger: 'change' }]
+  import Transfer from './Transfer.vue'
+  export default {
+    components: {
+      Transfer,
+    },
+    data() {
+      return {
+        visible: false,
+        form: {
+          allocate: '',
+          about: ['客户'],
+          remark: '',
+        },
+        rules: {
+          allocate: [
+            { required: true, message: '请选择接收对象', trigger: 'change' },
+          ],
+          about: [
+            { required: true, message: '请选择转移相关', trigger: 'change' },
+          ],
+        },
       }
-    }
-  },
-  components: {
-    Transfer
-  },
-  methods: {
-    handleClose () {
-      this.$refs.form.resetFields()
     },
-    choose () {
-      this.$refs.transfer.innerVisible = true
-    }
+    methods: {
+      handleClose() {
+        this.$refs.form.resetFields()
+      },
+      choose() {
+        this.$refs.transfer.innerVisible = true
+      },
+    },
   }
-}
 </script>
 
-<style>
-</style>
+<style></style>

+ 45 - 45
src/views/customer/components/ToOpen.vue

@@ -7,60 +7,60 @@
  * @FilePath: \opms_frontend\src\views\customer\components\ToOpen.vue
 -->
 <template>
-  <el-dialog :visible.sync="visible"
-             width="30%"
-             @close="handleClose"
-             title="转移客户">
-    <el-form :model="form"
-             ref="form"
-             label-width="100px">
-      <el-form-item label="移入公海原因"
-                    prop="remark">
-        <el-input v-model="form.remark"
-                  type="textarea"
-                  :rows="5"
-                  maxlength="300"
-                  show-word-limit
-                  resize="none"></el-input>
-        <span>* 转移到公海后此客户数据将属于公共资源,原归属人员不能再维护跟进和更新此客户数据。</span>
+  <el-dialog
+    title="转移客户"
+    :visible.sync="visible"
+    width="30%"
+    @close="handleClose">
+    <el-form ref="form" label-width="100px" :model="form">
+      <el-form-item label="移入公海原因" prop="remark">
+        <el-input
+          v-model="form.remark"
+          maxlength="300"
+          resize="none"
+          :rows="5"
+          show-word-limit
+          type="textarea" />
+        <span>
+          *
+          转移到公海后此客户数据将属于公共资源,原归属人员不能再维护跟进和更新此客户数据。
+        </span>
       </el-form-item>
     </el-form>
     <span slot="footer">
-      <el-button size="mini"
-                 @click="visible = false">取消</el-button>
-      <el-button size="mini"
-                 type="primary"
-                 @click="handleConfirm">确定</el-button>
+      <el-button size="mini" @click="visible = false">取消</el-button>
+      <el-button size="mini" type="primary" @click="handleConfirm">
+        确定
+      </el-button>
     </span>
   </el-dialog>
 </template>
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-export default {
-  data () {
-    return {
-      visible: false,
-      form: {
-        ids: [],
-        remark: ''
+  import to from 'await-to-js'
+  import api from '@/api/customer'
+  export default {
+    data() {
+      return {
+        visible: false,
+        form: {
+          ids: [],
+          remark: '',
+        },
       }
-    }
-  },
-  methods: {
-    handleClose () {
-      this.$refs.form.resetFields()
     },
-    async handleConfirm () {
-      const [err, res] = await to(api.moveToPubic({ ...this.form }))
-      if (err) return
-      this.$message.success(res.msg)
-      this.visible = false
-      this.$emit('refresh')
-    }
+    methods: {
+      handleClose() {
+        this.$refs.form.resetFields()
+      },
+      async handleConfirm() {
+        const [err, res] = await to(api.moveToPubic({ ...this.form }))
+        if (err) return
+        this.$message.success(res.msg)
+        this.visible = false
+        this.$emit('refresh')
+      },
+    },
   }
-}
 </script>
 
-<style>
-</style>
+<style></style>

+ 96 - 95
src/views/customer/components/Transfer.vue

@@ -7,144 +7,145 @@
  * @FilePath: \opms_frontend\src\views\customer\components\Transfer.vue
 -->
 <template>
-  <el-dialog width="40%"
-             title="选择销售工程师"
-             :visible.sync="innerVisible"
-             append-to-body>
+  <el-dialog
+    append-to-body
+    title="选择销售工程师"
+    :visible.sync="innerVisible"
+    width="40%">
     <el-row class="transfer">
       <el-col :span="12">
         <header>
-          <el-input v-model="keyword"
-                    placeholder="请输入关键字"
-                    suffix-icon="el-icon-search"
-                    clearable></el-input>
+          <el-input
+            v-model="keyword"
+            clearable
+            placeholder="请输入关键字"
+            suffix-icon="el-icon-search" />
         </header>
         <el-dropdown>
           <span class="el-dropdown-link">
-            按字母顺序查看<i class="el-icon-arrow-down el-icon--right"></i>
+            按字母顺序查看
+            <i class="el-icon-arrow-down el-icon--right"></i>
           </span>
           <el-dropdown-menu slot="dropdown">
             <el-dropdown-item>按创建顺序查看</el-dropdown-item>
           </el-dropdown-menu>
         </el-dropdown>
         <ul class="options">
-          <li v-for="(item,index) in options"
-              :key="index">
-            <span>{{item.name}}</span>
-            <i class="el-icon-arrow-right"
-               @click="transfer(index)"></i>
+          <li v-for="(item, index) in options" :key="index">
+            <span>{{ item.name }}</span>
+            <i class="el-icon-arrow-right" @click="transfer(index)"></i>
           </li>
         </ul>
       </el-col>
       <el-col :span="12">
         <header>
-          <span>已选: {{selected.length}}个员工</span>
-          <el-button type="text"
-                     :disabled="selected.length == 0"
-                     @click="clear">清空</el-button>
+          <span>已选: {{ selected.length }}个员工</span>
+          <el-button
+            :disabled="selected.length == 0"
+            type="text"
+            @click="clear">
+            清空
+          </el-button>
         </header>
         <ul class="options">
-          <li v-for="(item,index) in selected"
-              :key="index">
-            <span>{{item.name}}</span>
+          <li v-for="(item, index) in selected" :key="index">
+            <span>{{ item.name }}</span>
           </li>
         </ul>
       </el-col>
     </el-row>
     <span slot="footer">
-      <el-button type="primary"
-                 size="mini">保存</el-button>
-      <el-button size="mini"
-                 @click="innerVisible = false">取消</el-button>
+      <el-button size="mini" type="primary">保存</el-button>
+      <el-button size="mini" @click="innerVisible = false">取消</el-button>
     </span>
   </el-dialog>
 </template>
 
 <script>
-export default {
-  data () {
-    return {
-      innerVisible: false,
-      keyword: '',
-      options: [],
-      selected: []
-    }
-  },
-  mounted () {
-    this.options = [
-      {
-        name: 'wlj'
+  export default {
+    data() {
+      return {
+        innerVisible: false,
+        keyword: '',
+        options: [],
+        selected: [],
       }
-    ]
-  },
-  methods: {
-    transfer (index) {
-      const arr = this.options.splice(index, 1)
-      if (arr[0]) this.selected.push(arr[0])
     },
-    clear () {
-      this.selected = []
+    mounted() {
       this.options = [
         {
-          name: 'wlj'
-        }
+          name: 'wlj',
+        },
       ]
-    }
+    },
+    methods: {
+      transfer(index) {
+        const arr = this.options.splice(index, 1)
+        if (arr[0]) this.selected.push(arr[0])
+      },
+      clear() {
+        this.selected = []
+        this.options = [
+          {
+            name: 'wlj',
+          },
+        ]
+      },
+    },
   }
-}
 </script>
 
 <style lang="scss" scoped>
-.transfer {
-  height: 500px;
-  border: 1px solid #ebeef5;
-  .el-col {
-    height: 100%;
-    &:first-child {
-      border-right: 1px solid #ebeef5;
-    }
-    .el-dropdown {
-      height: 50px;
-      line-height: 50px;
-      margin: 0 8px;
-      width: calc(100% - 16px);
-      border-bottom: 1px solid #ebeef5;
-      span {
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
+  .transfer {
+    height: 500px;
+    border: 1px solid #ebeef5;
+    .el-col {
+      height: 100%;
+      &:first-child {
+        border-right: 1px solid #ebeef5;
       }
-    }
-    .options {
-      margin: 0;
-      padding: 0 10px;
-      list-style: none;
-      height: 400px;
-      overflow-y: auto;
-      li {
+      .el-dropdown {
         height: 50px;
         line-height: 50px;
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        & i {
-          transition: all 0.3s;
-          cursor: pointer;
-          &:hover {
-            color: #1d66dc;
-            font-weight: bold;
+        margin: 0 8px;
+        width: calc(100% - 16px);
+        border-bottom: 1px solid #ebeef5;
+        span {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+        }
+      }
+      .options {
+        margin: 0;
+        padding: 0 10px;
+        list-style: none;
+        height: 400px;
+        overflow-y: auto;
+        li {
+          height: 50px;
+          line-height: 50px;
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          & i {
+            transition: all 0.3s;
+            cursor: pointer;
+            &:hover {
+              color: #1d66dc;
+              font-weight: bold;
+            }
           }
         }
       }
     }
+    header {
+      height: 50px;
+      padding: 9px 8px;
+      border-bottom: 1px solid #ebeef5;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
   }
-  header {
-    height: 50px;
-    padding: 9px 8px;
-    border-bottom: 1px solid #ebeef5;
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-  }
-}
-</style>
+</style>

+ 682 - 403
src/views/customer/detail.vue

@@ -2,7 +2,7 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-26 09:30:47
  * @LastEditors: wanglj
- * @LastEditTime: 2022-12-28 16:28:54
+ * @LastEditTime: 2022-12-30 14:38:40
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\detail.vue
 -->
@@ -13,175 +13,290 @@
         <div class="title">
           <p>客户</p>
           <h3>
-            {{detail.custName}}
+            {{ detail.custName }}
             <span>
-              <template v-if="private == 1">
-                <el-button size="mini"
-                           @click="handleShift">转移客户</el-button>
-                <el-button size="mini"
-                           @click="handleToOpen">移入公海</el-button>
-                <el-button size="mini">创建项目</el-button>
+              <template v-if="privateCus == 1">
+                <el-button @click="handleShift">转移客户</el-button>
+                <el-button @click="handleToOpen">移入公海</el-button>
+                <el-button>创建项目</el-button>
               </template>
               <template v-else>
-                <el-button size="mini"
-                           @click="handleReceive">领取客户</el-button>
-                <el-button size="mini"
-                           @click="$refs.allocate.visible = true">分配客户</el-button>
+                <el-button @click="handleReceive">领取客户</el-button>
+                <el-button @click="$refs.allocate.visible = true">
+                  分配客户
+                </el-button>
               </template>
             </span>
           </h3>
         </div>
         <header>
-          <el-descriptions :column="6"
-                           direction="vertical"
-                           :colon="false">
-            <el-descriptions-item label="客户编码"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.custCode}}</el-descriptions-item>
-            <el-descriptions-item label="助记名"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.abbrName}}</el-descriptions-item>
-            <el-descriptions-item label="客户级别"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.custLevel}}</el-descriptions-item>
-            <el-descriptions-item label="客户行业"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.custIndustry}}</el-descriptions-item>
-            <el-descriptions-item label="客户状态"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.custStatus == 10 ? '正常' : '异常'}}</el-descriptions-item>
-            <el-descriptions-item label="最后跟进时间"
-                                  label-class-name="my-label"
-                                  content-class-name="my-content">{{detail.followUpDate}}</el-descriptions-item>
+          <el-descriptions :colon="false" :column="6" direction="vertical">
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="客户编码"
+              label-class-name="my-label">
+              {{ detail.custCode }}
+            </el-descriptions-item>
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="助记名"
+              label-class-name="my-label">
+              {{ detail.abbrName }}
+            </el-descriptions-item>
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="客户级别"
+              label-class-name="my-label">
+              {{ detail.custLevel }}
+            </el-descriptions-item>
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="客户行业"
+              label-class-name="my-label">
+              {{ detail.custIndustry }}
+            </el-descriptions-item>
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="客户状态"
+              label-class-name="my-label">
+              {{ detail.custStatus == 10 ? '正常' : '异常' }}
+            </el-descriptions-item>
+            <el-descriptions-item
+              content-class-name="my-content"
+              label="最后跟进时间"
+              label-class-name="my-label">
+              {{ detail.followUpDate }}
+            </el-descriptions-item>
           </el-descriptions>
         </header>
-        <el-tabs v-model="activeName"
-                 @tab-click="handleClick">
-          <el-tab-pane label="详细信息"
-                       name="first">详细信息</el-tab-pane>
-          <el-tab-pane label="联系人"
-                       name="second">
+        <el-tabs v-model="activeName" @tab-click="handleClick">
+          <el-tab-pane label="详细信息" name="first">
+            <el-descriptions border :column="2" size="medium">
+              <el-descriptions-item label="客户级别">
+                {{ detail.custLevel }}
+              </el-descriptions-item>
+              <el-descriptions-item label="下次联系时间">
+                {{ detail.followUpDate }}
+              </el-descriptions-item>
+              <el-descriptions-item label="所在地区">
+                {{ detail.custLocation }}
+              </el-descriptions-item>
+              <el-descriptions-item label="详细地址">
+                {{ detail.custAddress }}
+              </el-descriptions-item>
+              <el-descriptions-item label="创建人">
+                {{ detail.createdName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="创建时间">
+                {{ detail.createdTime }}
+              </el-descriptions-item>
+              <el-descriptions-item label="跟进次数">
+                {{ abstract.followContent }}
+              </el-descriptions-item>
+              <el-descriptions-item label="未跟进时长">
+                {{ abstract.notFollowDay }}
+              </el-descriptions-item>
+              <el-descriptions-item label="商机数量">
+                {{ abstract.business }}
+              </el-descriptions-item>
+              <el-descriptions-item label="商机总额">
+                {{ abstract.businessTotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="成交次数">
+                {{ abstract.dealCotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="成交总额">
+                {{ abstract.dealTotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="回款总额">
+                {{ abstract.paymentTotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="未回款总额">
+                {{ abstract.notPaymentTotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="开票总额" :span="24">
+                {{ abstract.drawTotal }}
+              </el-descriptions-item>
+              <el-descriptions-item label="备注" :span="24">
+                {{ detail.remark }}
+              </el-descriptions-item>
+            </el-descriptions>
+          </el-tab-pane>
+          <el-tab-pane label="联系人" name="second">
             <vab-query-form>
               <vab-query-form-left-panel :span="12">
-                <el-input placeholder="请输入单据名称/编号"
-                          prefix-icon="el-icon-search"
-                          style="width:50%"></el-input>
+                <el-input
+                  placeholder="请输入单据名称/编号"
+                  prefix-icon="el-icon-search"
+                  style="width: 50%" />
               </vab-query-form-left-panel>
               <vab-query-form-right-panel :span="12">
-                <el-button icon="el-icon-plus"
-                           @click="addContact">
+                <el-button icon="el-icon-plus" @click="addContact">
                   新建联系人
                 </el-button>
               </vab-query-form-right-panel>
             </vab-query-form>
-            <el-table v-loading="listLoading"
-                      :data="contactList"
-                      border
-                      height="calc(100% - 42px)"
-                      @selection-change="setSelectRows">
-              <el-table-column type="selection"
-                               align="center" />
-              <el-table-column align="center"
-                               label="姓名"
-                               prop="cuctName"></el-table-column>
-              <el-table-column align="center"
-                               label="岗位"
-                               prop="postion"></el-table-column>
-              <el-table-column align="center"
-                               label="电话"
-                               prop="telephone"></el-table-column>
-              <el-table-column align="center"
-                               label="微信"
-                               prop="wechat"></el-table-column>
-              <el-table-column align="center"
-                               label="邮箱"
-                               prop="email"></el-table-column>
-              <el-table-column align="center"
-                               label="是否决策人">
+            <el-table
+              v-loading="listLoading"
+              border
+              :data="contactList"
+              height="calc(100% - 42px)"
+              @selection-change="setSelectRows">
+              <el-table-column align="center" type="selection" />
+              <el-table-column align="center" label="姓名" prop="cuctName" />
+              <el-table-column align="center" label="岗位" prop="postion" />
+              <el-table-column align="center" label="电话" prop="telephone" />
+              <el-table-column align="center" label="微信" prop="wechat" />
+              <el-table-column align="center" label="邮箱" prop="email" />
+              <el-table-column align="center" label="是否决策人">
                 <template slot-scope="scope">
-                  <el-switch v-model="scope.row.policy"
-                             disabled
-                             :active-value="1"
-                             :inactive-value="0"></el-switch>
+                  <el-switch
+                    v-model="scope.row.policy"
+                    :active-value="1"
+                    disabled
+                    :inactive-value="0" />
                 </template>
               </el-table-column>
-              <el-table-column align="center"
-                               label="操作">
+              <el-table-column align="center" label="操作">
                 <template slot-scope="scope">
-                  <el-button type="text"
-                             @click="contactEdit(scope.row)">编辑</el-button>
-                  <el-button type="text"
-                             @click="contactDel(scope.row)">删除</el-button>
+                  <el-button type="text" @click="contactEdit(scope.row)">
+                    编辑
+                  </el-button>
+                  <el-button type="text" @click="contactDel(scope.row)">
+                    删除
+                  </el-button>
                 </template>
               </el-table-column>
             </el-table>
           </el-tab-pane>
-          <el-tab-pane label="项目记录"
-                       name="third">项目记录</el-tab-pane>
-          <el-tab-pane label="合同记录"
-                       name="fourth">合同记录</el-tab-pane>
-          <el-tab-pane label="工单记录"
-                       name="fifth">工单记录</el-tab-pane>
-          <el-tab-pane label="归属记录"
-                       name="sixth">
-            <el-table v-loading="listLoading"
-                      :data="belongs"
-                      border
-                      height="calc(100% - 42px)">
-              <el-table-column align="center"
-                               label="归属销售"
-                               prop="saleName"></el-table-column>
-              <el-table-column align="center"
-                               label="原来归属"
-                               prop="origSaleName"></el-table-column>
-              <el-table-column align="center"
-                               label="操作方式"
-                               prop="opnType">
+          <el-tab-pane label="跟进记录" name="third">
+            <ul class="follow">
+              <li v-for="(date, index) in followList" :key="index">
+                <div class="date">
+                  <h2>{{ date.followDay.split('-')[2] }}</h2>
+                  <h3>
+                    {{ date.followDay.split('-').splice(0, 2).join('.') }}
+                  </h3>
+                </div>
+                <ul class="content">
+                  <li v-for="(item, idx) in date.followupList" :key="idx">
+                    <!-- <el-avatar class="user-avatar"
+              :src="avatar" />-->
+                    <div class="text-container">
+                      <vab-icon
+                        class="user-avatar"
+                        icon="account-circle-fill" />
+                      <div class="text">
+                        <p class="action">
+                          <span>
+                            {{ item.contactsName }} 跟进({{
+                              formatType(item.followType)
+                            }})
+                          </span>
+                          <span>
+                            <vab-icon icon="time-line" />
+                            {{ item.followDate }}
+                          </span>
+                        </p>
+                        <p>{{ item.followContent }}</p>
+                        <div class="footer">
+                          <p>
+                            来自客户:
+                            <span>{{ item.custName }}</span>
+                          </p>
+                          <div>
+                            <el-button size="mini" @click="showDetail(item)">
+                              <vab-icon icon="arrow-right-circle-fill" />
+                              详情
+                            </el-button>
+                            <el-button size="mini" @click="showComment(item)">
+                              评论({{ item.commentNumber }})
+                            </el-button>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <transition name="height">
+                      <ul v-if="item.showComment" class="comments">
+                        <li v-for="comment in item.comments" :key="comment.id">
+                          <vab-icon
+                            class="user-avatar"
+                            icon="account-circle-fill" />
+                          <div class="text">
+                            <p>{{ comment.createdName }}</p>
+                            <p>{{ comment.content }}</p>
+                            <p>{{ comment.createdTime }}</p>
+                          </div>
+                        </li>
+                      </ul>
+                    </transition>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+          </el-tab-pane>
+          <el-tab-pane label="项目记录" name="fourth">项目记录</el-tab-pane>
+          <el-tab-pane label="合同记录" name="fifth">合同记录</el-tab-pane>
+          <el-tab-pane label="工单记录" name="sixth">工单记录</el-tab-pane>
+          <el-tab-pane label="归属记录" name="seventh">
+            <el-table
+              v-loading="listLoading"
+              border
+              :data="belongs"
+              height="calc(100% - 42px)">
+              <el-table-column
+                align="center"
+                label="归属销售"
+                prop="saleName" />
+              <el-table-column
+                align="center"
+                label="原来归属"
+                prop="origSaleName" />
+              <el-table-column align="center" label="操作方式" prop="opnType">
                 <template slot-scope="scope">
-                  {{scope.row.opnType == 10 ? '分配' : '转移'}}
+                  {{ scope.row.opnType == 10 ? '分配' : '转移' }}
                 </template>
               </el-table-column>
-              <el-table-column align="center"
-                               label="操作人"
-                               prop="opnPeople"></el-table-column>
-              <el-table-column align="center"
-                               label="操作时间"
-                               prop="opnDatetime"></el-table-column>
-              <el-table-column align="center"
-                               label="备注"
-                               prop="remark">
-              </el-table-column>
+              <el-table-column align="center" label="操作人" prop="opnPeople" />
+              <el-table-column
+                align="center"
+                label="操作时间"
+                prop="opnDatetime" />
+              <el-table-column align="center" label="备注" prop="remark" />
             </el-table>
           </el-tab-pane>
         </el-tabs>
       </el-col>
       <el-col :span="8">
         <div class="buttons">
-          <el-button type="primary"
-                     @click="handleEdit">编辑</el-button>
+          <el-button type="primary" @click="handleEdit">编辑</el-button>
           <el-button @click="handleDelete">删除</el-button>
         </div>
         <ul class="records">
-          <li v-for="(value, key) in records"
-              :key="key">
+          <li v-for="(value, key) in records" :key="key">
             <div class="date">
-              <h2>{{key.split('-')[2]}}</h2>
-              <h3>{{key.split('-').splice(0, 2).join('.')}}</h3>
+              <h2>{{ key.split('-')[2] }}</h2>
+              <h3>{{ key.split('-').splice(0, 2).join('.') }}</h3>
             </div>
             <ul class="content">
-              <li v-for="item in records[key]">
+              <li v-for="(item, index) in records[key]" :key="index">
                 <!-- <el-avatar class="user-avatar"
                            :src="avatar" /> -->
-                <vab-icon class="user-avatar"
-                          icon="account-circle-fill" />
+                <vab-icon class="user-avatar" icon="account-circle-fill" />
                 <div class="text">
-                  <p class="action">{{item.opnPeople}} {{item.opnType}}</p>
-                  <p>{{item.opnDate}}</p>
-                  <p v-if="item.opnContent.custName">客户名称:<span>{{item.opnContent.custName}}</span></p>
+                  <p class="action">{{ item.opnPeople }} {{ item.opnType }}</p>
+                  <p>{{ item.opnDate }}</p>
+                  <p v-if="item.opnContent.custName">
+                    客户名称:
+                    <span>{{ item.opnContent.custName }}</span>
+                  </p>
                   <template v-else-if="item.opnContent.cuctName">
-                    <p>联系人名称:<span>{{item.opnContent.cuctName}}</span></p>
-                    <p>职务:{{item.opnContent.postion}}</p>
-                    <p>手机:{{item.opnContent.telephone}}</p>
+                    <p>
+                      联系人名称:
+                      <span>{{ item.opnContent.cuctName }}</span>
+                    </p>
+                    <p>职务:{{ item.opnContent.postion }}</p>
+                    <p>手机:{{ item.opnContent.telephone }}</p>
                   </template>
                 </div>
               </li>
@@ -190,328 +305,492 @@
         </ul>
       </el-col>
     </el-row>
-    <Contact ref="contact"
-             @contactSave="contactSave"></Contact>
-    <Edit ref="edit"
-          @customerSave="customerSave"></Edit>
+    <Contact ref="contact" @contactSave="contactSave" />
+    <Edit ref="edit" @customerSave="customerSave" />
     <!-- 分配客户 -->
-    <Allocate ref="allocate"></Allocate>
+    <Allocate ref="allocate" />
     <!-- 转移客户 -->
-    <Shift ref="shift"></Shift>
+    <Shift ref="shift" />
     <!-- 移入公海 -->
-    <ToOpen ref="toOpen"
-            @refresh="back"></ToOpen>
+    <ToOpen ref="toOpen" @refresh="back" />
+    <!-- 跟进详情 -->
+    <FollowDetail ref="followDetail" />
   </div>
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import api from '@/api/customer'
-import to from 'await-to-js'
-import Contact from './components/Contact'
-import Edit from './components/Edit'
-import Allocate from './components/Allocate'
-import Shift from './components/Shift'
-import ToOpen from './components/ToOpen'
-export default {
-  data () {
-    return {
-      id: '',
-      private: '',
-      detail: {
-        custCode: '',//客户编码
-        abbrName: '',//助记名
-        level: '', //客户级别
-        indusTry: '',//客户行业
-        custStatus: '',//客户状态
-        followUpDate: ''//最后跟进时间
-      },
-      activeName: 'first',
-      listLoading: false,
-      contactList: [],
-      selectRows: [],
-      records: [],//操作记录
-      belongs: []
-    }
-  },
-  components: {
-    Edit,
-    Contact,
-    Allocate,
-    Shift,
-    ToOpen
-  },
-  computed: {
-    ...mapGetters({
-      avatar: 'user/avatar',
-      username: 'user/username',
-    }),
-  },
-  mounted () {
-    this.id = this.$route.query.id
-    this.private = this.$route.query.private
-    this.init()
-    this.getDynamics()
-  },
-  methods: {
-    async init () {
-      const [err, res] = await to(api.getDetail({ ids: [parseInt(this.id)] }))
-      if (err) return
-      if (res.data.list[0]) this.detail = res.data.list[0]
+  import { mapGetters } from 'vuex'
+  import api from '@/api/customer'
+  import follow from '@/api/customer/follow'
+  import to from 'await-to-js'
+  import Contact from './components/Contact'
+  import Edit from './components/Edit'
+  import Allocate from './components/Allocate'
+  import Shift from './components/Shift'
+  import ToOpen from './components/ToOpen'
+  import FollowDetail from './components/FollowDetail.vue'
+  export default {
+    name: 'CustomerDetail',
+    components: {
+      Edit,
+      Contact,
+      Allocate,
+      Shift,
+      ToOpen,
+      FollowDetail,
     },
-    async getDynamics () {
-      const [err, res] = await to(api.dynamicsList({ custId: parseInt(this.id) }))
-      if (err) return
-      if (res.data.list[0]) {
-        let obj = res.data.list[0]
-        for (const key in obj) {
-          for (const item of obj[key]) {
-            item.opnContent = JSON.parse(item.opnContent)
-          }
-        }
-        this.records = obj
+    data() {
+      return {
+        id: '',
+        privateCus: '',
+        detail: {
+          custCode: '', //客户编码
+          abbrName: '', //助记名
+          level: '', //客户级别
+          indusTry: '', //客户行业
+          custStatus: '', //客户状态
+          followUpDate: '', //最后跟进时间
+        },
+        abstract: {
+          followContent: '', //跟进次数
+          notFollowDay: '', //未跟进天数
+          business: '', //商机数量
+          businessTotal: '', //商机总额
+          dealCotal: '', //成交次数
+          dealTotal: '', //成交总额
+          paymentTotal: '', //回款总额
+          notPaymentTotal: '', //未回款总额
+          drawTotal: '', //开票总额
+        },
+        activeName: 'first',
+        listLoading: false,
+        contactList: [],
+        selectRows: [],
+        records: [], //操作记录
+        followList: [], //跟进记录
+        belongs: [],
       }
     },
-    setSelectRows (val) {
-      this.selectRows = val
-    },
-    async handleClick (tab) {
-      let err, res
-      if (tab.name == 'second') {
-        [err, res] = await to(api.getContact({ custId: parseInt(this.id) }))
-        if (err) return
-        this.contactList = res.data.list || []
-      } else if (tab.name == 'sixth' && this.belongs.length == 0) {
-        [err, res] = await to(api.getBelongs({ Id: parseInt(this.id) }))
-        if (err) return
-        this.belongs = res.data.list || []
-      }
+    computed: {
+      ...mapGetters({
+        avatar: 'user/avatar',
+        username: 'user/username',
+      }),
     },
-    // 添加联系人
-    addContact () {
-      this.$refs.contact.contactForm.custId = this.detail.id
-      this.$refs.contact.contactForm.custName = this.detail.custName
-      this.$refs.contact.contactVisible = true
-    },
-    // 保存联系人
-    contactSave () {
-      this.handleClick({ name: 'second' })
+    mounted() {
+      this.id = this.$route.query.id
+      this.privateCus = this.$route.query.privateCus
+      this.init()
       this.getDynamics()
     },
-    // 编辑客户
-    handleEdit () {
-      this.$refs.edit.title = '编辑客户'
-      this.$refs.edit.editForm = { ...this.detail }
-      this.$refs.edit.editVisible = true
-      this.$refs.edit.showLocation()
-    },
-    // 编辑联系人
-    contactEdit (row) {
-      this.$refs.contact.contactForm = { ...row }
-      this.$refs.contact.contactVisible = true
-    },
-    // 删除联系人
-    contactDel (row) {
-      this.$confirm('确认删除?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.deleteContact({ Id: row.id, custId: parseInt(this.id) }))
-        if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '删除成功!'
-          });
-          this.contactSave()
-        }
-      }).catch(() => {
-      })
-    },
-    // 转移客户
-    handleShift () {
-      this.$refs.shift.visible = true
-    },
-    // 移入公海
-    handleToOpen () {
-      this.$refs.toOpen.form.ids = [parseInt(this.id)]
-      this.$refs.toOpen.visible = true
-    },
-    // 客户删除
-    handleDelete () {
-      this.$confirm('确认删除?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.deleteCustomer({ Id: parseInt(this.id) }))
+    methods: {
+      async init() {
+        Promise.all([
+          api.getDetail({ ids: [parseInt(this.id)] }),
+          api.getAbstract({ id: parseInt(this.id) }),
+        ]).then(([detail, abstract]) => {
+          if (detail.data.list[0]) this.detail = detail.data.list[0]
+          if (abstract.data.list) this.abstract = abstract.data.list
+        })
+      },
+      async getDynamics() {
+        const [err, res] = await to(
+          api.dynamicsList({ custId: parseInt(this.id) })
+        )
         if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '删除成功!'
-          });
-          this.$router.go(-1)
+        if (res.data.list[0]) {
+          let obj = res.data.list[0]
+          for (const key in obj) {
+            for (const item of obj[key]) {
+              item.opnContent = JSON.parse(item.opnContent)
+            }
+          }
+          this.records = obj
         }
-      }).catch(() => {
-      })
-    },
-    back () {
-      this.$router.push('/customer/openSea')
-    },
-    // 领取
-    handleReceive () {
-      this.$confirm('确认领取客户?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.receiveCustomer({ ids: this.id }))
-        if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '领取成功!'
-          });
+      },
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      async handleClick(tab) {
+        let err, res
+        if (tab.name == 'second') {
+          ;[err, res] = await to(api.getContact({ custId: parseInt(this.id) }))
+          if (err) return
+          this.contactList = res.data.list || []
+        } else if (tab.name == 'third') {
+          let params = {
+            custId: this.id,
+            DaysBeforeToday: 9999,
+          }
+          ;[err, res] = await to(follow.getListByDay(params))
+          if (err) return
+          this.followList = res.data.list || []
+        } else if (tab.name == 'seventh' && this.belongs.length == 0) {
+          ;[err, res] = await to(api.getBelongs({ Id: parseInt(this.id) }))
+          if (err) return
+          this.belongs = res.data.list || []
         }
-      }).catch(() => {
-      })
+      },
+      // 添加联系人
+      addContact() {
+        this.$refs.contact.contactForm.custId = this.detail.id
+        this.$refs.contact.contactForm.custName = this.detail.custName
+        this.$refs.contact.contactVisible = true
+      },
+      // 保存联系人
+      contactSave() {
+        this.handleClick({ name: 'second' })
+        this.getDynamics()
+      },
+      // 编辑客户
+      handleEdit() {
+        this.$refs.edit.title = '编辑客户'
+        this.$refs.edit.editForm = { ...this.detail }
+        this.$refs.edit.editVisible = true
+        this.$refs.edit.showLocation()
+      },
+      // 编辑联系人
+      contactEdit(row) {
+        this.$refs.contact.contactForm = { ...row }
+        this.$refs.contact.contactVisible = true
+      },
+      // 删除联系人
+      contactDel(row) {
+        this.$confirm('确认删除?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(
+              api.deleteContact({ Id: row.id, custId: parseInt(this.id) })
+            )
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+              this.contactSave()
+            }
+          })
+          .catch(() => {})
+      },
+      // 转移客户
+      handleShift() {
+        this.$refs.shift.visible = true
+      },
+      // 移入公海
+      handleToOpen() {
+        this.$refs.toOpen.form.ids = [parseInt(this.id)]
+        this.$refs.toOpen.visible = true
+      },
+      // 客户删除
+      handleDelete() {
+        this.$confirm('确认删除?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(
+              api.deleteCustomer({ Id: parseInt(this.id) })
+            )
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+              this.$router.go(-1)
+            }
+          })
+          .catch(() => {})
+      },
+      back() {
+        this.$router.push('/customer/openSea')
+      },
+      // 领取
+      handleReceive() {
+        this.$confirm('确认领取客户?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(api.receiveCustomer({ ids: this.id }))
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '领取成功!',
+              })
+            }
+          })
+          .catch(() => {})
+      },
+      customerSave() {
+        this.init()
+        this.getDynamics()
+      },
+      formatType(val) {
+        let str = ''
+        if (val == 10) str = '电话'
+        else if (val == 20) str = '邮件'
+        else if (val == 30) str = '拜访'
+        return str
+      },
+      // 跟进记录详情
+      showDetail(row) {
+        this.$refs.followDetail.init({ ...row })
+      },
+      // 展开评论
+      showComment(row) {
+        if (!row.comments.length) return this.$message.warning('暂无评论')
+        row.showComment = !row.showComment
+        this.$forceUpdate()
+      },
     },
-    customerSave () {
-      this.init()
-      this.getDynamics()
-    }
   }
-}
 </script>
 
 <style lang="scss" scoped>
-$base: '.detail';
-#{$base} {
-  height: calc(100vh - 60px - 50px - 12px * 2 - 40px);
-  display: flex;
-  padding: 20px 40px;
-  > .el-row {
-    flex: 1;
-    > .el-col {
-      height: 100%;
-    }
-  }
-  .title {
-    p,
-    h3 {
-      margin: 0;
-    }
-    p {
-      font-size: 14px;
-      font-weight: 400;
-      line-height: 22px;
-    }
-    h3 {
-      font-size: 24px;
-      font-weight: 500;
-      line-height: 36px;
-      color: #333;
-      display: flex;
-      justify-content: space-between;
-    }
-  }
-  header {
-    height: 74px;
-    background: rgba(196, 196, 196, 0.5);
-    border-radius: 4px;
-    display: flex;
-    align-items: center;
-    padding: 0 20px;
-    margin-top: 16px;
-    ::v-deep .el-descriptions__body {
-      background: transparent;
-    }
-    ::v-deep .my-label {
-      font-size: 14px;
-      font-weight: 600;
-      color: #fff;
-    }
-    ::v-deep .my-content {
-      font-size: 14px;
-      font-weight: 600;
-      color: #333;
-    }
-  }
-  .el-tabs {
-    height: calc(100% - 148px);
+  $base: '.detail';
+  #{$base} {
+    height: calc(100vh - 60px - 50px - 12px * 2 - 40px);
     display: flex;
-    flex-direction: column;
-    ::v-deep .el-tabs__content {
+    padding: 20px 40px;
+    > .el-row {
       flex: 1;
-      .el-tab-pane {
+      > .el-col {
         height: 100%;
       }
     }
-  }
-  .buttons {
-    padding-top: 26px;
-    text-align: right;
-    height: 58px;
-  }
-  .records {
-    margin: 0;
-    padding: 10px 20px;
-    list-style: none;
-    height: calc(100% - 58px);
-    overflow-y: auto;
-    > li {
-      display: flex;
-      & + li {
-        margin-top: 10px;
+    .title {
+      p,
+      h3 {
+        margin: 0;
+      }
+      p {
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 22px;
+      }
+      h3 {
+        font-size: 24px;
+        font-weight: 500;
+        line-height: 36px;
+        color: #333;
+        display: flex;
+        justify-content: space-between;
       }
     }
-    .date {
-      width: 100px;
+    header {
+      height: 74px;
+      background: rgba(196, 196, 196, 0.5);
+      border-radius: 4px;
       display: flex;
-      flex-direction: column;
       align-items: center;
-      h2,
-      h3 {
-        margin: 0;
+      padding: 0 20px;
+      margin-top: 16px;
+      ::v-deep .el-descriptions__body {
+        background: transparent;
+      }
+      ::v-deep .my-label {
+        font-size: 14px;
+        font-weight: 600;
+        color: #fff;
       }
-      h2 {
-        font-size: 26px;
-        line-height: 32px;
+      ::v-deep .my-content {
+        font-size: 14px;
+        font-weight: 600;
+        color: #333;
       }
     }
-    .content {
-      flex: 1;
+    .el-tabs {
+      height: calc(100% - 148px);
+      display: flex;
+      flex-direction: column;
+      ::v-deep .el-tabs__content {
+        flex: 1;
+        .el-tab-pane {
+          height: 100%;
+        }
+      }
+    }
+    .buttons {
+      padding-top: 28px;
+      text-align: right;
+    }
+    .records {
+      margin: 0;
+      padding: 10px 20px;
       list-style: none;
-      li {
+      height: calc(100% - 60px);
+      overflow-y: auto;
+      > li {
         display: flex;
         & + li {
-          margin-top: 20px;
+          margin-top: 10px;
         }
       }
-      .user-avatar {
-        font-size: 40px;
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        h2,
+        h3 {
+          margin: 0;
+        }
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+      .content {
+        flex: 1;
+        list-style: none;
+        li {
+          display: flex;
+          & + li {
+            margin-top: 10px;
+          }
+        }
+        .user-avatar {
+          font-size: 40px;
+        }
+        .text {
+          flex: 1;
+          padding-left: 20px;
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 20px;
+            span {
+              color: #1d66dc;
+            }
+          }
+          p:nth-child(2) {
+            margin-bottom: 10px;
+          }
+          .action {
+            font-weight: bold;
+            color: #333;
+          }
+        }
+      }
+    }
+    .follow {
+      height: 100%;
+      padding: 10px 20px;
+      overflow: auto;
+      > li {
+        display: flex;
+        + li {
+          margin-top: 10px;
+        }
       }
-      .text {
-        padding-left: 20px;
-        p {
-          font-weight: 500;
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        h2,
+        h3 {
           margin: 0;
-          line-height: 20px;
-          span {
-            color: #1d66dc;
+        }
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+      .content {
+        flex: 1;
+        list-style: none;
+        > li {
+          border: 1px solid rgb(215, 232, 244);
+          background: rgb(247, 251, 254);
+          border-radius: 4px;
+          padding: 8px;
+          overflow: hidden;
+
+          .text-container {
+            display: flex;
+          }
+          .comments {
+            padding-left: 60px;
+            margin-top: 10px;
+            max-height: 200px;
+            overflow: auto;
+            li {
+              display: flex;
+              border-top: 1px solid #e3e5e7;
+              .text {
+                flex: 1;
+                padding: 0 10px;
+                p {
+                  font-weight: 500;
+                  margin: 0;
+                  line-height: 32px;
+                }
+                p:first-child {
+                  line-height: 30px;
+                  font-weight: bold;
+                }
+                p:last-child {
+                  font-size: 12px;
+                  color: #9499a0;
+                  text-align: right;
+                }
+              }
+            }
+          }
+          + li {
+            margin-top: 10px;
           }
         }
-        p:nth-child(2) {
-          margin-bottom: 10px;
+        .user-avatar {
+          font-size: 40px;
         }
-        .action {
-          font-weight: bold;
-          color: #333;
+        .text {
+          flex: 1;
+          padding-left: 20px;
+          padding-right: 10px;
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 32px;
+            span {
+              color: #1d66dc;
+            }
+          }
+          .action {
+            display: flex;
+            justify-content: space-between;
+            span:first-child {
+              font-weight: bold;
+              color: #333;
+            }
+          }
+          .footer {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+          }
         }
       }
     }
   }
-}
-</style>
+  .height-enter-active,
+  .height-leave-active {
+    transition: all 0.5s;
+  }
+  .height-enter-to,
+  .height-leave {
+    height: 200px;
+  }
+  .height-enter, .height-leave-to /* .fade-leave-active below version 2.1.8 */ {
+    height: 0;
+  }
+</style>

+ 363 - 126
src/views/customer/follow.vue

@@ -2,151 +2,388 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-15 15:38:21
  * @LastEditors: wanglj
- * @LastEditTime: 2022-12-28 18:01:16
+ * @LastEditTime: 2022-12-30 14:36:03
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\follow.vue
 -->
 <template>
   <div class="follow-container">
-    <vab-query-form>
-      <vab-query-form-left-panel :span="12">
-
-      </vab-query-form-left-panel>
-      <vab-query-form-right-panel :span="12">
-
-      </vab-query-form-right-panel>
-    </vab-query-form>
-    <div class="follow"></div>
-    <!-- 新增编辑客户弹窗 -->
-    <Edit ref="edit"
-          @createContact="createContact"
-          @customerSave="customerSave"></Edit>
-    <!-- 新建联系人弹窗 -->
-    <Contact ref="contact"></Contact>
-    <!-- 分配客户 -->
-    <Allocate ref="allocate"></Allocate>
+    <el-row :gutter="10" style="margin-bottom: 10px">
+      <el-col :span="4">
+        <el-input v-model="queryForm.custId" placeholder="客户" />
+      </el-col>
+      <!-- <el-col :span="4">
+        <el-date-picker
+          v-model="queryForm.date"
+          end-placeholder="结束日期"
+          placeholder="时间范围"
+          start-placeholder="开始日期"
+          style="width: 100%"
+          type="daterange" />
+      </el-col> -->
+      <el-col :span="4">
+        <el-input v-model="queryForm.daysBeforeToday" placeholder="查询天数">
+          <template slot="append">天</template>
+        </el-input>
+      </el-col>
+      <el-col :span="4">
+        <el-input v-model="queryForm.targetType" placeholder="跟进类型" />
+      </el-col>
+      <el-col :span="4">
+        <el-input v-model="queryForm.indusTry" placeholder="跟进人员" />
+      </el-col>
+      <el-col :span="4">
+        <el-input v-model="queryForm.managerId" placeholder="创建者" />
+      </el-col>
+      <el-col :span="4">
+        <el-button icon="el-icon-plus" type="primary" @click="search">
+          查询
+        </el-button>
+        <el-button @click="addFollowUp">添加</el-button>
+        <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+      </el-col>
+    </el-row>
+    <div class="follow">
+      <ul
+        v-infinite-scroll="load"
+        class="records infinite-list"
+        :infinite-scroll-disabled="loadFlag"
+        :infinite-scroll-immediate-check="false">
+        <li v-for="(date, index) in records" :key="index">
+          <div class="date">
+            <h2>{{ date.followDay.split('-')[2] }}</h2>
+            <h3>{{ date.followDay.split('-').splice(0, 2).join('.') }}</h3>
+          </div>
+          <ul class="content">
+            <li v-for="(item, idx) in date.followupList" :key="idx">
+              <!-- <el-avatar class="user-avatar"
+              :src="avatar" />-->
+              <vab-icon class="user-avatar" icon="account-circle-fill" />
+              <div class="text">
+                <p class="action">
+                  <span>
+                    {{ item.contactsName }} 跟进({{
+                      formatType(item.followType)
+                    }})
+                  </span>
+                  <span>
+                    <vab-icon icon="time-line" />
+                    {{ item.followDate }}
+                  </span>
+                </p>
+                <p>{{ item.followContent }}</p>
+                <div class="footer">
+                  <p>
+                    来自客户:
+                    <span>{{ item.custName }}</span>
+                  </p>
+                  <div>
+                    <el-button size="mini" @click="showDetail(item)">
+                      <vab-icon icon="arrow-right-circle-fill" />
+                      详情
+                    </el-button>
+                    <el-button size="mini" @click="showComment(item.id)">
+                      <!-- <vab-icon icon="chat-3-fill" /> -->
+                      评论({{ item.commentNumber }})
+                    </el-button>
+                  </div>
+                </div>
+              </div>
+            </li>
+          </ul>
+        </li>
+      </ul>
+      <div class="comment">
+        <ul>
+          <li v-for="item in comments" :key="item.id">
+            <vab-icon class="user-avatar" icon="account-circle-fill" />
+            <div class="text">
+              <p>{{ item.createdName }}</p>
+              <p>{{ item.content }}</p>
+              <p>{{ item.createdTime }}</p>
+            </div>
+          </li>
+        </ul>
+        <div class="form">
+          <el-input
+            v-model="comment"
+            maxlength="300"
+            resize="none"
+            :rows="5"
+            show-word-limit
+            type="textarea" />
+          <el-button size="mini" type="primary" @click="handleComment">
+            评论
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <!-- 跟进详情 -->
+    <FollowDetail ref="followDetail" />
   </div>
 </template>
 
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-import Contact from './components/Contact'
-import Edit from './components/Edit'
-import Allocate from './components/Allocate'
-export default {
-  name: 'OpenSea',
-  data () {
-    return {
-      listLoading: false,
-      layout: 'total, sizes, prev, pager, next, jumper',
-      list: [],
-      total: 0,
-      queryForm: {
-        pageNum: 1,
-        pageSize: 10,
-        custCode: '',   // 客户编码
-        custName: '',    //客户名称
-        custIndustry: '',    // 客户行业  ()
-        custLevel: '',     //客户级别
-      },
-      selectRows: []
-    }
-  },
-  components: {
-    Contact,
-    Edit,
-    Allocate
-  },
-  mounted () {
-    this.fetchData()
-    this.getOptions()
-  },
-  methods: {
-    getOptions () {
-
+  import to from 'await-to-js'
+  import api from '@/api/customer/follow'
+  import FollowDetail from './components/FollowDetail.vue'
+  export default {
+    name: 'Follow',
+    components: {
+      FollowDetail,
     },
-    async fetchData () {
-
-    },
-    reset () {
-      this.queryForm = {
-        pageNum: 1,
-        pageSize: 10
+    data() {
+      return {
+        listLoading: false,
+        queryForm: {
+          custId: '',
+          targetType: '',
+          targetId: '',
+          managerId: '',
+          daysBeforeToday: 20,
+          date: [],
+        },
+        loadFlag: true,
+        records: [],
+        followId: '',
+        comment: '',
+        comments: [],
+        visible: false,
+        form: {
+          id: '',
+          followType: '',
+          followDate: '',
+          followContent: '',
+          targetId: '',
+          targetType: '',
+          targetName: '',
+          custId: '',
+          custName: '',
+          contactsId: 0,
+          contactsName: '',
+          reminders: '',
+          nextTime: '',
+          remark: '',
+          createdBy: '',
+          createdName: '',
+          createdTime: '',
+          updatedBy: '',
+          updatedName: '',
+          updatedTime: '',
+          deletedTime: '',
+        },
       }
-      this.fetchData()
     },
-    // 客户编辑
-    async handleEdit (row) {
-      this.$refs.edit.init([row.id])
+    mounted() {
+      this.fetchData()
+      this.getOptions()
     },
-    // 客户详情
-    handleDetail (row) {
-      this.$router.push({
-        path: '/customer/detail',
-        query: {
-          id: row.id
+    methods: {
+      getOptions() {},
+      search() {
+        this.queryForm.daysBeforeToday = 20
+        this.fetchData()
+      },
+      async fetchData() {
+        let params = { ...this.queryForm }
+        const [err, res] = await to(api.getListByDay(params))
+        if (err) return console.log(err, 'err')
+        this.records = res.data.list || []
+        await this.$nextTick()
+        this.loadFlag = false
+      },
+      load() {
+        this.queryForm.daysBeforeToday += 20
+        console.log('到底了', this.queryForm.daysBeforeToday)
+        this.fetchData()
+      },
+      reset() {
+        this.queryForm = {
+          custId: '',
+          targetType: '',
+          targetId: '',
+          managerId: '',
+          daysBeforeToday: '',
+          date: [],
         }
-      })
-    },
-    // 客户删除
-    handleDelete (row) {
-      this.$confirm('确认删除?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.deleteCustomer({ Id: row.id }))
+        this.fetchData()
+      },
+      // 详情
+      showDetail(row) {
+        this.$refs.followDetail.init({ ...row })
+      },
+      async showComment(id) {
+        this.followId = id
+        const [err, res] = await to(api.getComment({ followId: id + '' }))
         if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '删除成功!'
-          });
-          this.fetchData()
+        this.comments = res.data.list || []
+      },
+      formatType(val) {
+        let str = ''
+        if (val == 10) str = '电话'
+        else if (val == 20) str = '邮件'
+        else if (val == 30) str = '拜访'
+        return str
+      },
+      // 评论
+      async handleComment() {
+        let str = ''
+        if (!this.followId) str = '请选择跟进记录'
+        else if (!this.comment) str = '请输入评论'
+        if (str) return this.$message.warning(str)
+        let params = {
+          followId: this.followId + '',
+          content: this.comment,
+          remark: '',
         }
-      }).catch(() => {
-      })
-    },
-    // 联系人弹窗
-    createContact (res) {
-      this.$refs.contact.contactForm.custId = res.id
-      this.$refs.contact.contactForm.custName = res.name
-      this.$refs.contact.contactVisible = true
-    },
-    customerSave () {
-      this.fetchData()
-    },
-    handleClose (form) {
-      this.$refs[form].resetFields()
-    },
-    // 领取
-    handleReceive () {
-      if (!this.selectRows.length) return this.$message.warning('请选择客户')
-      const arr = this.selectRows.map(item => item.id)
-      this.$confirm('确认领取客户?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.receiveCustomer({ ids: arr.join() }))
+        const [err, res] = await to(api.addComment(params))
         if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '领取成功!'
-          });
+        this.$message.success(res.msg)
+        this.showComment(this.followId)
+        this.comment = ''
+        this.fetchData()
+      },
+      addFollowUp() {
+        let params = {
+          followType: '20',
+          followDate: '2022-10-29',
+          followContent: '跟进内容',
+          targetId: 1,
+          targetType: '20',
+          targetName: 'dashoo',
+          custId: 26,
+          custName: '测试客户1348',
+          contactsId: 55,
+          contactsName: 'wanglj',
+          reminders: '',
+          nextTime: this.parseTime(new Date()),
+          remark: '',
         }
-      }).catch(() => {
-      })
-    }
+        api.addFollowUp(params)
+      },
+    },
   }
-}
 </script>
 
 <style lang="scss" scoped>
-$base: '.follow';
-.follow {
-  height: calc(100vh - 228px);
-}
-</style>
+  $base: '.follow';
+  .follow {
+    height: calc(100vh - 240px);
+    display: flex;
+    .comment {
+      width: 300px;
+      display: flex;
+      flex-direction: column;
+      border-radius: 4px;
+      border: 1px solid rgb(215, 232, 244);
+      ul {
+        flex: 1;
+        overflow-y: auto;
+        padding: 10px;
+        li {
+          display: flex;
+          border-bottom: 1px solid #e3e5e7;
+          .text {
+            flex: 1;
+            padding: 0 10px;
+            p {
+              font-weight: 500;
+              margin: 0;
+              line-height: 32px;
+            }
+            p:first-child {
+              line-height: 30px;
+              font-weight: bold;
+            }
+            p:last-child {
+              font-size: 12px;
+              color: #9499a0;
+              text-align: right;
+            }
+          }
+        }
+        .user-avatar {
+          font-size: 30px;
+        }
+      }
+      .form {
+        padding: 4px;
+        text-align: right;
+        .el-textarea {
+          margin-bottom: 4px;
+        }
+      }
+    }
+    .records {
+      flex: 1;
+      margin: 0;
+      height: 100%;
+      padding: 10px 20px;
+      list-style: none;
+      overflow: auto;
+      > li {
+        display: flex;
+        + li {
+          margin-top: 10px;
+        }
+      }
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        h2,
+        h3 {
+          margin: 0;
+        }
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+      .content {
+        flex: 1;
+        list-style: none;
+        li {
+          display: flex;
+          border: 1px solid rgb(215, 232, 244);
+          background: rgb(247, 251, 254);
+          border-radius: 4px;
+          padding: 8px;
+          + li {
+            margin-top: 10px;
+          }
+        }
+        .user-avatar {
+          font-size: 40px;
+        }
+        .text {
+          flex: 1;
+          padding-left: 20px;
+          padding-right: 10px;
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 32px;
+            span {
+              color: #1d66dc;
+            }
+          }
+          .action {
+            display: flex;
+            justify-content: space-between;
+            span:first-child {
+              font-weight: bold;
+              color: #333;
+            }
+          }
+          .footer {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 209 - 224
src/views/customer/list.vue

@@ -2,275 +2,260 @@
  * @Author: wanglj 471442253@qq.com
  * @Date: 2022-12-26 16:34:37
  * @LastEditors: wanglj
- * @LastEditTime: 2022-12-28 16:01:12
+ * @LastEditTime: 2022-12-30 11:21:20
  * @Description: file content
  * @FilePath: \opms_frontend\src\views\customer\list.vue
 -->
 <template>
   <div class="list-container">
-    <el-tabs v-model="activeName"
-             @tab-click="handleClick">
-      <el-tab-pane label="全部客户"
-                   name="first"></el-tab-pane>
-      <el-tab-pane label="我的客户"
-                   name="second"></el-tab-pane>
-      <el-tab-pane label="下属的客户"
-                   name="third"></el-tab-pane>
+    <el-tabs v-model="activeName" @tab-click="handleClick">
+      <el-tab-pane label="全部客户" name="first" />
+      <el-tab-pane label="我的客户" name="second" />
+      <el-tab-pane label="下属的客户" name="third" />
     </el-tabs>
-    <el-row :gutter="10"
-            style="margin-bottom:10px">
+    <el-row :gutter="10" style="margin-bottom: 10px">
       <el-col :span="4">
-        <el-input placeholder="客户编码"
-                  v-model="queryForm.custCode"></el-input>
+        <el-input v-model="queryForm.custCode" placeholder="客户编码" />
       </el-col>
       <el-col :span="4">
-        <el-input placeholder="客户名称"
-                  v-model="queryForm.custName"></el-input>
+        <el-input v-model="queryForm.custName" placeholder="客户名称" />
       </el-col>
       <el-col :span="4">
-        <el-input placeholder="客户行业"
-                  v-model="queryForm.indusTry"></el-input>
+        <el-input v-model="queryForm.indusTry" placeholder="客户行业" />
       </el-col>
       <el-col :span="12">
-        <el-button icon="el-icon-plus"
-                   type="primary"
-                   @click="fetchData">查询</el-button>
-        <el-button icon="el-icon-refresh-right"
-                   @click="reset">重置</el-button>
+        <el-button icon="el-icon-plus" type="primary" @click="fetchData">
+          查询
+        </el-button>
+        <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
       </el-col>
     </el-row>
     <vab-query-form>
       <vab-query-form-left-panel :span="12">
-        <el-button size="mini"
-                   type="primary"
-                   icon="el-icon-plus"
-                   @click="$refs.edit.init()">新建</el-button>
-        <el-button size="mini"
-                   type="primary"
-                   icon="el-icon-plus"
-                   @click="handleShift">转移客户</el-button>
-        <el-button size="mini"
-                   type="primary"
-                   icon="el-icon-plus"
-                   @click="handleToOpen">移入公海</el-button>
-        <el-button size="mini"
-                   type="primary"
-                   icon="el-icon-plus"
-                   @click="handleMerge">合并客户</el-button>
+        <el-button
+          icon="el-icon-plus"
+          size="mini"
+          type="primary"
+          @click="$refs.edit.init()">
+          新建
+        </el-button>
+        <el-button
+          icon="el-icon-plus"
+          size="mini"
+          type="primary"
+          @click="handleShift">
+          转移客户
+        </el-button>
+        <el-button
+          icon="el-icon-plus"
+          size="mini"
+          type="primary"
+          @click="handleToOpen">
+          移入公海
+        </el-button>
+        <el-button
+          icon="el-icon-plus"
+          size="mini"
+          type="primary"
+          @click="handleMerge">
+          合并客户
+        </el-button>
       </vab-query-form-left-panel>
       <vab-query-form-right-panel :span="12">
-        <el-button icon="el-icon-download"></el-button>
-        <el-button icon="el-icon-setting"></el-button>
+        <el-button icon="el-icon-download" />
+        <el-button icon="el-icon-setting" />
       </vab-query-form-right-panel>
     </vab-query-form>
-    <el-table v-loading="listLoading"
-              :data="list"
-              border
-              height="calc(100vh - 394px)"
-              @selection-change="setSelectRows">
-      <el-table-column show-overflow-tooltip
-                       type="selection"
-                       align="center" />
-      <el-table-column align="center"
-                       label="客户编码"
-                       prop="custCode"></el-table-column>
-      <el-table-column align="center"
-                       label="客户名称"
-                       prop="custName"></el-table-column>
-      <el-table-column align="center"
-                       label="助记名"
-                       prop="abbrName"></el-table-column>
-      <el-table-column align="center"
-                       label="所在地区"
-                       prop="custLocation"></el-table-column>
-      <el-table-column align="center"
-                       label="客户行业"
-                       prop="custIndustry"></el-table-column>
-      <el-table-column align="center"
-                       label="客户级别"
-                       prop="custLevel"></el-table-column>
-      <el-table-column align="center"
-                       label="客户状态"
-                       prop="custStatus">
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      height="calc(100vh - 394px)"
+      @selection-change="setSelectRows">
+      <el-table-column align="center" show-overflow-tooltip type="selection" />
+      <el-table-column align="center" label="客户编码" prop="custCode" />
+      <el-table-column align="center" label="客户名称" prop="custName" />
+      <el-table-column align="center" label="助记名" prop="abbrName" />
+      <el-table-column align="center" label="所在地区" prop="custLocation" />
+      <el-table-column align="center" label="客户行业" prop="custIndustry" />
+      <el-table-column align="center" label="客户级别" prop="custLevel" />
+      <el-table-column align="center" label="客户状态" prop="custStatus">
         <template slot-scope="scope">
-          {{scope.row.custStatus == 10 ? '正常' : '异常'}}
+          {{ scope.row.custStatus == 10 ? '正常' : '异常' }}
         </template>
       </el-table-column>
-      <el-table-column align="center"
-                       label="最后跟进时间"
-                       prop="followUpDate"></el-table-column>
-      <el-table-column align="center"
-                       label="创建人"
-                       prop="createdName"></el-table-column>
-      <el-table-column align="center"
-                       label="创建时间"
-                       prop="createdTime"></el-table-column>
-      <el-table-column align="center"
-                       label="操作">
+      <el-table-column
+        align="center"
+        label="最后跟进时间"
+        prop="followUpDate" />
+      <el-table-column align="center" label="创建人" prop="createdName" />
+      <el-table-column align="center" label="创建时间" prop="createdTime" />
+      <el-table-column align="center" label="操作">
         <template slot-scope="scope">
-          <el-button type="text"
-                     @click="handleEdit(scope.row)">编辑</el-button>
-          <el-button type="text"
-                     @click="handleDetail(scope.row)">详情</el-button>
-          <el-button type="text"
-                     @click="handleDelete(scope.row)">删除</el-button>
+          <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
+          <el-button type="text" @click="handleDetail(scope.row)">
+            详情
+          </el-button>
+          <el-button type="text" @click="handleDelete(scope.row)">
+            删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
-    <el-pagination background
-                   :current-page="queryForm.pageNum"
-                   :layout="layout"
-                   :page-size="queryForm.pageSize"
-                   :total="total"
-                   @current-change="handleCurrentChange"
-                   @size-change="handleSizeChange" />
+    <el-pagination
+      background
+      :current-page="queryForm.pageNum"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange" />
     <!-- 新增编辑客户弹窗 -->
-    <Edit ref="edit"
-          @createContact="createContact"
-          @customerSave="fetchData"></Edit>
+    <Edit ref="edit" @createContact="createContact" @customerSave="fetchData" />
     <!-- 新建联系人弹窗 -->
-    <Contact ref="contact"></Contact>
+    <Contact ref="contact" />
     <!-- 转移客户 -->
-    <Shift ref="shift"></Shift>
+    <Shift ref="shift" />
     <!-- 移入公海 -->
-    <ToOpen ref="toOpen"
-            @refresh="fetchData"></ToOpen>
+    <ToOpen ref="toOpen" @refresh="fetchData" />
     <!-- 合并客户 -->
-    <Merge ref="merge"
-           @refresh="fetchData"></Merge>
+    <Merge ref="merge" @refresh="fetchData" />
   </div>
 </template>
 
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-import Edit from './components/Edit'
-import Contact from './components/Contact'
-import Shift from './components/Shift'
-import ToOpen from './components/ToOpen'
-import Merge from './components/Merge'
-export default {
-  data () {
-    return {
-      activeName: 'first',
-      layout: 'total, sizes, prev, pager, next, jumper',
-      queryForm: {
-        custCode: '',
-        custName: '',
-        indusTry: '',
-        pageNum: 1,
-        pageSize: 10
-      },
-      total: 0,
-      listLoading: false,
-      list: [],
-      selectRows: []
-    }
-  },
-  components: {
-    Edit,
-    Contact,
-    Shift,
-    ToOpen,
-    Merge
-  },
-  mounted () {
-    this.fetchData()
-  },
-  methods: {
-    handleClick (tab) {
-      console.log(tab, 'tab');
-      this.fetchData()
+  import to from 'await-to-js'
+  import api from '@/api/customer'
+  import Edit from './components/Edit'
+  import Contact from './components/Contact'
+  import Shift from './components/Shift'
+  import ToOpen from './components/ToOpen'
+  import Merge from './components/Merge'
+  export default {
+    components: {
+      Edit,
+      Contact,
+      Shift,
+      ToOpen,
+      Merge,
     },
-    async fetchData () {
-      this.listLoading = true
-      const params = { ...this.queryForm }
-      const [err, res] = await to(api.getList(params))
-      if (err) return this.listLoading = false
-      this.list = res.data.list || []
-      this.total = res.data.total
-      this.listLoading = false
-    },
-    reset () {
-      this.queryForm = {
-        pageNum: 1,
-        pageSize: 10,
-        custCode: '',   // 客户编码
-        custName: '',    //客户名称
-        indusTry: ''    // 客户行业  ()
+    data() {
+      return {
+        activeName: 'first',
+        layout: 'total, sizes, prev, pager, next, jumper',
+        queryForm: {
+          custCode: '',
+          custName: '',
+          indusTry: '',
+          pageNum: 1,
+          pageSize: 10,
+        },
+        total: 0,
+        listLoading: false,
+        list: [],
+        selectRows: [],
       }
-      this.fetchData()
     },
-    // 客户编辑
-    handleEdit (row) {
-      this.$refs.edit.init([row.id])
-    },
-    // 联系人弹窗
-    async createContact () {
-      this.$refs.contact.contactForm.custId = res.id
-      this.$refs.contact.contactForm.custName = res.name
-      this.$refs.contact.contactVisible = true
+    mounted() {
+      this.fetchData()
     },
-    // 客户详情
-    handleDetail (row) {
-      this.$router.push({
-        path: '/customer/detail',
-        query: {
-          id: row.id,
-          private: 1
+    methods: {
+      handleClick(tab) {
+        console.log(tab, 'tab')
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(api.getList(params))
+        if (err) return (this.listLoading = false)
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.listLoading = false
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          custCode: '', // 客户编码
+          custName: '', //客户名称
+          indusTry: '', // 客户行业  ()
         }
-      })
-    },
-    // 客户删除
-    handleDelete (row) {
-      this.$confirm('确认删除?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.deleteCustomer({ Id: row.id }))
+        this.fetchData()
+      },
+      // 客户编辑
+      handleEdit(row) {
+        this.$refs.edit.init([row.id])
+      },
+      // 联系人弹窗
+      async createContact(res) {
+        this.$refs.contact.contactForm.custId = res.id
+        this.$refs.contact.contactForm.custName = res.name
+        this.$refs.contact.contactVisible = true
+      },
+      // 客户详情
+      handleDetail(row) {
+        this.$router.push({
+          path: '/customer/detail',
+          query: {
+            id: row.id,
+            privateCus: 1,
+          },
+        })
+      },
+      // 客户删除
+      handleDelete(row) {
+        this.$confirm('确认删除?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(api.deleteCustomer({ Id: row.id }))
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+            }
+          })
+          .catch(() => {})
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      // 转移客户
+      handleShift() {
+        this.$refs.shift.visible = true
+      },
+      // 移入公海
+      handleToOpen() {
+        if (!this.selectRows.length)
+          return this.$message.warning('请选择要移入公海的客户')
+        this.$refs.toOpen.form.ids = this.selectRows.map((item) => item.id)
+        this.$refs.toOpen.visible = true
+      },
+      async handleMerge() {
+        if (this.selectRows.length < 2)
+          return this.$message.warning('请选择两个以上客户进行合并')
+        const ids = this.selectRows.map((item) => item.id)
+        const [err, res] = await to(api.getDetail({ ids }))
         if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '删除成功!'
-          });
-        }
-      }).catch(() => {
-      })
-    },
-    handleSizeChange (val) {
-      this.queryForm.pageSize = val
-      this.fetchData()
-    },
-    handleCurrentChange (val) {
-      this.queryForm.pageNum = val
-      this.fetchData()
-    },
-    setSelectRows (val) {
-      this.selectRows = val
-    },
-    // 转移客户
-    handleShift () {
-      this.$refs.shift.visible = true
-    },
-    // 移入公海
-    handleToOpen () {
-      if (!this.selectRows.length) return this.$message.warning('请选择要移入公海的客户')
-      this.$refs.toOpen.form.ids = this.selectRows.map(item => item.id)
-      this.$refs.toOpen.visible = true
+        this.$refs.merge.init(res, ids)
+      },
     },
-    async handleMerge () {
-      if (this.selectRows.length < 2) return this.$message.warning('请选择两个以上客户进行合并')
-      const ids = this.selectRows.map(item => item.id)
-      const [err, res] = await to(api.getDetail({ ids }))
-      if (err) return
-      this.$refs.merge.init(res, ids)
-    }
   }
-}
 </script>
 
 <style lang="scss" scoped>
-$base: '.list';
-</style>
+  $base: '.list';
+</style>

+ 199 - 215
src/views/customer/openSea.vue

@@ -8,266 +8,250 @@
 -->
 <template>
   <div class="open-sea-container">
-    <el-row :gutter="10"
-            style="margin-bottom:10px">
+    <el-row :gutter="10" style="margin-bottom: 10px">
       <el-col :span="4">
-        <el-input placeholder="客户编码"
-                  v-model="queryForm.custCode"></el-input>
+        <el-input v-model="queryForm.custCode" placeholder="客户编码" />
       </el-col>
       <el-col :span="4">
-        <el-input placeholder="客户名称"
-                  v-model="queryForm.custName"></el-input>
+        <el-input v-model="queryForm.custName" placeholder="客户名称" />
       </el-col>
       <el-col :span="4">
-        <el-input placeholder="客户行业"
-                  v-model="queryForm.custIndustry"></el-input>
+        <el-input v-model="queryForm.custIndustry" placeholder="客户行业" />
       </el-col>
       <el-col :span="4">
-        <el-input placeholder="客户级别"
-                  v-model="queryForm.custLevel"></el-input>
+        <el-input v-model="queryForm.custLevel" placeholder="客户级别" />
       </el-col>
       <el-col :span="8">
-        <el-button icon="el-icon-plus"
-                   type="primary"
-                   @click="fetchData">查询</el-button>
-        <el-button icon="el-icon-refresh-right"
-                   @click="reset">重置</el-button>
+        <el-button icon="el-icon-plus" type="primary" @click="fetchData">
+          查询
+        </el-button>
+        <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
       </el-col>
     </el-row>
     <vab-query-form>
       <vab-query-form-left-panel :span="12">
-        <el-button icon="el-icon-plus"
-                   type="primary"
-                   @click="$refs.edit.init()">
+        <el-button
+          icon="el-icon-plus"
+          type="primary"
+          @click="$refs.edit.init()">
           新建
         </el-button>
-        <el-button icon="el-icon-plus"
-                   type="primary"
-                   @click="$refs.allocate.visible = true">
+        <el-button
+          icon="el-icon-plus"
+          type="primary"
+          @click="$refs.allocate.visible = true">
           分配
         </el-button>
-        <el-button icon="el-icon-plus"
-                   type="primary"
-                   @click="handleReceive">
+        <el-button icon="el-icon-plus" type="primary" @click="handleReceive">
           领取
         </el-button>
       </vab-query-form-left-panel>
       <vab-query-form-right-panel :span="12">
-        <el-button icon="el-icon-download"></el-button>
-        <el-button icon="el-icon-setting"></el-button>
+        <el-button icon="el-icon-download" />
+        <el-button icon="el-icon-setting" />
       </vab-query-form-right-panel>
     </vab-query-form>
-    <el-table v-loading="listLoading"
-              :data="list"
-              border
-              height="calc(100vh - 340px)"
-              @selection-change="setSelectRows">
-      <el-table-column show-overflow-tooltip
-                       type="selection"
-                       align="center" />
-      <el-table-column align="center"
-                       label="客户编码"
-                       prop="custCode"></el-table-column>
-      <el-table-column align="center"
-                       label="客户名称"
-                       prop="custName"></el-table-column>
-      <el-table-column align="center"
-                       label="助记名"
-                       prop="abbrName"></el-table-column>
-      <el-table-column align="center"
-                       label="所在地区"
-                       prop="custLocation"></el-table-column>
-      <el-table-column align="center"
-                       label="客户行业"
-                       prop="custIndustry"></el-table-column>
-      <el-table-column align="center"
-                       label="客户级别"
-                       prop="custLevel"></el-table-column>
-      <el-table-column align="center"
-                       label="客户状态"
-                       prop="custStatus">
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      height="calc(100vh - 340px)"
+      @selection-change="setSelectRows">
+      <el-table-column align="center" show-overflow-tooltip type="selection" />
+      <el-table-column align="center" label="客户编码" prop="custCode" />
+      <el-table-column align="center" label="客户名称" prop="custName" />
+      <el-table-column align="center" label="助记名" prop="abbrName" />
+      <el-table-column align="center" label="所在地区" prop="custLocation" />
+      <el-table-column align="center" label="客户行业" prop="custIndustry" />
+      <el-table-column align="center" label="客户级别" prop="custLevel" />
+      <el-table-column align="center" label="客户状态" prop="custStatus">
         <template slot-scope="scope">
-          {{scope.row.custStatus == 10 ? '正常' : '异常'}}
+          {{ scope.row.custStatus == 10 ? '正常' : '异常' }}
         </template>
       </el-table-column>
-      <el-table-column align="center"
-                       label="最后跟进时间"
-                       prop="followUpDate"></el-table-column>
-      <el-table-column align="center"
-                       label="创建人"
-                       prop="createdName"></el-table-column>
-      <el-table-column align="center"
-                       label="创建时间"
-                       prop="createdTime"></el-table-column>
-      <el-table-column align="center"
-                       label="操作">
+      <el-table-column
+        align="center"
+        label="最后跟进时间"
+        prop="followUpDate" />
+      <el-table-column align="center" label="创建人" prop="createdName" />
+      <el-table-column align="center" label="创建时间" prop="createdTime" />
+      <el-table-column align="center" label="操作">
         <template slot-scope="scope">
-          <el-button type="text"
-                     @click="handleEdit(scope.row)">编辑</el-button>
-          <el-button type="text"
-                     @click="handleDetail(scope.row)">详情</el-button>
-          <el-button type="text"
-                     @click="handleDelete(scope.row)">删除</el-button>
+          <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
+          <el-button type="text" @click="handleDetail(scope.row)">
+            详情
+          </el-button>
+          <el-button type="text" @click="handleDelete(scope.row)">
+            删除
+          </el-button>
         </template>
       </el-table-column>
     </el-table>
-    <el-pagination background
-                   :current-page="queryForm.pageNum"
-                   :layout="layout"
-                   :page-size="queryForm.pageSize"
-                   :total="total"
-                   @current-change="handleCurrentChange"
-                   @size-change="handleSizeChange" />
+    <el-pagination
+      background
+      :current-page="queryForm.pageNum"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange" />
     <!-- 新增编辑客户弹窗 -->
-    <Edit ref="edit"
-          @createContact="createContact"
-          @customerSave="customerSave"></Edit>
+    <Edit
+      ref="edit"
+      @createContact="createContact"
+      @customerSave="customerSave" />
     <!-- 新建联系人弹窗 -->
-    <Contact ref="contact"></Contact>
+    <Contact ref="contact" />
     <!-- 分配客户 -->
-    <Allocate ref="allocate"></Allocate>
+    <Allocate ref="allocate" />
   </div>
 </template>
 
 <script>
-import to from 'await-to-js'
-import api from '@/api/customer'
-import Contact from './components/Contact'
-import Edit from './components/Edit'
-import Allocate from './components/Allocate'
-export default {
-  name: 'OpenSea',
-  data () {
-    return {
-      listLoading: false,
-      layout: 'total, sizes, prev, pager, next, jumper',
-      list: [],
-      total: 0,
-      queryForm: {
-        pageNum: 1,
-        pageSize: 10,
-        custCode: '',   // 客户编码
-        custName: '',    //客户名称
-        custIndustry: '',    // 客户行业  ()
-        custLevel: '',     //客户级别
-      },
-      selectRows: []
-    }
-  },
-  components: {
-    Contact,
-    Edit,
-    Allocate
-  },
-  mounted () {
-    this.fetchData()
-    this.getOptions()
-  },
-  methods: {
-    getOptions () {
-      Promise.all([api.getProvinceInfo()]).then(([province]) => {
-        this.provinceOptions = province.data.list || []
-      }).catch(err => console.log(err))
+  import to from 'await-to-js'
+  import api from '@/api/customer'
+  import Contact from './components/Contact'
+  import Edit from './components/Edit'
+  import Allocate from './components/Allocate'
+  export default {
+    name: 'OpenSea',
+    components: {
+      Contact,
+      Edit,
+      Allocate,
     },
-    async fetchData () {
-      this.listLoading = true
-      const params = { ...this.queryForm }
-      const [err, res] = await to(api.getPublicList(params))
-      if (err) return this.listLoading = false
-      this.list = res.data.list || []
-      this.total = res.data.total
-      this.listLoading = false
-    },
-    reset () {
-      this.queryForm = {
-        pageNum: 1,
-        pageSize: 10,
-        custCode: '',   // 客户编码
-        custName: '',    //客户名称
-        custIndustry: '',    // 客户行业  ()
-        custLevel: '',     //客户级别
+    data() {
+      return {
+        listLoading: false,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        list: [],
+        total: 0,
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          custCode: '', // 客户编码
+          custName: '', //客户名称
+          custIndustry: '', // 客户行业  ()
+          custLevel: '', //客户级别
+        },
+        selectRows: [],
       }
-      this.fetchData()
     },
-    handleSizeChange (val) {
-      this.queryForm.pageSize = val
+    mounted() {
       this.fetchData()
+      this.getOptions()
     },
-    handleCurrentChange (val) {
-      this.queryForm.pageNum = val
-      this.fetchData()
-    },
-    setSelectRows (val) {
-      this.selectRows = val
-    },
-    // 客户编辑
-    async handleEdit (row) {
-      this.$refs.edit.init([row.id])
-    },
-    // 客户详情
-    handleDetail (row) {
-      this.$router.push({
-        path: '/customer/detail',
-        query: {
-          id: row.id
-        }
-      })
-    },
-    // 客户删除
-    handleDelete (row) {
-      this.$confirm('确认删除?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.deleteCustomer({ Id: row.id }))
-        if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '删除成功!'
-          });
-          this.fetchData()
+    methods: {
+      getOptions() {
+        Promise.all([api.getProvinceInfo()])
+          .then(([province]) => {
+            this.provinceOptions = province.data.list || []
+          })
+          .catch((err) => console.log(err))
+      },
+      async fetchData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(api.getPublicList(params))
+        if (err) return (this.listLoading = false)
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.listLoading = false
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          custCode: '', // 客户编码
+          custName: '', //客户名称
+          custIndustry: '', // 客户行业  ()
+          custLevel: '', //客户级别
         }
-      }).catch(() => {
-      })
-    },
-    // 联系人弹窗
-    createContact (res) {
-      this.$refs.contact.contactForm.custId = res.id
-      this.$refs.contact.contactForm.custName = res.name
-      this.$refs.contact.contactVisible = true
-    },
-    customerSave () {
-      this.fetchData()
-    },
-    handleClose (form) {
-      this.$refs[form].resetFields()
+        this.fetchData()
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+      // 客户编辑
+      async handleEdit(row) {
+        this.$refs.edit.init([row.id])
+      },
+      // 客户详情
+      handleDetail(row) {
+        this.$router.push({
+          path: '/customer/detail',
+          query: {
+            id: row.id,
+          },
+        })
+      },
+      // 客户删除
+      handleDelete(row) {
+        this.$confirm('确认删除?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(api.deleteCustomer({ Id: row.id }))
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+              this.fetchData()
+            }
+          })
+          .catch(() => {})
+      },
+      // 联系人弹窗
+      createContact(res) {
+        this.$refs.contact.contactForm.custId = res.id
+        this.$refs.contact.contactForm.custName = res.name
+        this.$refs.contact.contactVisible = true
+      },
+      customerSave() {
+        this.fetchData()
+      },
+      handleClose(form) {
+        this.$refs[form].resetFields()
+      },
+      // 领取
+      handleReceive() {
+        if (!this.selectRows.length) return this.$message.warning('请选择客户')
+        const arr = this.selectRows.map((item) => item.id)
+        this.$confirm('确认领取客户?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(
+              api.receiveCustomer({ ids: arr.join() })
+            )
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '领取成功!',
+              })
+            }
+          })
+          .catch(() => {})
+      },
     },
-    // 领取
-    handleReceive () {
-      if (!this.selectRows.length) return this.$message.warning('请选择客户')
-      const arr = this.selectRows.map(item => item.id)
-      this.$confirm('确认领取客户?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning'
-      }).then(async () => {
-        const [err, res] = await to(api.receiveCustomer({ ids: arr.join() }))
-        if (err) return
-        if (res.code == 200) {
-          this.$message({
-            type: 'success',
-            message: '领取成功!'
-          });
-        }
-      }).catch(() => {
-      })
-    }
   }
-}
 </script>
 
 <style lang="scss" scoped>
-$base: '.open-sea';
-</style>
+  $base: '.open-sea';
+</style>

+ 2 - 2
src/views/opportunity/all.vue

@@ -44,9 +44,9 @@
       <el-table-column align="center" label="最后跟进" prop="" />
       <el-table-column align="center" label="下次联系时间" prop="" />
       <el-table-column align="center" label="操作">
-        <template slot-scope="scope">
+        <!-- <template slot-scope="scope">
           <el-button type="text" />
-        </template>
+        </template> -->
       </el-table-column>
     </el-table>
     <el-pagination