Przeglądaj źródła

feature: 400咨询记录管理

lai 2 lat temu
rodzic
commit
4786471ca6

+ 19 - 0
src/api/consult/index.js

@@ -0,0 +1,19 @@
+import micro_request from '@/utils/micro_request'
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  list(query) {
+    return micro_request.postRequest(basePath, 'ProductConsultRecord', 'List', query)
+  },
+  delete(query) {
+    return micro_request.postRequest(basePath, 'ProductConsultRecord', 'Delete', query)
+  },
+  add(query) {
+    return micro_request.postRequest(basePath, 'ProductConsultRecord', 'Add', query)
+  },
+  get(query) {
+    return micro_request.postRequest(basePath, 'ProductConsultRecord', 'Get', query)
+  },
+  update(query) {
+    return micro_request.postRequest(basePath, 'ProductConsultRecord', 'Update', query)
+  },
+}

+ 233 - 0
src/views/consult/components/Edit.vue

@@ -0,0 +1,233 @@
+<template>
+  <div>
+    <el-dialog append-to-body :title="title" :visible.sync="visible" @close="close">
+      <el-form ref="form" :model="form" :rules="rules">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="序号" prop="code">
+              <el-input v-model="form.code" disabled :placeholder="form.code ? form.code : '自动生成无需填写'" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="日期时间" prop="consultTime">
+              <el-date-picker
+                v-model="form.consultTime"
+                format="yyyy-MM-dd HH:mm"
+                placeholder="请选择发布日期"
+                style="width: 100%"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="所在地区" required>
+              <el-row :gutter="4" style="width: 100%; padding-top: 32px">
+                <el-col :span="8">
+                  <el-select
+                    v-model="form.province"
+                    :disabled="areaEditDisable"
+                    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
+                    v-model="form.city"
+                    :disabled="areaEditDisable"
+                    placeholder="市"
+                    value-key="id"
+                    @change="cityChange">
+                    <el-option
+                      v-for="item in currentProvince ? currentProvince.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="unit">
+              <el-input v-model="form.unit" placeholder="请输入单位名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系人" prop="name">
+              <el-input v-model="form.name" placeholder="请输入联系人" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系方式" prop="contact">
+              <el-input v-model="form.contact" placeholder="请输入联系方式" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="咨询产品" prop="product">
+              <el-input v-model="form.product" placeholder="请输入咨询产品" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="对接人" prop="inchargeName">
+              <el-input v-model="form.inchargeName" suffix-icon="el-icon-search" @focus="handleSelectSale" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="400电话内容" prop="content">
+          <el-input v-model="form.content" maxlength="500" resize="none" :rows="5" show-word-limit type="textarea" />
+        </el-form-item>
+      </el-form>
+      <span slot="footer">
+        <el-button v-show="form.id" type="primary" @click="consultEdit">保存</el-button>
+        <el-button v-show="!form.id" type="primary" @click="consultSave">保存</el-button>
+        <el-button @click="visible = false">取消</el-button>
+      </span>
+    </el-dialog>
+    <!-- 选择销售工程师弹窗 -->
+    <select-user ref="selectSales" :query-params="{ roles: ['SalesEngineer'] }" @save="selectSales" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import customerApi from '@/api/customer'
+  import consultApi from '@/api/consult'
+  import SelectUser from '@/components/select/SelectUser'
+
+  export default {
+    components: { SelectUser },
+    data() {
+      return {
+        title: '',
+        visible: false,
+        areaEditDisable: false,
+        provinceOptions: [],
+        currentProvince: [],
+        form: {
+          id: 0,
+          code: '', // 序号
+          consultTime: null, // 日期时间
+          provinceId: 0, // 所在省ID
+          province: '', // 所在省
+          cityId: 0, // 所在市ID
+          city: '', // 所在市
+          unit: '', // 单位名称
+          name: '', // 联系人
+          contact: '', // 联系方式
+          product: '', // 咨询产品
+          inchargeId: '', // 对接人ID
+          inchargeName: '', // 对接人(销售工程师)
+          content: '', // 内容
+        },
+        rules: {
+          code: [{ required: false }],
+          consultTime: [{ required: true, trigger: ['blur', 'change'], message: '请输入日期时间' }],
+          provinceId: [{ required: true, trigger: ['blur', 'change'], message: '请输入所在省ID' }],
+          province: [{ required: true, trigger: ['blur', 'change'], message: '请输入所在省' }],
+          cityId: [{ required: true, trigger: ['blur', 'change'], message: '请输入所在市ID' }],
+          city: [{ required: true, trigger: ['blur', 'change'], message: '请输入所在市' }],
+          unit: [{ required: true, trigger: ['blur', 'change'], message: '请输入单位名称' }],
+          name: [{ required: true, trigger: ['blur', 'change'], message: '请输入联系人' }],
+          contact: [{ required: true, trigger: ['blur', 'change'], message: '请输入联系方式' }],
+          product: [{ required: true, trigger: ['blur', 'change'], message: '请输入咨询产品' }],
+          inchargeId: [{ required: true, trigger: ['blur', 'change'], message: '请输入对接人ID' }],
+          inchargeName: [{ required: true, trigger: ['blur', 'change'], message: '请输入对接人' }],
+          content: [{ required: true, trigger: ['blur', 'change'], message: '请输入内容' }],
+        },
+      }
+    },
+    mounted() {
+      Promise.all([customerApi.getProvinceDetail()])
+        .then(([province]) => {
+          this.provinceOptions = province.data.list || []
+        })
+        .catch((err) => console.log(err))
+    },
+    methods: {
+      async init(id) {
+        this.visible = true
+        if (!id) {
+          this.title = '新建咨询记录'
+          return
+        }
+        this.title = '编辑咨询记录'
+        const [err, res] = await to(consultApi.get({ id: id }))
+        if (err) return
+        this.form = res.data
+      },
+      // 保存
+      async consultSave() {
+        this.$refs.form.validate(async (valid) => {
+          if (valid) {
+            let params = { ...this.form }
+            const [err, res] = await to(consultApi.add(params))
+            if (err) return
+            this.$message.success(res.msg)
+            this.visible = false
+            this.$emit('consultSave')
+          }
+        })
+      },
+      async consultEdit() {
+        this.$refs.form.validate(async (valid) => {
+          if (valid) {
+            let params = { ...this.form }
+            const [err, res] = await to(consultApi.update(params))
+            if (err) return
+            this.$message.success(res.msg)
+            this.visible = false
+            this.$emit('consultSave')
+          }
+        })
+      },
+      provinceChange(val) {
+        this.currentProvince = val
+        this.form.provinceId = val.id
+        this.form.province = val.distName
+        this.form.cityId = 0
+        this.form.city = ''
+        this.$forceUpdate()
+      },
+      cityChange(val) {
+        this.form.cityId = val.id
+        this.form.city = val.distName
+        this.$forceUpdate()
+      },
+      selectSales(val) {
+        if (val && val.length > 0) {
+          this.form.inchargeId = val[0].id
+          this.form.inchargeName = val.map((item) => item.nickName).join()
+        }
+      },
+      handleSelectSale() {
+        this.$refs.selectSales.open()
+      },
+      close() {
+        this.form = {
+          id: 0,
+          code: '', // 序号
+          consultTime: null, // 日期时间
+          provinceId: 0, // 所在省ID
+          province: '', // 所在省
+          cityId: 0, // 所在市ID
+          city: '', // 所在市
+          unit: '', // 单位名称
+          name: '', // 联系人
+          contact: '', // 联系方式
+          product: '', // 咨询产品
+          inchargeId: '', // 对接人ID
+          inchargeName: '', // 对接人(销售工程师)
+          content: '', // 内容
+        }
+        this.$refs['form'].resetFields()
+      },
+    },
+  }
+</script>
+
+<style></style>

