Forráskód Böngészése

Merge branch 'feature_20241118_家装客资'

zhujindu 1 éve
szülő
commit
ed6095d385

+ 2 - 2
.env.test

@@ -1,9 +1,9 @@
 # 页面标题
 VUE_APP_TITLE = 门店拜访
+ENV = 'test'
 
 # 本地开发环境配置
-ENV = 'test'
-# VUE_APP_Target=http://192.168.100.190:9560/
+# VUE_APP_Target=http://192.168.100.191:9560/
 # VUE_APP_SSB_LINK=http://suishenbangtest.nipponpaint.com.cn
 # VUE_APP_XD_LINK=http://b2btest.nipponpaint.com.cn
 # VUE_APP_BASE_API = '/'

+ 73 - 0
src/api/assignAwait.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request';
+
+// 家装客资-查询待分配客资列表接口
+export function selectNotAllocationList(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectNotAllocationList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-根据客资类型查询分配人岗位列表和用户列表接口
+export function selectUserListByCustomerType(data) {
+  return request({
+    url: 'mobile/customerClueInfo/selectUserListByCustomerType',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-根据岗位列表查询用户接口
+export function selectUserListByPostName(data) {
+  return request({
+    url: 'mobile/customerClueInfo/selectUserListByPostName',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-分配客资归属接口
+export function allocationCustomer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/allocationCustomer',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-根据客资信息,查询家装客资任务接口
+export function selectCustomerClueInfoById(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectCustomerClueInfoById',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-查询下属家装销售专员列表接口
+export function selectSubUserList(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectSubUserList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-确认转下属接口
+export function allocationSubCustomer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/allocationSubCustomer',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-新增客资跟进信息答案接口
+export function insertFollowCustomerClueAnswer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/insertFollowCustomerClueAnswer',
+    method: 'post',
+    data: data,
+  });
+}

+ 17 - 0
src/api/complaintDetail.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request';
+
+// 不想写
+export function getComplaintCustomerClueInfoById(query) {
+  return request({
+    url: '/mobile/customerClueInfo/getComplaintCustomerClueInfoById',
+    method: 'get',
+    params: query,
+  });
+}
+export function insertCustomerClueAnswerKs(data) {
+  return request({
+    url: '/mobile/customerClueInfo/insertCustomerClueAnswerKs',
+    method: 'post',
+    data,
+  });
+}

+ 10 - 0
src/api/week.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request';
+
+// 家装客资-查询当前用户是否存在家装客资分配权限
+export function selectAllocationPermission(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectAllocationPermission',
+    method: 'get',
+    params: query,
+  });
+}

+ 41 - 10
src/router/index.js

@@ -46,16 +46,6 @@ const router = new VueRouter({
           name: 'login',
           component: () => import('@/views/home/login.vue'),
         },
-        {
-          path: '/clew',
-          name: 'clew',
-          component: () => import('@/views/clew/index.vue'),
-        },
-        {
-          path: '/clewent',
-          name: 'clew',
-          component: () => import('@/views/clew/clewent.vue'),
-        },
         // {
         //     path: "/logincs",
         //     name: "logincs",
@@ -413,6 +403,47 @@ const router = new VueRouter({
           name: 'VisitSummaryDetail',
           component: () => import('@/views/week/VisitSummaryDetail.vue'),
         },
+        {
+          path: '/assignAwait',
+          name: 'assignAwait',
+          meta: { title: '待分配客资' },
+          component: () => import('@/views/week/assignAwait/index.vue'),
+        },
+        {
+          path: '/assignPage',
+          name: 'assignPage',
+          meta: { title: '分配客资' },
+          component: () => import('@/views/week/assignAwait/assignPage.vue'),
+        },
+        {
+          path: '/JZfollowUp',
+          name: 'JZfollowUp',
+          meta: { title: '客资跟进' },
+          component: () => import('@/views/week/assignAwait/JZfollowUp.vue'),
+        },
+      ],
+    },
+    {
+      path: '/clew',
+      component: layout,
+      redirect: '/clew',
+      children: [
+        {
+          path: '/clew',
+          name: 'clew',
+          component: () => import('@/views/clew/index.vue'),
+        },
+        {
+          path: '/clewent',
+          name: 'clewent',
+          component: () => import('@/views/clew/clewent.vue'),
+        },
+        {
+          path: '/complaintDetail',
+          name: 'complaintDetail',
+          meta: { title: '渠道投诉' },
+          component: () => import('@/views/clew/complaintDetail/index.vue'),
+        },
       ],
     },
   ],

+ 5 - 1
src/store/modules/user.js

@@ -1,4 +1,5 @@
 import { getMobileUserInfo } from '@/api/index';
+import { selectAllocationPermission } from '@/api/week';
 
 const user = {
   state: {
@@ -13,11 +14,14 @@ const user = {
     SET_ACTIVA_TYPE_STORE: (state, value) => {
       state.activaTypeStore = value;
     },
+    SET_ASSIGN_FLAG: (state, value) => {
+      state.isAssignFlag = value;
+    },
   },
 
   actions: {
     // 获取用户信息
-    getUserInfo({ commit, state }) {
+    getUserInfo({ dispatch, commit, state }) {
       return new Promise((resolve, reject) => {
         getMobileUserInfo()
           .then((res) => {

+ 1 - 1
src/utils/request.js

@@ -9,7 +9,7 @@ import errorCode from '@/utils/errorCode';
 axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
 const service = axios.create({
   baseURL: process.env.VUE_APP_BASE_API,
-  timeout: 30000,
+  timeout: 0, //30000
   withCredentials: true,
 });
 // request拦截器

+ 81 - 0
src/views/clew/complaintDetail/complaintLog.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="cardclewContent infoDetail">
+    <div class="complaintLog" v-for="(iten, index) in customerClueInfoComplaintList" :key="index">
+      <div class="info">
+        <div class="label">来电分类:</div>
+        <div class="value">
+          <!-- 一级分类 -->
+          <span v-if="iten.customerClassifyName">{{ iten.customerClassifyName }}-</span>
+          <!-- 二级分类 -->
+          <span v-if="iten.customerSubClassifyName">{{ iten.customerSubClassifyName }}-</span>
+          <!-- 三级分类 -->
+          <span v-if="iten.customerThreeClassifyName">{{ iten.customerThreeClassifyName }}</span>
+        </div>
+      </div>
+      <div class="info">
+        <div class="label">用户诉求:</div>
+        <div class="value">{{ iten.userDemand }}</div>
+      </div>
+      <div class="info">
+        <div class="label">反馈内容:</div>
+        <div class="value">{{ iten.feedbackContent }}</div>
+      </div>
+      <div class="info">
+        <div class="label">来点时间:</div>
+        <div class="value">{{ iten.createTime }}</div>
+      </div>
+      <div class="info">
+        <div class="label">来点次数:</div>
+        <div class="value">{{ iten.serialNumber }}</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getDictOption } from '@/api/index';
+export default {
+  name: 'infoDetail',
+  props: {
+    customerClueInfoComplaintList: {
+      type: Array,
+      default: () => [],
+    },
+  },
+  data() {
+    return {};
+  },
+  activated() {},
+  methods: {},
+};
+</script>
+<style scoped lang="scss">
+.cardclewContent {
+  background: #fff;
+  box-sizing: border-box;
+  padding: 10px 16px;
+  margin: 6px 10px 10px;
+}
+.cardclewContent .info {
+  font-size: 14px;
+  color: #444;
+  line-height: 28px;
+  display: flex;
+  .value {
+    flex: 1;
+  }
+}
+.cardclewContent .title p {
+  padding: 0;
+  margin: 0;
+}
+.cardclewContent .title .textLeft {
+  display: inline-block;
+  padding-bottom: 10px;
+}
+.complaintLog {
+  border-bottom: 1px solid #ccc;
+  margin-bottom: 8px;
+  padding-bottom: 8px;
+}
+</style>

+ 246 - 0
src/views/clew/complaintDetail/followUpHistory.vue

@@ -0,0 +1,246 @@
+<template>
+  <div
+    class="container linep containertext followUpHistory"
+    style="
+      background-color: #fff;
+      width: 94%;
+      margin: 0px auto;
+      border-radius: 6px;
+      font-size: 14px;
+    ">
+    <van-form ref="tabstoreVal">
+      <div v-for="(item, index) in customItemList" :key="index">
+        <div
+          class="formLabel z-cell z-cells z-celly"
+          v-if="item.answerType == 'sz' || item.answerType == 'rq'">
+          <van-cell>
+            <template #title
+              >{{ index + 1 }}.{{ item.customerClueName }}
+              <span
+                style="color: #444; font-size: 12px; margin: 0; padding: 10px 0; text-align: right"
+                >{{ item.remark }}</span
+              ></template
+            >
+          </van-cell>
+          <p class="mg0" style="word-break: break-all">{{ item.answerValue }}</p>
+        </div>
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'zp'">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+        <div
+          class="formLabel z-cell z-cells z-celly"
+          v-if="item.answerType == 'wb' && viewTextShow">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <div class="selesetText">
+            <p class="mg0" style="word-break: break-all">{{ item.answerValue }}</p>
+          </div>
+          <!-- <p style="color: #444; font-size: 12px; margin: 0; padding: 10px 0; text-align: right">
+            {{ item.remark }}
+          </p> -->
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'duox'">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <div class="selesetText">
+            <div class="mg0" v-for="(item1, index1) in item.customerClueOptionList" :key="index1">
+              <p style="color: #0057ba" v-if="item1.checked">
+                <i
+                  style="
+                    border: 1px solid #0057ba;
+                    margin-right: 10px;
+                    border-radius: 2px;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item1.customerClueOption }}</span>
+              </p>
+              <p v-if="!item1.checked">
+                <i
+                  style="
+                    border: 1px solid #ccc;
+                    margin-right: 10px;
+                    border-radius: 2px;
+                    color: #fff;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item1.customerClueOption }}</span>
+              </p>
+            </div>
+          </div>
+        </div>
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'dx'">
+          <van-cell>
+            <template #title> {{ index + 1 }}.{{ item.customerClueName }} </template>
+          </van-cell>
+          <div class="selesetText">
+            <div class="mg0" v-for="(item2, index2) in item.customerClueOptionList" :key="index2">
+              <p style="color: #0057ba" v-if="item2.checked">
+                <i
+                  style="
+                    border: 1px solid #0057ba;
+                    border-radius: 40px;
+                    margin-right: 10px;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item2.customerClueOption }}</span>
+              </p>
+              <p v-if="!item2.checked">
+                <i
+                  style="
+                    border: 1px solid #ccc;
+                    border-radius: 40px;
+                    margin-right: 10px;
+                    color: #fff;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item2.customerClueOption }}</span>
+              </p>
+            </div>
+          </div>
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+      </div>
+    </van-form>
+  </div>
+</template>
+<script>
+import deleteUploadImg from '@/components/deleteUploadImg2';
+import deleteUploadImgv from '@/components/deleteUploadImg';
+import { getCustomerClueAnswerById } from '@/api/clew';
+export default {
+  components: { deleteUploadImg, deleteUploadImgv },
+  props: {
+    historyId: {
+      type: [String, Number],
+      default: '',
+    },
+    showView: {
+      type: Boolean,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      customItemList: [],
+      viewTextShow: false,
+    };
+  },
+  watch: {
+    showView: {
+      handler(val) {
+        if (val) this.getFollowUpHistory();
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    getFollowUpHistory(val) {
+      this.toastLoading(0, '加载中...', true);
+      getCustomerClueAnswerById({ userCustomerClueId: this.historyId }).then((res) => {
+        this.toastLoading().clear();
+        this.customItemList = res.data.customerClue.customerClueItemList;
+        for (var pl = 0; pl < this.customItemList[1].customerClueOptionList.length; pl++) {
+          if (
+            this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+              '跟进中'
+            ) != -1
+          ) {
+            if (this.customItemList[1].customerClueOptionList[pl].value == 'Y') {
+              this.viewTextShow = true;
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '跟进'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName =
+                  '具体合作意向反馈(拜访时间、合同方向、预估合作时间)';
+              }
+            }
+          } else {
+            if (this.customItemList[1].customerClueOptionList[pl].value == 'Y') {
+              this.viewTextShow = true;
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '意向'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '没有意向原因';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '开户'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '开户经销商代码(例:0110067321)';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '开店'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '开户门店代码(例:0190129032)';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '出货'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '填写具体的产品和数量';
+              }
+            }
+          }
+        }
+      });
+    },
+  },
+};
+</script>
+<style lang="scss">
+.followUpHistory {
+  .formLabel {
+    margin: 0 16px;
+    border-bottom: 1px solid #f1f1f1;
+  }
+  .formLabel .van-cell {
+    padding: 10px 0;
+    font-size: 14px;
+  }
+  .formLabel .van-cell::after {
+    border: 0;
+  }
+  .formLabeltext .van-field {
+    border: 1px solid #f1f1f1;
+    padding: 6px;
+    width: 100%;
+    border-radius: 4px;
+    overflow: hidden;
+  }
+  .formLabel .van-field__control {
+    padding: 0 10px;
+  }
+  .z-checkbox .van-radio {
+    padding: 6px 0;
+  }
+  .z-cell .van-cell__title {
+    font-size: 14px;
+  }
+  .formLabel .van-radio__label,
+  .formLabel .van-checkbox__label {
+    font-size: 14px;
+  }
+}
+</style>

+ 377 - 0
src/views/clew/complaintDetail/index.vue

@@ -0,0 +1,377 @@
+<template>
+  <div class="bgcolor complaintDetail">
+    <div class="navBarTOP">
+      <van-nav-bar class="navBar" left-arrow :title="title" @click-left="onClickLeft" />
+    </div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <!-- 客诉详情 -->
+    <infoDetail v-if="infoData" :infoData="infoData" :customerClassify="customerClassifyOption">
+    </infoDetail>
+    <!-- 客诉记录 -->
+    <complaintLog
+      v-if="infoData && infoData.customerClueInfoComplaintList.length"
+      :customerClueInfoComplaintList="infoData.customerClueInfoComplaintList"></complaintLog>
+    <!-- 跟进记录 userCustomerClueList -->
+    <!-- 历史跟进记录 -->
+    <p style="margin: 0 16px 8px; color: #888" v-if="infoData && infoData.userCustomerClueList">
+      该客诉历史跟进记录
+    </p>
+    <van-cell-group inset class="cardclewContentCell" v-if="infoData">
+      <div style="border-radius: 6px; overflow: hidden">
+        <van-cell
+          is-link
+          v-for="(item, index) in infoData.userCustomerClueList"
+          :key="index"
+          @click="viewFn(item.userCustomerClueId)">
+          <template #title>
+            <span class="custom-title">{{ item.nickName }}</span>
+          </template>
+          <div class="cardContent">
+            <p class="textLeft" style="padding-bottom: 0px; margin: 0">{{ item.createTime }}</p>
+          </div>
+        </van-cell>
+      </div>
+    </van-cell-group>
+    <!-- 跟进任务填写 -->
+    <div class="assign" v-if="infoData && infoData.isClose != 1">
+      <!-- <div class="assign"> -->
+      <!-- 来电分类 -->
+      <div class="complaintType">
+        <div class="typeItem">
+          <van-field
+            readonly
+            clickable
+            name="picker"
+            :value="customerClassifyValue"
+            label="来电分类一"
+            placeholder="点击选择来电分类一"
+            @click="showPicker1 = true" />
+        </div>
+        <div class="typeItem">
+          <van-field
+            readonly
+            clickable
+            name="picker"
+            :value="customerSubClassifyValue"
+            label="来电分类二"
+            placeholder="点击选择来电分类二"
+            @click="showPicker2 = true" />
+        </div>
+      </div>
+      <!-- <div class="followUp required">跟进结果</div> -->
+      <div class="taskGather" v-if="taskGather">
+        <radioGroup :clueOptionList="taskGather"></radioGroup>
+      </div>
+      <div class="tc" style="padding: 0 16px">
+        <van-button class="submitBtn" block type="info" color="#0057ba" @click="onSubmit">
+          提交
+        </van-button>
+      </div>
+    </div>
+    <!-- 客诉跟进历史 -->
+    <van-dialog
+      v-model="showView"
+      title="客诉历史"
+      show-cancel-button
+      cancel-button-text="关闭"
+      :show-confirm-button="false"
+      class="dialogz">
+      <followUpHistory :historyId="historyId" :showView="showView"></followUpHistory>
+    </van-dialog>
+    <van-popup v-model="showPicker1" position="bottom">
+      <van-picker
+        show-toolbar
+        value-key="dictLabel"
+        :columns="customerClassifyOption"
+        @confirm="onConfirm1"
+        @cancel="showPicker1 = false" />
+    </van-popup>
+    <van-popup v-model="showPicker2" position="bottom">
+      <van-picker
+        show-toolbar
+        value-key="dictLabel"
+        :columns="customerSubClassifyOption"
+        @confirm="onConfirm2"
+        @cancel="showPicker2 = false" />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import {
+  getComplaintCustomerClueInfoById,
+  insertCustomerClueAnswerKs,
+} from '@/api/complaintDetail';
+import infoDetail from './infoDetail.vue';
+import complaintLog from './complaintLog.vue';
+import { mapState } from 'vuex';
+import radioGroup from './radioGroup';
+import followUpHistory from './followUpHistory';
+import { getDictOption } from '@/api/index';
+
+export default {
+  name: 'complaintDetail',
+  components: {
+    infoDetail,
+    complaintLog,
+    radioGroup,
+    followUpHistory,
+  },
+  computed: {
+    ...mapState({
+      userInfo: (state) => state.user.userInfo,
+    }),
+  },
+  data() {
+    return {
+      id: '',
+      infoData: null,
+      title: '',
+      postName: '',
+      taskGather: null, //跟进任务集合
+      requiredFlag: true, //问题必填检验
+      requiredMessage: '', //必填提示信息
+      showView: false,
+      historyId: '',
+      customerClassifyValue: '',
+      customerClassify: '',
+      customerSubClassifyValue: '',
+      customerSubClassify: '',
+      showPicker1: false,
+      showPicker2: false,
+      customerClassifyOption: [],
+      customerSubClassifyOption: [],
+    };
+  },
+  watch: {},
+  activated() {
+    this.id = this.$route.query.id;
+    this.postName = localStorage.getItem('postName');
+    this.getComplaintCustomerClueInfoByIdFun();
+  },
+  methods: {
+    async getCustomerClassify() {
+      let option = await getDictOption({}, 'customer_classify');
+      this.customerClassifyOption = option.data || [];
+      // 来电分类一
+      let item = this.customerClassifyOption.find(
+        (val) => val.dictValue == this.infoData.customerClassify
+      );
+      this.customerClassifyValue = item.dictLabel || '';
+      this.customerClassify = item.dictValue;
+    },
+    async getCustomerSubClassify() {
+      let option = await getDictOption({}, 'customer_sub_classify');
+      this.customerSubClassifyOption = option.data || [];
+      // 来电分类二
+      let item = this.customerSubClassifyOption.find(
+        (val) => val.dictValue == this.infoData.customerSubClassify
+      );
+      this.customerSubClassifyValue = item.dictLabel || '';
+      this.customerSubClassify = item.dictValue;
+    },
+    getComplaintCustomerClueInfoByIdFun() {
+      this.toastLoading(0, '加载中...', true);
+      this.id = this.$route.query.id;
+      getComplaintCustomerClueInfoById({ customerClueInfoId: this.id }).then((response) => {
+        this.toastLoading().clear();
+        if (response.code == 200) {
+          this.infoData = response.data;
+          this.title = response.data.name;
+          if (this.infoData.isClose != 1) {
+            // response.data.customerClue.customerClueItemList[0].customerClueInfoId = this.id;
+            this.taskGather = response.data.customerClue.customerClueItemList;
+          }
+          this.getCustomerClassify();
+          this.getCustomerSubClassify();
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    onSubmit() {
+      // 没有选择跟进记录
+      // if (!this.taskGather[0].searchValue) {
+      //   this.$toast('请选择跟进结果');
+      //   return;
+      // }
+      this.requiredFlag = true;
+      let customerClueItemList = [];
+      // 每一个层级都是一道题的题目,子级就是题,被选中和填写的题要带上题目一块上传(题的同级也要上传)
+      // 第一级题目下的题默认都要上传
+      let params = {
+        customerClueItemList: [],
+        customerClassify: this.customerClassify,
+        customerSubClassify: this.customerSubClassify,
+      };
+      params.customerClueItemList.push(...this.deepClone(this.taskGather, 0));
+      // let optionList = this.taskGather[0].customerClueOptionList;
+      this.filterOption(this.taskGather, params);
+      console.log(JSON.stringify(params));
+      // 必填验证
+      if (this.requiredFlag) {
+        this.toastLoading(0, '加载中...', true);
+        insertCustomerClueAnswerKs(params).then((res) => {
+          this.toastLoading().clear();
+          if (res.code == 200) {
+            this.$toast(res.msg);
+            window.location.replace(window.location.origin + '/mobile/clew');
+          } else {
+            this.$toast(res.msg);
+          }
+        });
+      } else {
+        this.$toast(this.requiredMessage);
+      }
+    },
+    filterOption(optionList, params) {
+      for (let val = 0; val < optionList.length; val++) {
+        if (
+          optionList[val].isMust == '0' &&
+          optionList[val].searchValue == null &&
+          optionList[val].answerType == 'dx'
+        ) {
+          // 题目必填校验
+          this.requiredFlag = false;
+          this.requiredMessage = '请选择' + optionList[val].customerClueName;
+          return;
+        } else if (optionList[val].isMust == '0' && optionList[val].searchValue) {
+          let customerClueOptionList = optionList[val].customerClueOptionList;
+          for (let i = 0; i < customerClueOptionList.length; i++) {
+            if (customerClueOptionList[i].value == 'Y') {
+              if (customerClueOptionList[i].customerClueItemList) {
+                // 必填校验
+                this.isRequiredFlag(customerClueOptionList[i].customerClueItemList);
+                let customerClueItemList =
+                  params.customerClueItemList[val].customerClueOptionList[i].customerClueItemList;
+                customerClueItemList.push(
+                  ...this.deepClone(customerClueOptionList[i].customerClueItemList, 0)
+                );
+                if (customerClueOptionList[i].customerClueItemList[0]) {
+                  this.filterOption(customerClueOptionList[i].customerClueItemList, params);
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    // 深拷贝指定拷贝层级
+    deepClone(obj, num) {
+      // 检查是否为对象或数组
+      if (obj === null || typeof obj !== 'object') {
+        return obj; // 基本类型直接返回
+      }
+      // 创建一个数组或对象
+      const copy = Array.isArray(obj) ? [] : {};
+      // 遍历对象的每个属性
+      for (const key in obj) {
+        if (obj.hasOwnProperty(key) && num < 2) {
+          // 递归调用深拷贝
+          if (key == 'customerClueOptionList' || key == 'customerClueItemList') {
+            num = num + 1;
+          }
+          copy[key] = this.deepClone(obj[key], num);
+        }
+      }
+      return copy;
+    },
+    isRequiredFlag(optionList) {
+      // console.log(optionList);
+      // 必填类型
+      for (let i = 0; i < optionList.length; i++) {
+        if (optionList[i].answerType == 'wb' && optionList[i].isMust == 0) {
+          if (!optionList[i].answerValue) {
+            this.requiredFlag = false;
+            this.requiredMessage = optionList[i].remark;
+            return;
+          }
+        }
+      }
+    },
+    // 校验错误返回信息
+    onFailed(errorInfo) {
+      console.log('failed', errorInfo);
+    },
+    // 查看历史跟进记录
+    viewFn(val) {
+      //   this.viewTextShow = false;
+      this.historyId = val;
+      this.showView = true;
+    },
+    onConfirm1(value) {
+      this.customerClassifyValue = value.dictLabel;
+      this.customerClassify = value.dictValue;
+      this.showPicker1 = false;
+    },
+    onConfirm2(value) {
+      this.customerSubClassifyValue = value.dictLabel;
+      this.customerSubClassify = value.dictValue;
+      this.showPicker2 = false;
+    },
+    onClickLeft() {
+      this.$router.go(-1);
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.complaintDetail {
+  .assign {
+    margin: 10px;
+    background-color: #fff;
+    padding-bottom: 20px;
+    .followUp {
+      padding: 16px;
+      font-size: 14px;
+      font-weight: 600;
+    }
+  }
+  .taskGather {
+    padding-left: 20px;
+    .title {
+      font-size: 15px;
+      font-weight: 600;
+      padding: 5px 0;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.complaintDetail {
+  .van-field__label {
+    width: 100px;
+    &::before {
+      content: '*';
+      color: red;
+    }
+  }
+  .centerBtn {
+    float: right;
+    background: #0057ba;
+    border-color: #0057ba;
+    color: #fff;
+    margin-top: -33px;
+    border-radius: 5px;
+  }
+  .dialogz {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    height: 72vh;
+    .van-dialog__content {
+      flex: 1;
+      overflow-y: auto;
+    }
+  }
+  .van-cell {
+    font-size: 15px;
+    color: #000;
+  }
+}
+</style>

+ 77 - 0
src/views/clew/complaintDetail/infoDetail.vue

@@ -0,0 +1,77 @@
+<template>
+  <div class="cardclewContent infoDetail">
+    <div class="info">姓名:{{ infoData.name }}</div>
+    <div class="info">
+      手机号:<a
+        style="color: #0057ba; font-weight: bold; text-decoration: underline"
+        :href="'tel:' + infoData.phone"
+        >{{ infoData.phone }}<van-icon name="phone"
+      /></a>
+    </div>
+    <div class="info">区域:{{ infoData.companyName }}</div>
+    <div class="info">省:{{ infoData.provinceName }}</div>
+    <div class="info">市:{{ infoData.cityName }}</div>
+    <div class="info">区/县:{{ infoData.countryName }}</div>
+    <div class="info">
+      分类:
+      <span v-if="customerClassify.length">{{ filterClassify(infoData.customerClassify) }}</span>
+    </div>
+    <div class="info">详细地址:{{ infoData.detailAddress }}</div>
+    <div class="info">商店授权号:{{ infoData.storeCode }}</div>
+    <div class="info">商店地址:{{ infoData.storeAddr }}</div>
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'infoDetail',
+  props: {
+    infoData: {
+      type: Object,
+      default: {},
+    },
+    source: {
+      // assignPage 拥有家装分配权限
+      type: String,
+    },
+    customerClassify: {
+      type: Array,
+      default: () => [],
+    },
+  },
+  data() {
+    return {};
+  },
+  methods: {
+    filterClassify(customerClassify) {
+      let item = this.customerClassify.find((val) => val.dictValue == customerClassify);
+      return item.dictLabel || '';
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.cardclewContentCell {
+  margin: 0 10px;
+}
+.cardclewContent {
+  background: #fff;
+  box-sizing: border-box;
+  padding: 10px 16px;
+  margin: 6px 10px 10px;
+}
+.cardclewContent .info {
+  font-size: 14px;
+  color: #444;
+  line-height: 28px;
+}
+.cardclewContent .title p {
+  padding: 0;
+  margin: 0;
+}
+.cardclewContent .title .textLeft {
+  display: inline-block;
+  padding-bottom: 10px;
+}
+</style>

+ 139 - 0
src/views/clew/complaintDetail/radioGroup.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="radioGroup">
+    <template v-for="(val, ind) in clueOptionList">
+      <div class="title" v-if="val.customerClueName">
+        <span class="van-f-red" v-if="val.isMust == 0">*</span>
+        {{ val.customerClueName }}
+      </div>
+      <template v-if="val.answerType == 'dx'">
+        <van-radio-group v-model="val.searchValue" @change="radioGroupChange">
+          <template v-for="(item, index) in val.customerClueOptionList">
+            <van-radio :name="item.customerClueOptionId" :key="index" @click="radioClick">
+              {{ item.customerClueOption }}
+            </van-radio>
+            <radioGroup
+              :clueOptionList="item.customerClueItemList"
+              :parentOptionList="val"
+              :parentId="item.customerClueOptionId"
+              v-if="val.searchValue == item.customerClueOptionId"></radioGroup>
+          </template>
+        </van-radio-group>
+      </template>
+      <template v-if="val.answerType == 'wb'">
+        <template v-if="parentOptionList.searchValue == val.itemOptionParentId">
+          <van-field v-model="val.answerValue" :placeholder="val.remark" />
+        </template>
+      </template>
+    </template>
+  </div>
+</template>
+<script>
+export default {
+  name: 'radioGroup',
+  props: {
+    clueOptionList: {
+      type: Array,
+      default: () => [],
+    },
+    parentOptionList: {
+      type: Object,
+      default: () => {},
+    },
+    parentId: {
+      type: [Number, null],
+      default: null,
+    },
+  },
+  data() {
+    return {};
+  },
+  created() {
+    // console.log(this.clueOptionList);
+  },
+  methods: {
+    radioGroupChange(name) {
+      if (!name) return;
+      // console.log(name);
+      // console.log(this.clueOptionList);
+      // 如果选中的数据有父级,将父级数据修改为选中状态
+      if (this.parentOptionList && this.parentId) {
+        this.$set(this.parentOptionList, 'searchValue', this.parentId);
+      }
+      // 获取选中数据
+      let clueOptionList = this.clueOptionList.find((val) => val.searchValue == name);
+      let activaRadio = clueOptionList.customerClueOptionList.find(
+        (val) => val.customerClueOptionId == name
+      );
+      // 修改选中状态
+      this.$set(activaRadio, 'value', 'Y');
+
+      // 过滤未选中的数据
+      let exceptItself = clueOptionList.customerClueOptionList.filter(
+        (val) => val.customerClueOptionId !== name
+      );
+      // 删除未选中数据状态兄弟级和子级
+      // console.log(exceptItself);
+      this.toggleOtheChildren(exceptItself);
+    },
+    toggleOtheChildren(exceptItself) {
+      if (!exceptItself) return;
+      exceptItself.forEach((val) => {
+        this.$set(val, 'value', 'N');
+        if (val.customerClueItemList && val.customerClueItemList[0]) {
+          this.$set(val.customerClueItemList[0], 'searchValue', null);
+          if (
+            val.customerClueItemList[0] &&
+            val.customerClueItemList[0].customerClueOptionList.length
+          ) {
+            this.toggleOtheChildren(val.customerClueItemList[0].customerClueOptionList);
+          }
+        }
+      });
+    },
+    radioClick(event) {
+      // console.log(event);
+    },
+  },
+};
+</script>
+<style lang="scss">
+.radioGroup {
+  font-size: 15px;
+  .van-radio {
+    padding: 5px 0;
+  }
+  .van-radio-group {
+    padding-left: 20px;
+  }
+  .van-cell {
+    padding: 10px 0;
+    /* font-size: 14px; */
+  }
+  .van-cell::after {
+    border: 0;
+  }
+  .van-field {
+    border: 1px solid #cdc8c8;
+    padding: 0;
+    width: 100%;
+    border-radius: 4px;
+    overflow: hidden;
+    background-color: unset;
+    height: 30px;
+    line-height: 30px;
+    margin: 5px 0;
+  }
+  .van-field__control {
+    padding: 0 10px;
+  }
+  .van-field__control {
+    padding: 0 10px;
+  }
+  .van-radio__label {
+    /* font-size: 14px; */
+  }
+  .van-f-red {
+    font-size: 14px;
+  }
+}
+</style>

+ 96 - 24
src/views/clew/index.vue

@@ -1,7 +1,16 @@
 <template>
-  <div class="bgcolor">
+  <div class="bgcolor clew">
     <div class="navBarTOP">
-      <van-nav-bar class="navBar" title="客资任务" left-arrow @click-left="onClickLeft" />
+      <van-nav-bar class="navBar" title="客资任务" left-arrow @click-left="onClickLeft">
+        <template #right>
+          <van-dropdown-menu>
+            <van-dropdown-item
+              v-model="cid"
+              :options="customerClueOption"
+              @change="dropdownChange" />
+          </van-dropdown-menu>
+        </template>
+      </van-nav-bar>
       <van-tabs v-model="isHandle" color="#0057ba" @change="tabChange">
         <van-tab title="未跟进" name="-1" :disabled="disabled"></van-tab>
         <van-tab title="跟进中" name="0" :disabled="disabled"></van-tab>
@@ -27,9 +36,15 @@
                 <p class="textRight" v-if="isHandle == '-1' || isHandle == '0'">详情</p>
               </div>
               <div class="info">
-                客资线索:<span v-if="item.cid == 3">申请代理</span
-                ><span v-if="item.cid == 4">开设门店</span
-                ><span v-if="item.cid == 5">批量采购</span>
+                客资线索:
+                <span v-if="item.cid == 3">申请代理</span>
+                <span v-if="item.cid == 4">开设门店</span>
+                <span v-if="item.cid == 5">批量采购</span>
+                <span v-if="item.cid == 7">家装客资</span>
+                <span v-if="item.cid == 9">渠道客诉</span>
+              </div>
+              <div class="info" v-if="item.cid == 7">
+                家装类型:{{ item.jzCustomerDescription }}
               </div>
               <div class="info">联系电话:{{ item.phone }}</div>
               <div class="info" v-if="item.createTime">首次接入时间:{{ item.createTime }}</div>
@@ -47,6 +62,7 @@
 
 <script>
 import { infolist } from '@/api/clew';
+import { getDictOption } from '@/api';
 export default {
   name: 'clew',
   data() {
@@ -56,20 +72,23 @@ export default {
       loading: false,
       list: [],
       finished: true,
+      cid: -1,
+      customerClueOption: [],
     };
   },
-  created() {
+  activated() {
+    this.getCustomerClue();
     this.approveList();
   },
   watch: {
     $route(to, from) {
-      if (to.path == '/clew' && from.path == '/My/index') {
-        this.approveList();
-      }
-      if (to.path == '/clew' && from.path == '/clewent') {
-        this.pageNum = 1;
-        this.approveList();
-      }
+      // if (to.path == '/clew' && from.path == '/My/index') {
+      //   this.approveList();
+      // }
+      // if (to.path == '/clew' && from.path == '/clewent') {
+      //   this.pageNum = 1;
+      //   this.approveList();
+      // }
     },
   },
   beforeRouteLeave(to, from, next) {
@@ -79,6 +98,18 @@ export default {
     next();
   },
   methods: {
+    async getCustomerClue() {
+      let option = await getDictOption({}, 'customer_clue');
+      let arr = [{ text: '全部', value: -1 }];
+      option.data.forEach((val) => {
+        arr.push({
+          text: val.dictLabel,
+          value: val.dictValue,
+        });
+      });
+      this.customerClueOption = arr;
+      console.log(this.customerClueOption);
+    },
     submit() {
       var taskIds = [];
       var instanceIds = [];
@@ -111,11 +142,26 @@ export default {
         });
     },
     approveFn(row) {
-      this.list = [];
-      this.$router.push({
-        path: '/clewent',
-        query: { id: row.customerClueInfoId },
-      });
+      if (row.cid == 9) {
+        // 渠道客诉
+        this.$router.push({
+          path: '/complaintDetail',
+          query: { id: row.customerClueInfoId },
+        });
+      } else if (row.cid == 7) {
+        // 家装客资跟进
+        this.$router.push({
+          path: '/JZfollowUp',
+          query: { id: row.customerClueInfoId },
+        });
+      } else {
+        // 非家装客资跟进
+        this.list = [];
+        this.$router.push({
+          path: '/clewent',
+          query: { id: row.customerClueInfoId },
+        });
+      }
     },
     tabChange() {
       this.disabled = true;
@@ -129,20 +175,25 @@ export default {
         message: '数据加载中...',
         forbidClick: true,
       });
-      infolist({ type: this.isHandle }).then((res) => {
+      infolist({ type: this.isHandle, cid: this.cid == -1 ? null : this.cid }).then((res) => {
         loading1.clear();
         this.disabled = false;
         this.loading = false;
         this.list = res.data;
       });
     },
+    dropdownChange(value) {
+      this.list = [];
+      this.pageNum = 1;
+      this.approveList();
+    },
     onClickLeft() {
       this.$router.push('/My/index');
     },
   },
 };
 </script>
-<style>
+<style lang="scss">
 .newCarList {
   margin: 14px;
   border-radius: 8px;
@@ -153,10 +204,10 @@ export default {
   overflow: hidden;
 }
 .newCarList .newlist .title {
-  line-height: 32px;
+  /* line-height: 32px; */
 }
 .newCarList .van-cell__right-icon {
-  top: -4px;
+  top: 5px;
 }
 .newCarList .newlist {
   box-sizing: border-box;
@@ -165,7 +216,8 @@ export default {
   font-size: 14px;
   font-weight: bold;
   color: #333;
-  line-height: 14px;
+  padding: 5px 0;
+  /* line-height: 14px; */
 }
 .newCarList .newlist .info {
   font-size: 14px;
@@ -178,7 +230,7 @@ export default {
 }
 .newCarList .newlist .title .textLeft {
   display: inline-block;
-  padding-bottom: 10px;
+  /* padding-bottom: 10px; */
 }
 .newCarList .newlist .title .textRight {
   float: right;
@@ -187,4 +239,24 @@ export default {
 .van-tab--active {
   color: #0057ba;
 }
+.clew {
+  .van-nav-bar {
+    z-index: 2;
+  }
+  .van-nav-bar__right {
+    height: 40px;
+    margin-top: 3px;
+    .van-dropdown-menu {
+      height: 100%;
+      border: 1px solid #f4f0f0;
+    }
+  }
+  .van-dropdown-menu__bar {
+    padding: 0 4px;
+    height: 100%;
+  }
+  .van-dropdown-menu__title::after {
+    right: 0 !important;
+  }
+}
 </style>

+ 369 - 0
src/views/week/assignAwait/JZfollowUp.vue

@@ -0,0 +1,369 @@
+<template>
+  <div class="bgcolor assignPage">
+    <div class="navBarTOP">
+      <van-nav-bar class="navBar" left-arrow :title="title" @click-left="onClickLeft" />
+    </div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <!-- 客资详情 -->
+    <assignAwaitDetail :infoData="infoData" v-if="infoData" :source="'JZfollowUp'">
+      <div class="info">
+        跟进状态:
+        <span v-if="infoData.isClose == 1">跟进完成</span>
+        <span v-if="infoData.isClose == 0">跟进中</span>
+        <span v-if="infoData.isClose == -1">未跟进</span>
+      </div>
+      <!-- 转交 -->
+      <!--  -->
+      <van-button
+        v-if="infoData.isClose != 1 && postName && postName == '区域家装销售负责人'"
+        type="info"
+        size="small"
+        plain
+        class="centerBtn"
+        @click="showPicker = true">
+        转交下属
+      </van-button>
+    </assignAwaitDetail>
+    <!-- 历史跟进记录 -->
+    <p style="margin: 0 16px 8px; color: #888" v-if="infoData && infoData.userCustomerClueList">
+      该客资历史跟进记录
+    </p>
+    <van-cell-group inset class="cardclewContentCell" v-if="infoData">
+      <div style="border-radius: 6px; overflow: hidden">
+        <van-cell
+          is-link
+          v-for="(item, index) in infoData.userCustomerClueList"
+          :key="index"
+          @click="viewFn(item.userCustomerClueId)">
+          <template #title>
+            <span class="custom-title">{{ item.nickName }}</span>
+          </template>
+          <div class="cardContent">
+            <p class="textLeft" style="padding-bottom: 0px; margin: 0">{{ item.createTime }}</p>
+          </div>
+        </van-cell>
+      </div>
+    </van-cell-group>
+    <!-- 跟进任务填写 -->
+    <div class="assign" v-if="infoData && infoData.isClose != 1">
+      <div class="followUp required">跟进结果</div>
+      <div class="taskGather" v-if="taskGather && taskGather">
+        <radioGroup :clueOptionList="taskGather"></radioGroup>
+      </div>
+      <div class="tc" style="padding: 0 16px">
+        <van-button class="submitBtn" block type="info" color="#0057ba" @click="onSubmit">
+          提交
+        </van-button>
+      </div>
+    </div>
+    <!-- 转交下属 -->
+    <van-popup v-model="showPicker" position="bottom">
+      <van-picker
+        title="转交下属"
+        show-toolbar
+        value-key="nickName"
+        :columns="assignTypeData"
+        @confirm="confirm"
+        @cancel="showPicker = false" />
+    </van-popup>
+    <!-- 客资跟进历史 -->
+    <van-dialog
+      v-model="showView"
+      title="客资历史"
+      show-cancel-button
+      cancel-button-text="关闭"
+      :show-confirm-button="false"
+      class="dialogz">
+      <followUpHistory :historyId="historyId" :showView="showView"></followUpHistory>
+    </van-dialog>
+  </div>
+</template>
+
+<script>
+import { getCustomerClueInfoById } from '@/api/clew';
+import { getDictOption } from '@/api/index';
+import assignAwaitDetail from './assignAwaitDetail.vue';
+import {
+  selectCustomerClueInfoById,
+  selectSubUserList,
+  allocationSubCustomer,
+  insertFollowCustomerClueAnswer,
+} from '@/api/assignAwait';
+import { mapState } from 'vuex';
+import radioGroup from './radioGroup';
+import followUpHistory from './followUpHistory';
+
+export default {
+  name: 'assignPage',
+  components: {
+    assignAwaitDetail,
+    radioGroup,
+    followUpHistory,
+  },
+  computed: {
+    ...mapState({
+      userInfo: (state) => state.user.userInfo,
+    }),
+  },
+  data() {
+    return {
+      id: '',
+      infoData: null,
+      collectionItemList: [],
+      title: '',
+      postName: '',
+      showPicker: false,
+      assignTypeData: [],
+      taskGather: null, //跟进任务集合
+      showView: false,
+      historyId: '',
+      requiredFlag: true, //问题必填检验
+      requiredMessage: '', //必填提示信息
+    };
+  },
+  watch: {},
+  activated() {
+    this.id = this.$route.query.id;
+    this.postName = localStorage.getItem('postName');
+    this.getCustomerClueInfoById();
+  },
+  methods: {
+    getCustomerClueInfoById() {
+      this.toastLoading(0, '加载中...', true);
+      this.id = this.$route.query.id;
+      this.collectionAnswerlisd = [];
+      this.collectionItemList = [];
+      getCustomerClueInfoById({ customerClueInfoId: this.id }).then((response) => {
+        this.toastLoading().clear();
+        if (response.code == 200) {
+          this.infoData = response.data;
+          this.title = response.data.name;
+          if (response.data.customerClue != null) {
+            this.collectionItemList = response.data.customerClue.customerClueItemList;
+          } else {
+            this.collectionItemList = [];
+          }
+          //  获取转交下属数据 未跟进状态下
+          if (
+            this.infoData.isClose != 1 &&
+            this.postName &&
+            this.postName == '区域家装销售负责人'
+          ) {
+            this.getSelectSubUserList();
+          }
+          if (this.infoData.isClose != 1) {
+            this.getSelectCustomerClueInfoById();
+          }
+          // 当前客资归属人不是当前登录人
+          if (this.infoData.sendUserId != localStorage.getItem('userId')) {
+            this.$dialog
+              .confirm({
+                title: '提示',
+                message: '该客资已经被转移给其他人',
+                showCancelButton: false,
+              })
+              .then(() => {
+                window.location.replace(window.location.origin + '/mobile/clew');
+              });
+          }
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    getSelectCustomerClueInfoById() {
+      selectCustomerClueInfoById({ customerClueInfoId: this.id }).then((res) => {
+        if (res.code == 200) {
+          // res.data.customerClue.searchValue.customerClueItemList
+          if (res.data.customerClue) {
+            res.data.customerClue.customerClueItemList[0].customerClueInfoId = this.id;
+            this.taskGather = res.data.customerClue.customerClueItemList;
+          }
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    getSelectSubUserList() {
+      selectSubUserList().then((res) => {
+        if (res.code == 200) {
+          this.assignTypeData = res.data ? res.data : [];
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    confirm(value) {
+      this.$dialog
+        .confirm({
+          title: '提示',
+          message: '一旦转交出去,则无法再查看,请确认!',
+        })
+        .then(() => {
+          this.toastLoading(0, '加载中...', true);
+          allocationSubCustomer({
+            userId: value.userId, //	string	用户ID
+            customerClueInfoId: this.id, //	string	客资主键ID
+          }).then((res) => {
+            this.toastLoading().clear();
+            if (res.code == 200) {
+              this.$toast(res.msg);
+              window.location.replace(window.location.origin + '/mobile/clew');
+            } else {
+              this.$toast(res.msg);
+            }
+          });
+        });
+    },
+    // 查看历史跟进记录
+    viewFn(val) {
+      //   this.viewTextShow = false;
+      this.historyId = val;
+      this.showView = true;
+    },
+    onSubmit() {
+      // 没有选择跟进记录
+      if (!this.taskGather[0].searchValue) {
+        this.$toast('请选择跟进结果');
+        return;
+      }
+      this.requiredFlag = true;
+      let customerClueItemList = [];
+      // 每一个层级都是一道题的题目,子级就是题,被选中和填写的题要带上题目一块上传(题的同级也要上传)
+      // 第一级题目下的题默认都要上传
+      let params = { customerClueItemList: [] };
+      params.customerClueItemList.push(...this.deepClone(this.taskGather, 0));
+      let optionList = this.taskGather[0].customerClueOptionList;
+      this.filterOption(optionList, params);
+      // 必填验证
+      if (this.requiredFlag) {
+        this.toastLoading(0, '加载中...', true);
+        insertFollowCustomerClueAnswer(params).then((res) => {
+          this.toastLoading().clear();
+          if (res.code == 200) {
+            this.$toast(res.msg);
+            window.location.replace(window.location.origin + '/mobile/clew');
+          } else {
+            this.$toast(res.msg);
+          }
+        });
+      } else {
+        this.$toast(this.requiredMessage);
+      }
+    },
+    filterOption(optionList, params) {
+      let copy = null;
+      for (let i = 0; i < optionList.length; i++) {
+        if (optionList[i].value == 'Y') {
+          if (optionList[i].customerClueItemList) {
+            // 必填校验
+            this.isRequiredFlag(optionList[i].customerClueItemList);
+            params.customerClueItemList.push(
+              ...this.deepClone(optionList[i].customerClueItemList, 0)
+            );
+            if (
+              optionList[i].customerClueItemList[0] &&
+              optionList[i].customerClueItemList[0].customerClueOptionList.length
+            ) {
+              this.filterOption(
+                optionList[i].customerClueItemList[0].customerClueOptionList,
+                params
+              );
+            }
+          }
+        }
+      }
+    },
+    deepClone(obj, num) {
+      // 检查是否为对象或数组
+      if (obj === null || typeof obj !== 'object') {
+        return obj; // 基本类型直接返回
+      }
+      // 创建一个数组或对象
+      const copy = Array.isArray(obj) ? [] : {};
+      // 遍历对象的每个属性
+      for (const key in obj) {
+        if (obj.hasOwnProperty(key) && num < 2) {
+          // 递归调用深拷贝
+          if (key == 'customerClueOptionList' || key == 'customerClueItemList') {
+            num = num + 1;
+          }
+          copy[key] = this.deepClone(obj[key], num);
+        }
+      }
+      return copy;
+    },
+    isRequiredFlag(optionList) {
+      console.log(optionList);
+      // 必填类型
+      for (let i = 0; i < optionList.length; i++) {
+        if (
+          (optionList[i].answerType == 'wb' || optionList[i].answerType == 'sz') &&
+          optionList[i].isMust == 0
+        ) {
+          if (!optionList[i].answerValue) {
+            this.requiredFlag = false;
+            this.requiredMessage = optionList[i].remark;
+            return;
+          }
+        }
+      }
+    },
+    // 校验错误返回信息
+    onFailed(errorInfo) {
+      console.log('failed', errorInfo);
+    },
+    onClickLeft() {
+      this.$router.go(-1);
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.assignPage {
+  .assign {
+    margin: 10px;
+    background-color: #fff;
+    padding-bottom: 20px;
+    .followUp {
+      padding: 16px;
+      font-size: 14px;
+      font-weight: 600;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.assignPage {
+  .van-field__label {
+    width: 100px;
+    &::before {
+      content: '*';
+      color: red;
+    }
+  }
+  .centerBtn {
+    float: right;
+    background: #0057ba;
+    border-color: #0057ba;
+    color: #fff;
+    margin-top: -33px;
+    border-radius: 5px;
+  }
+  .dialogz {
+    width: 100%;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    height: 72vh;
+    .van-dialog__content {
+      flex: 1;
+      overflow-y: auto;
+    }
+  }
+}
+</style>

+ 107 - 0
src/views/week/assignAwait/assignAwaitDetail.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="cardclewContent assignAwaitDetail">
+    <div class="info">
+      客资线索:
+      <!-- 待分配页面和跟进页面展示不一样 -->
+      <template v-if="source == 'assignPage'">
+        <span>{{ infoData.jzCustomerDescription }}</span>
+      </template>
+      <template v-else>
+        <span v-if="infoData.cid == 3">申请代理</span>
+        <span v-if="infoData.cid == 4">开设门店</span>
+        <span v-if="infoData.cid == 5">批量采购</span>
+        <span v-if="infoData.cid == 7">家装客资</span>
+      </template>
+    </div>
+    <!-- 跟进页面增加客资类型 -->
+    <div class="info" v-if="source == 'JZfollowUp'">
+      客资类型:
+      <span v-if="customerClassify.length && infoData.customerClassify">{{
+        filterClassify(infoData.customerClassify)
+      }}</span>
+    </div>
+    <div class="info">公司名称:{{ infoData.clueCompanyName }}</div>
+    <div class="info">角色:{{ infoData.capacity }}</div>
+    <div class="info">区域:{{ infoData.companyName }}</div>
+    <div class="info">省:{{ infoData.provinceName }}</div>
+    <div class="info">市:{{ infoData.cityName }}</div>
+    <div class="info">区/县:{{ infoData.countryName }}</div>
+    <div class="info">姓名:{{ infoData.name }}</div>
+    <div class="info">
+      来电电话:<a
+        style="color: #0057ba; font-weight: bold; text-decoration: underline"
+        :href="'tel:' + infoData.phone"
+        >{{ infoData.phone }}<van-icon name="phone"
+      /></a>
+    </div>
+    <div class="info" v-if="infoData.createTime">首次接入时间:{{ infoData.createTime }}</div>
+    <slot></slot>
+  </div>
+</template>
+
+<script>
+import { getDictOption } from '@/api/index';
+export default {
+  name: 'assignAwaitDetail',
+  props: {
+    infoData: {
+      type: Object,
+      default: {},
+    },
+    source: {
+      // assignPage 拥有家装分配权限
+      type: String,
+    },
+  },
+  data() {
+    return {
+      customerClassify: [],
+    };
+  },
+  watch: {
+    source: {
+      handler(val) {
+        if (val == 'JZfollowUp') {
+          this.getCustomerClassify();
+        }
+      },
+      immediate: true,
+    },
+  },
+  created() {},
+  methods: {
+    async getCustomerClassify() {
+      let option = await getDictOption({}, 'customer_classify');
+      this.customerClassify = option.data || [];
+    },
+    filterClassify(customerClassify) {
+      let item = this.customerClassify.find((val) => val.dictValue == customerClassify);
+      return item.dictLabel || '';
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.cardclewContentCell {
+  margin: 0 10px;
+}
+.cardclewContent {
+  background: #fff;
+  box-sizing: border-box;
+  padding: 10px 16px;
+  margin: 6px 10px 10px;
+}
+.cardclewContent .info {
+  font-size: 14px;
+  color: #444;
+  line-height: 28px;
+}
+.cardclewContent .title p {
+  padding: 0;
+  margin: 0;
+}
+.cardclewContent .title .textLeft {
+  display: inline-block;
+  padding-bottom: 10px;
+}
+</style>

+ 289 - 0
src/views/week/assignAwait/assignPage.vue

@@ -0,0 +1,289 @@
+<template>
+  <div class="bgcolor assignPage">
+    <div class="navBarTOP">
+      <van-nav-bar class="navBar" left-arrow :title="title" @click-left="onClickLeft" />
+    </div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <div class="lineGrey"></div>
+    <!-- 客资详情 -->
+    <assignAwaitDetail :infoData="infoData" :source="'assignPage'"></assignAwaitDetail>
+    <!-- 选择分配组织 -->
+    <div class="assign">
+      <van-form validate-first @failed="onFailed" @submit="onSubmit" :show-error="false">
+        <van-field
+          readonly
+          clickable
+          name="picker"
+          :value="assignTypeValue"
+          label="客资类型"
+          placeholder="点击选择客资类型"
+          right-icon="arrow"
+          :rules="[{ required: true, message: '请选择客资类型' }]"
+          @click="showPicker = true" />
+        <van-field
+          v-if="activaAssignTypeItem && activaAssignTypeItem.dictValue != 1"
+          readonly
+          clickable
+          name="picker"
+          :value="assignUserPostValue"
+          label="分配人员岗位"
+          placeholder="点击选择分配人员岗位"
+          right-icon="arrow"
+          :rules="[{ required: true, message: '请选择分配人员岗位' }]"
+          @click="assignUserPostShow = true" />
+        <van-field
+          readonly
+          clickable
+          name="picker"
+          :value="assignUserValue"
+          label="分配人员"
+          placeholder="点击选择分配人员"
+          right-icon="arrow"
+          :rules="[{ required: true, message: '请选择分配人员' }]"
+          @click="assignUserShow = true" />
+        <div style="margin: 16px">
+          <van-button round block type="info" native-type="submit">提交</van-button>
+        </div>
+      </van-form>
+    </div>
+    <!-- 类型选择 -->
+    <van-popup v-model="showPicker" position="bottom">
+      <van-picker
+        show-toolbar
+        value-key="dictLabel"
+        :columns="assignTypeData"
+        @confirm="onConfirm"
+        @cancel="showPicker = false" />
+    </van-popup>
+    <!-- 分配人员岗位 -->
+    <van-popup v-model="assignUserPostShow" position="bottom">
+      <van-picker
+        show-toolbar
+        value-key="postName"
+        :columns="assignUserPostData"
+        @confirm="onConfirmAssignUserPost"
+        @cancel="assignUserPost = false" />
+    </van-popup>
+    <!-- 分配人员选择 -->
+    <van-popup v-model="assignUserShow" position="bottom">
+      <van-picker
+        show-toolbar
+        value-key="nickName"
+        :columns="assignUserData"
+        @confirm="onConfirmAssignUser"
+        @cancel="assignUserShow = false" />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import { getCustomerClueInfoById } from '@/api/clew';
+import { getDictOption } from '@/api/index';
+import assignAwaitDetail from './assignAwaitDetail.vue';
+import {
+  selectUserListByCustomerType,
+  selectUserListByPostName,
+  allocationCustomer,
+} from '@/api/assignAwait';
+
+export default {
+  name: 'assignPage',
+  components: {
+    assignAwaitDetail,
+  },
+  data() {
+    return {
+      id: '',
+      infoData: {},
+      collectionItemList: [],
+      showPicker: false,
+      assignUserPostShow: false,
+      assignUserShow: false,
+      columns: [],
+      valueKey: '',
+      assignTypeData: [], //客资类型下拉选数据
+      activaAssignTypeItem: null, //客资类型当前选择的数据
+      assignTypeValue: '', //客资类型选中value
+      assignUserPostData: [], //分配人员岗位下拉选数据
+      assignUserPostItem: null, //分配人员岗位当前选择的数据
+      assignUserPostValue: '', //分配人员岗位选中value
+      assignUserData: [], //分配人员下拉选数据
+      assignUserItem: null, //分配人员当前选择的数据
+      assignUserValue: '', //分配人员选中value
+      title: '',
+    };
+  },
+  watch: {
+    'activaAssignTypeItem.dictValue': {
+      handler(val) {
+        if (val) this.getSelectUserListByCustomerType();
+      },
+    },
+    assignUserPostValue: {
+      handler(val) {
+        if (val) this.getSelectUserListByPostName();
+      },
+    },
+  },
+  activated() {
+    this.id = this.$route.query.id;
+    this.getDictOptionFun();
+    this.getCustomerClueInfoById();
+  },
+  methods: {
+    async getDictOptionFun() {
+      // 获取字典(jz_customer_type)参数对应的备注值,如果选择大型装企直接返回用户表列表
+      let customer = await getDictOption({}, 'jz_customer_type');
+      if (customer.data && customer.data.length) {
+        this.assignTypeData = customer.data;
+        this.activaAssignTypeItem = this.assignTypeData[0];
+        this.assignTypeValue = this.activaAssignTypeItem.dictLabel;
+      } else {
+        this.assignTypeData = [];
+        this.activaAssignTypeItem = null;
+        this.assignTypeValue = '';
+      }
+    },
+    getSelectUserListByCustomerType() {
+      let params = JSON.parse(this.activaAssignTypeItem.remark);
+      selectUserListByCustomerType(params).then((res) => {
+        if (res.code == 200) {
+          // 如果选择大型装企直接返回用户表列表
+          if (this.activaAssignTypeItem.dictValue == 1) {
+            if (res.data && res.data.userList.length) {
+              this.assignUserData = res.data.userList;
+              this.assignUserItem = this.assignUserData[0];
+              this.assignUserValue = this.assignUserItem.nickName;
+            } else {
+              this.assignUserData = [];
+              this.assignUserItem = null;
+              this.assignUserValue = '';
+            }
+          } else {
+            if (res.data && res.data.postList.length) {
+              let resData = [];
+              res.data.postList.forEach((val) => {
+                resData.push({
+                  postName: val,
+                });
+              });
+              this.assignUserPostData = resData;
+              this.assignUserPostItem = this.assignUserPostData[0];
+              this.assignUserPostValue = this.assignUserPostItem.postName;
+            } else {
+              this.assignUserPostData = [];
+              this.assignUserPostItem = null;
+              this.assignUserPostValue = '';
+            }
+          }
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    getSelectUserListByPostName() {
+      selectUserListByPostName({ postName: this.assignUserPostValue }).then((res) => {
+        if (res.code == 200) {
+          if (res.data && res.data.userList.length) {
+            this.assignUserData = res.data.userList;
+            this.assignUserItem = this.assignUserData[0];
+            this.assignUserValue = this.assignUserItem.nickName;
+          } else {
+            this.assignUserData = [];
+            this.assignUserItem = null;
+            this.assignUserValue = '';
+          }
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    getCustomerClueInfoById() {
+      this.id = this.$route.query.id;
+      this.collectionAnswerlisd = [];
+      this.collectionItemList = [];
+      getCustomerClueInfoById({ customerClueInfoId: this.id }).then((response) => {
+        if (response.code == 200) {
+          this.infoData = response.data;
+          this.title = response.data.name;
+          if (response.data.customerClue != null) {
+            this.collectionItemList = response.data.customerClue.customerClueItemList;
+          } else {
+            this.collectionItemList = [];
+          }
+          // 当前客资是否已经被分配
+          if (this.infoData.allocationStatus == 1) {
+            this.$dialog
+              .confirm({
+                title: '提示',
+                message: '该客资已经被分配',
+                showCancelButton: false,
+              })
+              .then(() => {
+                window.location.replace(window.location.origin + '/mobile/assignAwait');
+              });
+          }
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    onSubmit() {
+      this.toastLoading(0, '加载中...', true);
+      allocationCustomer({
+        userId: this.assignUserItem.userId || '', //	string	用户ID
+        customerClueInfoId: this.id, //	string	客资主键ID
+        customerClassify: this.activaAssignTypeItem.dictValue || '', //	string	客资类型,(取值页面字典选择的值 jz_customer_type)
+      }).then((res) => {
+        this.toastLoading().clear();
+        if (res.code == 200) {
+          this.$toast(res.msg);
+          window.location.replace(window.location.origin + '/mobile/assignAwait');
+        } else {
+          this.$toast(res.msg);
+        }
+      });
+    },
+    // 校验错误返回信息
+    onFailed(errorInfo) {
+      console.log('failed', errorInfo);
+    },
+    onConfirm(value) {
+      this.activaAssignTypeItem = value;
+      this.assignTypeValue = this.activaAssignTypeItem.dictLabel;
+      this.showPicker = false;
+    },
+    onConfirmAssignUserPost(value) {
+      this.assignUserPostItem = value;
+      this.assignUserPostValue = this.assignUserPostItem.postName;
+      this.assignUserPostShow = false;
+    },
+    onConfirmAssignUser(value) {
+      this.assignUserItem = value;
+      this.assignUserValue = this.assignUserItem.nickName;
+      this.assignUserShow = false;
+    },
+    onClickLeft() {
+      this.$router.go(-1);
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.assignPage {
+}
+</style>
+<style lang="scss">
+.assignPage {
+  .van-field__label {
+    width: 100px;
+    &::before {
+      content: '*';
+      color: red;
+    }
+  }
+}
+</style>

+ 246 - 0
src/views/week/assignAwait/followUpHistory.vue

@@ -0,0 +1,246 @@
+<template>
+  <div
+    class="container linep containertext followUpHistory"
+    style="
+      background-color: #fff;
+      width: 94%;
+      margin: 0px auto;
+      border-radius: 6px;
+      font-size: 14px;
+    ">
+    <van-form ref="tabstoreVal">
+      <div v-for="(item, index) in customItemList" :key="index">
+        <div
+          class="formLabel z-cell z-cells z-celly"
+          v-if="item.answerType == 'sz' || item.answerType == 'rq'">
+          <van-cell>
+            <template #title
+              >{{ index + 1 }}.{{ item.customerClueName }}
+              <span
+                style="color: #444; font-size: 12px; margin: 0; padding: 10px 0; text-align: right"
+                >{{ item.remark }}</span
+              ></template
+            >
+          </van-cell>
+          <p class="mg0" style="word-break: break-all">{{ item.answerValue }}</p>
+        </div>
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'zp'">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+        <div
+          class="formLabel z-cell z-cells z-celly"
+          v-if="item.answerType == 'wb' && viewTextShow">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <div class="selesetText">
+            <p class="mg0" style="word-break: break-all">{{ item.answerValue }}</p>
+          </div>
+          <!-- <p style="color: #444; font-size: 12px; margin: 0; padding: 10px 0; text-align: right">
+            {{ item.remark }}
+          </p> -->
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'duox'">
+          <van-cell>
+            <template #title>{{ index + 1 }}.{{ item.customerClueName }}</template>
+          </van-cell>
+          <div class="selesetText">
+            <div class="mg0" v-for="(item1, index1) in item.customerClueOptionList" :key="index1">
+              <p style="color: #0057ba" v-if="item1.checked">
+                <i
+                  style="
+                    border: 1px solid #0057ba;
+                    margin-right: 10px;
+                    border-radius: 2px;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item1.customerClueOption }}</span>
+              </p>
+              <p v-if="!item1.checked">
+                <i
+                  style="
+                    border: 1px solid #ccc;
+                    margin-right: 10px;
+                    border-radius: 2px;
+                    color: #fff;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item1.customerClueOption }}</span>
+              </p>
+            </div>
+          </div>
+        </div>
+        <div class="formLabel z-cell z-cells z-celly" v-if="item.answerType == 'dx'">
+          <van-cell>
+            <template #title> {{ index + 1 }}.{{ item.customerClueName }} </template>
+          </van-cell>
+          <div class="selesetText">
+            <div class="mg0" v-for="(item2, index2) in item.customerClueOptionList" :key="index2">
+              <p style="color: #0057ba" v-if="item2.checked">
+                <i
+                  style="
+                    border: 1px solid #0057ba;
+                    border-radius: 40px;
+                    margin-right: 10px;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item2.customerClueOption }}</span>
+              </p>
+              <p v-if="!item2.checked">
+                <i
+                  style="
+                    border: 1px solid #ccc;
+                    border-radius: 40px;
+                    margin-right: 10px;
+                    color: #fff;
+                    padding: 2px;
+                  "
+                  class="van-icon van-icon-success"></i>
+                <span>{{ item2.customerClueOption }}</span>
+              </p>
+            </div>
+          </div>
+          <delete-upload-imgv :imgs="item.fileInfoList"></delete-upload-imgv>
+        </div>
+      </div>
+    </van-form>
+  </div>
+</template>
+<script>
+import deleteUploadImg from '@/components/deleteUploadImg2';
+import deleteUploadImgv from '@/components/deleteUploadImg';
+import { getCustomerClueAnswerById } from '@/api/clew';
+export default {
+  components: { deleteUploadImg, deleteUploadImgv },
+  props: {
+    historyId: {
+      type: [String, Number],
+      default: '',
+    },
+    showView: {
+      type: Boolean,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      customItemList: [],
+      viewTextShow: false,
+    };
+  },
+  watch: {
+    showView: {
+      handler(val) {
+        if (val) this.getFollowUpHistory();
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    getFollowUpHistory(val) {
+      this.toastLoading(0, '加载中...', true);
+      getCustomerClueAnswerById({ userCustomerClueId: this.historyId }).then((res) => {
+        this.toastLoading().clear();
+        this.customItemList = res.data.customerClue.customerClueItemList;
+        for (var pl = 0; pl < this.customItemList[1].customerClueOptionList.length; pl++) {
+          if (
+            this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+              '跟进中'
+            ) != -1
+          ) {
+            if (this.customItemList[1].customerClueOptionList[pl].value == 'Y') {
+              this.viewTextShow = true;
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '跟进'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName =
+                  '具体合作意向反馈(拜访时间、合同方向、预估合作时间)';
+              }
+            }
+          } else {
+            if (this.customItemList[1].customerClueOptionList[pl].value == 'Y') {
+              this.viewTextShow = true;
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '意向'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '没有意向原因';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '开户'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '开户经销商代码(例:0110067321)';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '开店'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '开户门店代码(例:0190129032)';
+                this.customItemList[2].remark = '请输入';
+              }
+              if (
+                this.customItemList[1].customerClueOptionList[pl].customerClueOption.indexOf(
+                  '出货'
+                ) != -1
+              ) {
+                this.customItemList[2].customerClueName = '填写具体的产品和数量';
+              }
+            }
+          }
+        }
+      });
+    },
+  },
+};
+</script>
+<style lang="scss">
+.followUpHistory {
+  .formLabel {
+    margin: 0 16px;
+    border-bottom: 1px solid #f1f1f1;
+  }
+  .formLabel .van-cell {
+    padding: 10px 0;
+    font-size: 14px;
+  }
+  .formLabel .van-cell::after {
+    border: 0;
+  }
+  .formLabeltext .van-field {
+    border: 1px solid #f1f1f1;
+    padding: 6px;
+    width: 100%;
+    border-radius: 4px;
+    overflow: hidden;
+  }
+  .formLabel .van-field__control {
+    padding: 0 10px;
+  }
+  .z-checkbox .van-radio {
+    padding: 6px 0;
+  }
+  .z-cell .van-cell__title {
+    font-size: 14px;
+  }
+  .formLabel .van-radio__label,
+  .formLabel .van-checkbox__label {
+    font-size: 14px;
+  }
+}
+</style>

+ 125 - 0
src/views/week/assignAwait/index.vue

@@ -0,0 +1,125 @@
+<template>
+  <div class="assignAwait">
+    <van-nav-bar class="navBar" title="待分配客资" left-arrow @click-left="onClickLeft" />
+    <div class="container">
+      <van-list v-model="loading" :finished="finished" finished-text="--已经到底了--">
+        <div class="newCarList" v-for="(item, index) in list" :key="index">
+          <van-cell is-link>
+            <div class="newlist" @click="approveFn(item)">
+              <div class="title">
+                <p class="textLeft">{{ item.name }}</p>
+              </div>
+              <div class="info">
+                客资线索:
+                <span v-if="item.cid == 7">家装客资</span>
+              </div>
+              <div class="info">联系电话:{{ item.phone }}</div>
+              <div class="info" v-if="item.createTime">首次接入时间:{{ item.createTime }}</div>
+              <div class="info" v-if="item.latestClueTime">
+                最后一次跟进时间:{{ item.latestClueTime }}
+              </div>
+            </div>
+          </van-cell>
+        </div>
+        <van-empty description="" v-if="list.length == 0" />
+      </van-list>
+    </div>
+  </div>
+</template>
+
+<script>
+import { selectNotAllocationList } from '@/api/assignAwait';
+export default {
+  name: 'assignAwait',
+  data() {
+    return {
+      disabled: false,
+      loading: false,
+      list: [],
+      finished: true,
+    };
+  },
+  activated() {
+    this.approveList();
+  },
+  methods: {
+    approveFn(row) {
+      this.list = [];
+      this.$router.push({
+        path: '/assignPage',
+        query: { id: row.customerClueInfoId },
+      });
+    },
+    approveList() {
+      let loading1 = this.$toast.loading({
+        duration: 0,
+        message: '数据加载中...',
+        forbidClick: true,
+      });
+      selectNotAllocationList().then((res) => {
+        loading1.clear();
+        this.disabled = false;
+        this.loading = false;
+        this.list = res.data;
+      });
+    },
+    onClickLeft() {
+      this.$router.push('/My/index');
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.assignAwait {
+  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  .header {
+    position: sticky;
+    top: 0px;
+    z-index: 10;
+  }
+  .newCarList {
+    margin: 14px;
+    border-radius: 8px;
+    overflow: hidden;
+  }
+  .newCarList .van-cell {
+    border-radius: 6px;
+    overflow: hidden;
+  }
+  .newCarList .van-cell__right-icon {
+    top: -4px;
+  }
+  .newCarList .newlist {
+    box-sizing: border-box;
+  }
+  .newCarList .newlist .title {
+    font-size: 14px;
+    font-weight: bold;
+    color: #333;
+    /* line-height: 14px; */
+  }
+  .newCarList .newlist .info {
+    font-size: 14px;
+    color: #999;
+    line-height: 26px;
+  }
+  .newCarList .newlist .title p {
+    padding: 0;
+    margin: 0;
+  }
+  .newCarList .newlist .title .textLeft {
+    display: inline-block;
+    /* padding-bottom: 10px; */
+  }
+  .newCarList .newlist .title .textRight {
+    float: right;
+    color: #0057ba;
+  }
+  .van-tab--active {
+    color: #0057ba;
+  }
+}
+</style>

+ 115 - 0
src/views/week/assignAwait/radioGroup copy.vue

@@ -0,0 +1,115 @@
+<template>
+  <div class="radioGroup">
+    <template v-if="clueOptionList && clueOptionList.length && clueOptionList[0]">
+      <template v-if="clueOptionList[0].answerType == 'dx'">
+        <van-radio-group v-model="clueOptionList[0].searchValue" @change="radioGroupChange">
+          <template v-for="(item, index) in clueOptionList[0].customerClueOptionList">
+            <van-radio :name="item.customerClueOptionId" :key="index" @click="radioClick">
+              {{ item.customerClueOption }}
+            </van-radio>
+            <radioGroup
+              :clueOptionList="item.customerClueItemList"
+              :parentOptionList="item"></radioGroup>
+          </template>
+        </van-radio-group>
+      </template>
+      <template v-if="clueOptionList[0].answerType == 'wb'">
+        {{ clueOptionList[0].customerClueName }}
+        <van-field
+          v-model="clueOptionList[0].searchValue"
+          :placeholder="clueOptionList[0].customerClueName" />
+      </template>
+    </template>
+  </div>
+</template>
+<script>
+export default {
+  name: 'radioGroup',
+  props: {
+    clueOptionList: {
+      type: Array,
+      default: () => [],
+    },
+    parentOptionList: {
+      type: Object,
+      default: () => {},
+    },
+  },
+  data() {
+    return {};
+  },
+  methods: {
+    getParentId(list, name) {
+      debugger;
+      for (let i in list) {
+        if (list[i].customerClueOptionId == name) {
+          return [list[i]];
+        }
+        if (list[i].customerClueItemList) {
+          let node = this.getParentId(list[i].customerClueOptionList, name);
+          if (node !== undefined) {
+            return node.concat(list[i]);
+          }
+        }
+      }
+    },
+    radioGroupChange(name) {
+      if (!name) return;
+      console.log(name);
+      // console.log(this.clueOptionList);
+      // 如果选中的数据有父级,将父级数据修改为选中状态
+      if (this.parentOptionList) {
+        // console.log(this.parentOptionList);
+        // this.$set(this.parentOptionList, 'searchValue', 'Y');
+        // console.log(this.getParentId(this.parentOptionList.customerClueOptionList, name));
+        // 获取选中数据
+        // let activaRadio = this.parentOptionList.customerClueOptionList.find(
+        //   (val) => val.customerClueOptionId == name
+        // );
+      } else {
+      }
+      // 获取选中数据
+      let activaRadio = this.clueOptionList[0].customerClueOptionList.find(
+        (val) => val.customerClueOptionId == name
+      );
+      console.log(this.parentOptionList);
+      // 修改选中状态
+      // console.log(activaRadio);
+      this.$set(activaRadio, 'value', 'Y');
+
+      // 过滤未选中的数据
+      let exceptItself = this.clueOptionList[0].customerClueOptionList.filter(
+        (val) => val.customerClueOptionId !== name
+      );
+      // 删除未选中数据状态兄弟级和子级
+      // console.log(exceptItself);
+      this.toggleOtheChildren(exceptItself);
+    },
+    toggleOtheChildren(exceptItself) {
+      if (!exceptItself) return;
+      exceptItself.forEach((val) => {
+        this.$set(val, 'value', 'N');
+        if (val.customerClueItemList && val.customerClueItemList[0]) {
+          this.$set(val.customerClueItemList[0], 'searchValue', null);
+          if (
+            val.customerClueItemList[0] &&
+            val.customerClueItemList[0].customerClueOptionList.length
+          ) {
+            this.toggleOtheChildren(val.customerClueItemList[0].customerClueOptionList);
+          }
+        }
+      });
+    },
+    radioClick(event) {
+      // console.log(event);
+    },
+  },
+};
+</script>
+<style lang="scss">
+.radioGroup {
+  .van-radio-group {
+    padding-left: 20px;
+  }
+}
+</style>

+ 145 - 0
src/views/week/assignAwait/radioGroup.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="radioGroup">
+    <template v-if="clueOptionList && clueOptionList.length">
+      <template v-if="clueOptionList[0].answerType == 'dx'">
+        <van-radio-group v-model="clueOptionList[0].searchValue" @change="radioGroupChange">
+          <template v-for="(item, index) in clueOptionList[0].customerClueOptionList">
+            <van-radio :name="item.customerClueOptionId" :key="index" @click="radioClick">
+              {{ item.customerClueOption }}
+            </van-radio>
+            <radioGroup
+              :clueOptionList="item.customerClueItemList"
+              :parentOptionList="clueOptionList[0]"
+              :parentId="item.customerClueOptionId"
+              v-if="clueOptionList[0].searchValue == item.customerClueOptionId"></radioGroup>
+          </template>
+        </van-radio-group>
+      </template>
+      <template v-for="(item, index) in clueOptionList" v-if="clueOptionList.length">
+        <template
+          v-if="parentOptionList && parentOptionList.searchValue == item.itemOptionParentId">
+          <template v-if="item.answerType == 'wb' || item.answerType == 'sz'">
+            <span class="van-f-red" v-if="item.isMust == 0">*</span>
+            {{ item.customerClueName }}
+          </template>
+          <!-- 回答类型:wb-文本,sz-数字,rq-日期,zp-照片,dx -->
+          <template v-if="item.answerType == 'wb'">
+            <van-field v-model="item.answerValue" :placeholder="item.remark" />
+          </template>
+          <template v-if="item.answerType == 'sz'">
+            <van-field
+              v-model="item.answerValue"
+              :placeholder="item.remark"
+              type="number"></van-field>
+          </template>
+        </template>
+      </template>
+    </template>
+  </div>
+</template>
+<script>
+export default {
+  name: 'radioGroup',
+  props: {
+    clueOptionList: {
+      type: Array,
+      default: () => [],
+    },
+    parentOptionList: {
+      type: Object,
+      default: () => {},
+    },
+    parentId: {
+      type: [Number, null],
+      default: null,
+    },
+  },
+  data() {
+    return {};
+  },
+  created() {
+    // console.log(this.clueOptionList);
+  },
+  methods: {
+    radioGroupChange(name) {
+      if (!name) return;
+      // console.log(name);
+      // console.log(this.clueOptionList);
+      // 如果选中的数据有父级,将父级数据修改为选中状态
+      if (this.parentOptionList && this.parentId) {
+        this.$set(this.parentOptionList, 'searchValue', this.parentId);
+      }
+      // 获取选中数据
+      let activaRadio = this.clueOptionList[0].customerClueOptionList.find(
+        (val) => val.customerClueOptionId == name
+      );
+      // 修改选中状态
+      this.$set(activaRadio, 'value', 'Y');
+
+      // 过滤未选中的数据
+      let exceptItself = this.clueOptionList[0].customerClueOptionList.filter(
+        (val) => val.customerClueOptionId !== name
+      );
+      // 删除未选中数据状态兄弟级和子级
+      // console.log(exceptItself);
+      this.toggleOtheChildren(exceptItself);
+    },
+    toggleOtheChildren(exceptItself) {
+      if (!exceptItself) return;
+      exceptItself.forEach((val) => {
+        this.$set(val, 'value', 'N');
+        if (val.customerClueItemList && val.customerClueItemList[0]) {
+          this.$set(val.customerClueItemList[0], 'searchValue', null);
+          if (
+            val.customerClueItemList[0] &&
+            val.customerClueItemList[0].customerClueOptionList.length
+          ) {
+            this.toggleOtheChildren(val.customerClueItemList[0].customerClueOptionList);
+          }
+        }
+      });
+    },
+    radioClick(event) {
+      // console.log(event);
+    },
+  },
+};
+</script>
+<style lang="scss">
+.radioGroup {
+  font-size: 15px;
+  .van-radio {
+    padding: 5px 0;
+  }
+  .van-radio-group {
+    padding-left: 20px;
+  }
+  .van-cell {
+    padding: 10px 0;
+    /* font-size: 14px; */
+  }
+  .van-cell::after {
+    border: 0;
+  }
+  .van-field {
+    border: 1px solid #cdc8c8;
+    padding: 0;
+    width: 100%;
+    border-radius: 4px;
+    overflow: hidden;
+    background-color: unset;
+    height: 30px;
+    line-height: 30px;
+    margin: 5px 0;
+  }
+  .van-field__control {
+    padding: 0 10px;
+  }
+  .van-field__control {
+    padding: 0 10px;
+  }
+  .van-radio__label {
+    /* font-size: 14px; */
+  }
+}
+</style>

+ 31 - 2
src/views/week/index.vue

@@ -49,8 +49,8 @@
         </van-cell>
       </van-cell-group>
       <!--      客资类-->
-      <van-cell-group inset class="mtb10" v-if="customerClueButton">
-        <van-cell title="客资任务" to="/clew">
+      <van-cell-group inset class="mtb10">
+        <van-cell title="客资任务" to="/clew" v-if="customerClueButton">
           <template #icon>
             <van-icon :name="history" class="zicon" />
           </template>
@@ -62,6 +62,20 @@
             <van-icon name="arrow" size="16" />
           </template>
         </van-cell>
+        <van-cell title="待分配客资" to="/assignAwait" v-if="isAssignFlag">
+          <template #icon>
+            <van-icon :name="history" class="zicon" />
+          </template>
+          <template #title>
+            <span>待分配客资</span>
+            &nbsp;<van-tag type="danger" v-if="notAllocationNum > 0">{{
+              notAllocationNum
+            }}</van-tag>
+          </template>
+          <template #right-icon>
+            <van-icon name="arrow" size="16" />
+          </template>
+        </van-cell>
       </van-cell-group>
       <!--      汇报类-->
       <van-cell-group inset class="mtb10">
@@ -177,6 +191,7 @@ import Weekly from '@/assets/Weekly.png';
 import storeselect from '@/assets/Icon/storeselect.png';
 import tabBar from '@/components/tabBar';
 import { getReportInfo, getstoreCoverPosition } from '@/api/index';
+import { selectAllocationPermission } from '@/api/week';
 export default {
   name: 'MyList',
   components: { tabBar },
@@ -211,6 +226,8 @@ export default {
       customerClueButton: false,
       customerClueNum: 0,
       thisWeekRemarkNum: null,
+      notAllocationNum: 0, //待分配客资数量
+      isAssignFlag: false,
     };
   },
   watch: {
@@ -276,6 +293,8 @@ export default {
         message: '加载中...',
         forbidClick: true,
       });
+      // 如果有客资分配权限,获取待分配客资数量
+      this.getSelectAllocationPermission();
       getReportInfo({ isContent: false }).then((res) => {
         loading1.clear();
         this.list = res.data.summaryTasks;
@@ -314,6 +333,16 @@ export default {
         }
       });
     },
+    getSelectAllocationPermission() {
+      selectAllocationPermission()
+        .then((res) => {
+          this.isAssignFlag = res.data.isAllocationPermission;
+          this.notAllocationNum = res.data.notAllocationNum;
+        })
+        .catch((error) => {
+          console.log(error);
+        });
+    },
     login() {
       if (this.num > 10) {
         this.$router.push('/login');