+ 258 - 0
src/views/consult/components/FollowUp.vue

@@ -0,0 +1,258 @@
+<template>
+  <div>
+    <el-dialog append-to-body :title="title" :visible.sync="visible" @close="close">
+      <el-form ref="form" :model="form" :rules="rules">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="序号" prop="code">
+              <el-input v-model="form.code" disabled :placeholder="form.code ? form.code : '自动生成无需填写'" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="日期时间" prop="consultTime">
+              <el-date-picker
+                v-model="form.consultTime"
+                disabled
+                format="yyyy-MM-dd HH:mm"
+                placeholder="请选择发布日期"
+                style="width: 100%"
+                type="datetime"
+                value-format="yyyy-MM-dd HH:mm" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="所在地区" required>
+              <el-row :gutter="4" style="width: 100%; padding-top: 32px">
+                <el-col :span="8">
+                  <el-select
+                    v-model="form.province"
+                    :disabled="areaEditDisable"
+                    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
+                    v-model="form.city"
+                    :disabled="areaEditDisable"
+                    placeholder="市"
+                    value-key="id"
+                    @change="cityChange">
+                    <el-option
+                      v-for="item in currentProvince ? currentProvince.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="unit">
+              <el-input v-model="form.unit" disabled placeholder="请输入单位名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系人" prop="name">
+              <el-input v-model="form.name" disabled placeholder="请输入联系人" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="联系方式" prop="contact">
+              <el-input v-model="form.contact" disabled placeholder="请输入联系方式" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="咨询产品" prop="product">
+              <el-input v-model="form.product" disabled placeholder="请输入咨询产品" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="对接人" prop="inchargeName">
+              <el-input v-model="form.inchargeName" disabled suffix-icon="el-icon-search" @focus="handleSelectSale" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="400电话内容" prop="content">
+          <el-input
+            v-model="form.content"
+            disabled
+            maxlength="500"
+            resize="none"
+            :rows="4"
+            show-word-limit
+            type="textarea" />
+        </el-form-item>
+        <el-form-item label="进展描述" prop="progress">
+          <el-input v-model="form.progress" maxlength="500" resize="none" :rows="4" show-word-limit type="textarea" />
+        </el-form-item>
+        <el-form-item label="下一步计划" prop="nextPlan">
+          <el-input v-model="form.nextPlan" maxlength="500" resize="none" :rows="4" show-word-limit type="textarea" />
+        </el-form-item>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="经销商/代理商" prop="distributorName">
+              <el-input v-model="form.distributorName" suffix-icon="el-icon-search" @focus="handleSelectDistributor" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否创建项目" prop="isBig">
+              <el-select v-model="createProj" placeholder="请选择" style="width: 100%">
+                <el-option label="是" :value="true" />
+                <el-option label="否" :value="false" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <span slot="footer">
+        <el-button type="primary" @click="consultEdit">保存</el-button>
+        <el-button @click="visible = false">取消</el-button>
+      </span>
+    </el-dialog>
+    <!-- 选择销售工程师弹窗 -->
+    <select-user ref="selectSales" :query-params="{ roles: ['SalesEngineer'] }" @save="selectSales" />
+    <!-- 选择经销商弹窗 -->
+    <select-distributor ref="selectDistributor" @save="selectDistributor" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import customerApi from '@/api/customer'
+  import consultApi from '@/api/consult'
+  import SelectUser from '@/components/select/SelectUser'
+  import SelectDistributor from '@/components/select/SelectDistributor'
+
+  export default {
+    components: { SelectUser, SelectDistributor },
+    data() {
+      return {
+        title: '',
+        visible: false,
+        createProj: false,
+        areaEditDisable: true,
+        provinceOptions: [],
+        currentProvince: [],
+        form: {
+          id: 0,
+          code: '', // 序号
+          consultTime: null, // 日期时间
+          provinceId: 0, // 所在省ID
+          province: '', // 所在省
+          cityId: 0, // 所在市ID
+          city: '', // 所在市
+          unit: '', // 单位名称
+          name: '', // 联系人
+          contact: '', // 联系方式
+          product: '', // 咨询产品
+          inchargeId: '', // 对接人ID
+          inchargeName: '', // 对接人(销售工程师)
+          content: '', // 内容
+          progress: '', // 进展描述
+          nextPlan: '', // 下一步计划
+          distributorId: 0, // 经销商ID
+          distributorName: '', // 经销商
+        },
+        rules: {},
+      }
+    },
+    mounted() {
+      Promise.all([customerApi.getProvinceDetail()])
+        .then(([province]) => {
+          this.provinceOptions = province.data.list || []
+        })
+        .catch((err) => console.log(err))
+    },
+    methods: {
+      async init(id) {
+        this.createProj = false
+        this.visible = true
+        if (!id) {
+          this.title = '新建咨询记录'
+          return
+        }
+        this.title = '更新咨询记录'
+        const [err, res] = await to(consultApi.get({ id: id }))
+        if (err) return
+        this.form = res.data
+      },
+      async consultEdit() {
+        this.$refs.form.validate(async (valid) => {
+          if (valid) {
+            let params = { ...this.form }
+            const [err, res] = await to(consultApi.update(params))
+            if (err) return
+            this.$message.success(res.msg)
+            this.visible = false
+            this.$emit('consultSave', this.createProj)
+          }
+        })
+      },
+      provinceChange(val) {
+        this.currentProvince = val
+        this.form.provinceId = val.id
+        this.form.province = val.distName
+        this.form.cityId = 0
+        this.form.city = ''
+        this.$forceUpdate()
+      },
+      cityChange(val) {
+        this.form.cityId = val.id
+        this.form.city = val.distName
+        this.$forceUpdate()
+      },
+      selectSales(val) {
+        if (val && val.length > 0) {
+          this.form.inchargeId = val[0].id
+          this.form.inchargeName = val.map((item) => item.nickName).join()
+        }
+      },
+      selectDistributor(val) {
+        if (val && val.length > 0) {
+          this.form.distributorId = val[0].id
+          this.form.distributorName = val.map((item) => item.distName).join()
+        }
+      },
+      handleSelectSale() {
+        this.$refs.selectSales.open()
+      },
+      handleSelectDistributor() {
+        this.$refs.selectDistributor.open()
+      },
+      close() {
+        this.form = {
+          id: 0,
+          code: '', // 序号
+          consultTime: null, // 日期时间
+          provinceId: 0, // 所在省ID
+          province: '', // 所在省
+          cityId: 0, // 所在市ID
+          city: '', // 所在市
+          unit: '', // 单位名称
+          name: '', // 联系人
+          contact: '', // 联系方式
+          product: '', // 咨询产品
+          inchargeId: '', // 对接人ID
+          inchargeName: '', // 对接人(销售工程师)
+          content: '', // 内容
+          progress: '', // 进展描述
+          nextPlan: '', // 下一步计划
+          distributorId: 0, // 经销商ID
+          distributorName: '', // 经销商
+        }
+        this.$refs['form'].resetFields()
+      },
+    },
+  }
+</script>
+
+<style></style>

+ 446 - 0
src/views/consult/detail.vue

@@ -0,0 +1,446 @@
+<template>
+  <div class="details">
+    <div class="side-layout">
+      <div class="info">
+        <div class="title">
+          <p>咨询记录详情</p>
+          <h3>
+            {{ details.unit }}
+            <span></span>
+          </h3>
+        </div>
+        <header>
+          <el-descriptions :colon="false" :column="7" direction="vertical">
+            <el-descriptions-item content-class-name="my-content" label="联系人" label-class-name="my-label">
+              <span style="color: #1890ff; cursor: pointer">
+                {{ details.name }}
+              </span>
+            </el-descriptions-item>
+          </el-descriptions>
+          <el-descriptions :colon="false" :column="7" direction="vertical">
+            <el-descriptions-item content-class-name="my-content" label="联系方式" label-class-name="my-label">
+              <span style="color: #1890ff; cursor: pointer">
+                {{ details.contact }}
+              </span>
+            </el-descriptions-item>
+          </el-descriptions>
+          <el-descriptions :colon="false" :column="7" direction="vertical">
+            <el-descriptions-item content-class-name="my-content" label="对接人" label-class-name="my-label">
+              <span style="color: #1890ff; cursor: pointer">
+                {{ details.inchargeName }}
+              </span>
+            </el-descriptions-item>
+          </el-descriptions>
+        </header>
+        <el-tabs v-model="activeName" @tab-click="handleClick">
+          <el-tab-pane label="详细信息" name="details">
+            <el-descriptions
+              border
+              :column="2"
+              :content-style="{ width: '25%', 'word-break': 'break-all' }"
+              :label-style="{ width: '25%' }"
+              size="small">
+              <el-descriptions-item label="序号">
+                {{ details.code }}
+              </el-descriptions-item>
+              <el-descriptions-item label="日期时间">
+                {{ details.consultTime }}
+              </el-descriptions-item>
+              <el-descriptions-item label="所在省">
+                {{ details.province }}
+              </el-descriptions-item>
+              <el-descriptions-item label="所在市">
+                {{ details.city }}
+              </el-descriptions-item>
+              <el-descriptions-item label="单位名称">
+                {{ details.unit }}
+              </el-descriptions-item>
+              <el-descriptions-item label="联系人">
+                {{ details.name }}
+              </el-descriptions-item>
+              <el-descriptions-item label="联系方式">
+                {{ details.contact }}
+              </el-descriptions-item>
+              <el-descriptions-item label="咨询产品">
+                {{ details.product }}
+              </el-descriptions-item>
+              <el-descriptions-item label="对接人">
+                {{ details.inchargeName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="经销商/代理商">
+                {{ details.distributorName }}
+              </el-descriptions-item>
+              <el-descriptions-item label="内容" :span="24">
+                {{ details.content }}
+              </el-descriptions-item>
+              <el-descriptions-item label="进展描述" :span="24">
+                {{ details.progress }}
+              </el-descriptions-item>
+              <el-descriptions-item label="下一步计划" :span="24">
+                {{ details.nextPlan }}
+              </el-descriptions-item>
+            </el-descriptions>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import { mapGetters } from 'vuex'
+  import api from '@/api/consult'
+
+  export default {
+    name: 'BusinessDetail',
+    components: {},
+    data() {
+      return {
+        id: undefined,
+        details: {},
+        activeName: 'details',
+      }
+    },
+    computed: {
+      ...mapGetters({
+        avatar: 'user/avatar',
+        username: 'user/username',
+      }),
+    },
+    created() {
+      this.id = parseInt(this.$route.query.id)
+      this.init()
+    },
+    mounted() {},
+    methods: {
+      init() {
+        Promise.all([api.get({ id: this.id })]).then(([details]) => {
+          if (details.data) this.details = details.data
+        })
+      },
+      handleClick() {},
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  ::v-deep .el-tabs__content {
+    overflow: auto;
+  }
+
+  $base: '.details';
+
+  #{$base} {
+    height: calc(100vh - 60px - 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: #1d66dc;
+      }
+
+      ::v-deep .my-content {
+        font-size: 14px;
+        font-weight: 600;
+        color: #333;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+
+    .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;
+      height: calc(100% - 60px);
+      overflow-y: 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;
+
+          & + 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;
+        }
+      }
+
+      .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 {
+          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: 190px;
+            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;
+          }
+        }
+
+        .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;
+          }
+        }
+      }
+    }
+
+    .no-follow {
+      height: 100%;
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+      color: rgba(0, 0, 0, 0.65);
+    }
+  }
+
+  .height-enter-active,
+  .height-leave-active {
+    transition: all 0.5s;
+  }
+
+  .height-enter-to,
+  .height-leave {
+    height: 190px;
+  }
+
+  .height-enter,
+.height-leave-to
+
+/* .fade-leave-active below version 2.1.8 */ {
+    height: 0;
+  }
+
+  ::v-deep .el-descriptions__table tbody {
+    td,
+    th {
+      width: 25%;
+    }
+  }
+</style>

+ 266 - 0
src/views/consult/index.vue

@@ -0,0 +1,266 @@
+<!--
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-01-05 11:28:51
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-03-22 14:53:09
+ * @FilePath: \opms_frontend\src\views\contract\index.vue
+-->
+<template>
+  <div class="contract-container">
+    <vab-query-form>
+      <vab-query-form-top-panel>
+        <el-form :inline="true" label-width="0px" :model="queryForm" @submit.native.prevent>
+          <el-form-item prop="inchargeName">
+            <el-input v-model="queryForm.inchargeName" clearable placeholder="对接人" @keyup.enter.native="queryData" />
+          </el-form-item>
+          <el-form-item prop="name">
+            <el-input v-model="queryForm.name" clearable placeholder="联系人" @keyup.enter.native="queryData" />
+          </el-form-item>
+          <el-form-item prop="unit">
+            <el-input v-model="queryForm.unit" clearable placeholder="单位" @keyup.enter.native="queryData" />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-top-panel>
+      <vab-query-form-left-panel :span="12">
+        <el-button v-permissions="['contract:manage:add']" icon="el-icon-plus" type="primary" @click="handleEdit()">
+          新建
+        </el-button>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <table-tool :columns="columns" :show-columns.sync="showColumns" table-type="contractTable" />
+      </vab-query-form-right-panel>
+    </vab-query-form>
+    <el-table ref="table" v-loading="listLoading" :data="list" :height="height">
+      <el-table-column
+        v-for="(item, index) in showColumns"
+        :key="index"
+        align="center"
+        :label="item.label"
+        :min-width="item.width"
+        :prop="item.prop"
+        show-overflow-tooltip
+        :sortable="item.sortable">
+        <template #default="{ row }">
+          <el-button v-if="item.prop === 'code'" class="link-button" type="text" @click="handleDetail(row)">
+            {{ row.code }}
+          </el-button>
+          <span v-else-if="item.label === '日期时间'">
+            {{ parseTime(row.consultTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" fixed="right" label="操作" width="140px">
+        <template slot-scope="scope">
+          <el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
+          <el-button type="text" @click="handleFollowUp(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" />
+    <Edit ref="edit" @consultSave="consultSave" />
+    <FollowUp ref="followUp" @consultSave="consultSave" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import consultApi from '@/api/consult'
+  import Edit from './components/Edit'
+  import FollowUp from './components/FollowUp'
+  import TableTool from '@/components/table/TableTool'
+
+  export default {
+    name: 'Consult',
+    components: {
+      Edit,
+      FollowUp,
+      TableTool,
+    },
+    data() {
+      return {
+        height: this.$baseTableHeight(2),
+        listLoading: false,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        list: [],
+        total: 0,
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          name: '', // 联系人
+          inchargeName: '', //对接人
+          unit: '', //单位
+        },
+        selectRows: [], //选择的表格数据
+        contractOptions: {}, //合同类型
+        productLineOptions: {}, //产品线
+        lines: [],
+        // 自定义列表
+        showColumns: [],
+        columns: [
+          {
+            label: '序号',
+            width: '160px',
+            prop: 'code',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '日期时间',
+            width: '100px',
+            prop: 'consultTime',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '所在省',
+            width: '200px',
+            prop: 'province',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '所在市',
+            width: '100px',
+            prop: 'city',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '单位名称',
+            width: '280px',
+            prop: 'unit',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '联系人',
+            prop: 'name',
+            width: '140px',
+          },
+          {
+            label: '对接人',
+            width: '120px',
+            prop: 'inchargeName',
+            sortable: false,
+            disableCheck: false,
+          },
+        ],
+      }
+    },
+    watch: {
+      showColumns: function () {
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+    },
+    activated() {
+      this.queryData()
+    },
+    mounted() {
+      this.queryData()
+      this.getOptions()
+    },
+    methods: {
+      getOptions() {
+        Promise.all([this.getDicts('contract_type'), this.getDicts('sys_product_line')])
+          .then(([contract, productLine]) => {
+            this.contractOptions = {}
+            contract.data.values.filter((i) => {
+              this.contractOptions[i.key] = i.value
+            })
+            this.productLineOptions = {}
+            productLine.data.values.filter((i) => {
+              this.productLineOptions[i.key] = i.value
+            })
+            this.lines = productLine.data.values
+          })
+          .catch((err) => console.log(err))
+      },
+      async queryData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(consultApi.list(params))
+        if (err) return (this.listLoading = false)
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.listLoading = false
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          name: '', // 联系人
+          inchargeName: '', //对接人
+          unit: '', //单位
+        }
+        this.queryData()
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.queryData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.queryData()
+      },
+      async handleEdit(row = null) {
+        row ? this.$refs.edit.init(row.id) : this.$refs.edit.init()
+      },
+      async handleFollowUp(row = null) {
+        row ? this.$refs.followUp.init(row.id) : this.$refs.followUp.init()
+      },
+      handleDetail(row) {
+        this.$router.push({
+          path: '/consult/detail',
+          query: {
+            id: row.id,
+          },
+        })
+      },
+      handleDelete(row) {
+        let ids = [row.id]
+        this.$confirm('确认删除?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(consultApi.delete({ id: ids }))
+            if (err) return
+            if (res.code == 200) {
+              this.$message({
+                type: 'success',
+                message: '删除成功!',
+              })
+              this.queryData()
+            }
+          })
+          .catch(() => {})
+      },
+      consultSave(createProj) {
+        this.queryData()
+        if (createProj) {
+          this.$router.push({
+            path: '/opportunity/business',
+          })
+        }
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.contract-container';
+</style>