Browse Source

海外客户列表
eta试用ing

Karsa 11 months ago
parent
commit
5e20f3ea61

+ 2 - 1
package.json

@@ -15,12 +15,13 @@
     "axios": "^1.6.7",
     "crypto-js": "^4.2.0",
     "element-plus": "2.4.4",
+    "highcharts": "11.2.0",
     "jquery": "^3.7.1",
     "js-md5": "^0.8.3",
-    "highcharts": "11.2.0",
     "moment": "^2.30.1",
     "pinia": "^2.1.7",
     "vue": "^3.4.19",
+    "vue-datepicker-next": "^1.0.3",
     "vue-router": "^4.3.0",
     "vue3-tree-org": "^4.2.2"
   },

+ 16 - 0
pnpm-lock.yaml

@@ -31,6 +31,9 @@ dependencies:
   vue:
     specifier: ^3.4.19
     version: 3.4.20
+  vue-datepicker-next:
+    specifier: ^1.0.3
+    version: 1.0.3(vue@3.4.20)
   vue-router:
     specifier: ^4.3.0
     version: 4.3.0(vue@3.4.20)
@@ -777,6 +780,10 @@ packages:
   /csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
+  /date-format-parse@0.2.7:
+    resolution: {integrity: sha512-/+lyMUKoRogMuTeOVii6lUwjbVlesN9YRYLzZT/g3TEZ3uD9QnpjResujeEqUW+OSNbT7T1+SYdyEkTcRv+KDQ==}
+    dev: false
+
   /dayjs@1.11.10:
     resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
     dev: false
@@ -1202,6 +1209,15 @@ packages:
       fsevents: 2.3.3
     dev: true
 
+  /vue-datepicker-next@1.0.3(vue@3.4.20):
+    resolution: {integrity: sha512-Brqjh896BJGVxP7d6tGDsPMu0SDAB8hAdtG7zWF8VIHJB21dk1VB9KgdajD9Y9uXbg+wHN0vmL7sbMPIyehQVQ==}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      date-format-parse: 0.2.7
+      vue: 3.4.20
+    dev: false
+
   /vue-demi@0.14.7(vue@3.4.20):
     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
     engines: {node: '>=12'}

+ 19 - 0
src/api/modules/overseasCustom.js

@@ -94,6 +94,25 @@ export const overseasCustomInterence = {
      */
     overseasCustomInterence:(params)=>{
         return http.get('/overseas_custom/custom/label/statistics',params)
+    },
+    
+    /**
+     * 海外客户路演 销售 研究列表
+     * @param {*} params AdminType: researcher seller
+     * @returns 
+     */
+    getOverseasRoadShowUsers: params => {
+        return http.get('/roadshow/overseas_custom/sys_user/list',params)
+    },
+
+    /**
+     * 海外客户路演列表
+     * @param {*} params 
+     * ResearcherId SellerId  StartDate EndDate  CompanyStatus
+     * @returns 
+     */
+    getOverseasRoadShowList: params => {
+        return http.get('/roadshow/overseas_custom/calendar/list',params)
     }
 
 }

+ 5 - 1
src/main.js

@@ -13,13 +13,17 @@ import '@/styles/global.scss'
 import vue3TreeOrg from 'vue3-tree-org';
 import "vue3-tree-org/lib/vue3-tree-org.css";
 
+
+import 'vue-datepicker-next/index.css';
+import 'vue-datepicker-next/locale/zh-cn';
+
 function setupApp() {
   const app = createApp(App)
   initStore(app)
 
   initRouter(app)
 
-  app.use(ElementPlus,{locale: zhCn})
+  app.use(ElementPlus,{locale: zhCn,size: 'large'})
   app.use(vue3TreeOrg)
   app.mount('#app')
 }

+ 85 - 73
src/router/modules/customRoutes.js

@@ -59,12 +59,15 @@ export default [
       //   name: "英文客户列表",
       //   hidden: false,
       // },
-      // {
-      //   path: "overseasCustomList",
-      //   component: () => import("@/views/custom_manage/overseasList/overseasCustomList.vue"),
-      //   name: "海外客户列表",
-      //   hidden: false,
-      // },
+      {
+        path: "overseasCustomList",
+        component: () => import("@/views/custom_manage/overseas/overseasCustomList.vue"),
+        name: "overseasCustomList",
+        hidden: false,
+        meta: {
+          title: '海外客户列表'
+        }
+      },
       // {
       //   path: "trialContactListEn",
       //   component: () => import("@/views/custom_manage/customList/limitContactListEn.vue"),
@@ -309,67 +312,73 @@ export default [
       //   component: () => import("@/views/custom_manage/listMatch.vue"),
       //   hidden: false,
       // },
-      // {
-      //   path: "etaTrialList",
-      //   component: () => import("@/views/custom_manage/etaTrialList/etaTrialList.vue"),
-      //   name: "ETA试用",
-      //   hidden: false,
-      //   meta: {
-      //     keepAlive: false,
-      //   },
-      // },
-      // {
-      //   path: "etaApprovalList",
-      //   component: () => import("@/views/custom_manage/etaTrialList/etaTrialList.vue"),
-      //   name: "审批列表",
-      //   hidden: false,
-      //   meta: {
-      //     pathFrom: "etaTrialList",
-      //     pathName: "ETA试用",
-      //     keepAlive: false,
-      //   },
-      // },
-      // {
-      //   path: "etaAddApproval",
-      //   component: () => import("@/views/custom_manage/etaTrialList/addApproval.vue"),
-      //   name: "新增申请",
-      //   hidden: false,
-      //   meta: {
-      //     pathFrom: "etaTrialList",
-      //     pathName: "ETA试用",
-      //     keepAlive: false,
-      //   },
-      // },
-      // {
-      //   path: "questionnaireSurvey",
-      //   component: () => import("@/views/custom_manage/etaTrialList/questionnaireSurvey.vue"),
-      //   name: "问卷调研",
-      //   hidden: false,
-      //   meta: {
-      //     pathFrom: "etaTrialList",
-      //     pathName: "ETA试用",
-      //   },
-      // },
-      // {
-      //   path: "questionnaireOption",
-      //   component: () => import("@/views/custom_manage/etaTrialList/questionnaireOption.vue"),
-      //   name: "问卷配置",
-      //   hidden: false,
-      //   meta: {
-      //     pathFrom: "etaTrialList",
-      //     pathName: "ETA试用",
-      //   },
-      // },
-      // {
-      //   path: "textQuestionDetail",
-      //   component: () => import("@/views/custom_manage/etaTrialList/textQuestionDetail.vue"),
-      //   name: "统计详情",
-      //   hidden: false,
-      //   meta: {
-      //     pathFrom: "etaTrialList",
-      //     pathName: "ETA试用",
-      //   },
-      // },
+      {
+        path: "etaTrialList",
+        component: () => import("@/views/custom_manage/etaTrial/etaTrialList.vue"),
+        name: "etaTrialList",
+        hidden: false,
+        meta: {
+          keepAlive: false,
+          title: 'ETA试用'
+        },
+      },
+      {
+        path: "etaApprovalList",
+        component: () => import("@/views/custom_manage/etaTrial/etaTrialList.vue"),
+        name: "etaApprovalList",
+        hidden: false,
+        meta: {
+          title: '审批列表',
+          pathFrom: "etaTrialList",
+          pathName: "ETA试用",
+          keepAlive: false,
+        },
+      },
+      {
+        path: "etaAddApproval",
+        component: () => import("@/views/custom_manage/etaTrial/addApproval.vue"),
+        name: "etaAddApproval",
+        hidden: false,
+        meta: {
+          title: '新增申请',
+          pathFrom: "etaTrialList",
+          pathName: "ETA试用",
+          keepAlive: false,
+        },
+      },
+      {
+        path: "questionnaireSurvey",
+        component: () => import("@/views/custom_manage/etaTrial/questionnaireSurvey.vue"),
+        name: "questionnaireSurvey",
+        hidden: false,
+        meta: {
+          title: '问卷调研',
+          pathFrom: "etaTrialList",
+          pathName: "ETA试用",
+        },
+      },
+      {
+        path: "questionnaireOption",
+        component: () => import("@/views/custom_manage/etaTrial/questionnaireOption.vue"),
+        name: "questionnaireOption",
+        hidden: false,
+        meta: {
+          title: '问卷配置',
+          pathFrom: "etaTrialList",
+          pathName: "ETA试用",
+        },
+      },
+      {
+        path: "textQuestionDetail",
+        component: () => import("@/views/custom_manage/etaTrial/textQuestionDetail.vue"),
+        name: "textQuestionDetail",
+        hidden: false,
+        meta: {
+          title: '统计详情',
+          pathFrom: "etaTrialList",
+          pathName: "ETA试用",
+        },
+      },
       // {
       //   path: "customerContractStatistics",
       //   component: () => import("@/views/custom_manage/contractStatistics.vue"),
@@ -428,12 +437,15 @@ export default [
       //     pathName: "商家管理",
       //   }
       // },
-      // {
-      //   path:'overseasCustomRoadshow',
-      //   name:"海外客户路演",
-      //   component: () => import('@/views/custom_manage/overseasList/overseasCustomRoadshow.vue'),
-      //   hidden: false
-      // }
+      {
+        path:'overseasCustomRoadshow',
+        name:"overseasCustomRoadshow",
+        component: () => import('@/views/custom_manage/overseas/overseasCustomRoadshow.vue'),
+        hidden: false,
+        meta: {
+          title: '海外客户路演'
+        }
+      }
     ],
   },
 ];

+ 37 - 0
src/views/custom_manage/custom/customSearch.vue

@@ -757,6 +757,43 @@ function lookHandle(item) {
   })
   isLook.value = true;
 }
+
+/* 权益处理数据 */
+const indexArrEquity = ref([])//权益
+const spanArrEquity = ref([]);
+const posEquity = ref(0)
+function getSpanArr(data) {
+  data.map((v, i, s) => {
+    if (!v.PermissionTypeName) {
+      indexArrEquity.value.push(i);
+    }
+  });
+  for (var i = 0; i < data.length; i++) {
+    let element = data[i]
+    if (i === 0) {
+      spanArrEquity.value.push(1);
+      posEquity.value = 0;
+    } else {
+      // 判断当前元素与上一个元素是否相同(line为标记)
+      if (element.PermissionName === data[i - 1].PermissionName) {
+        if(element.IsMerge){
+          // 主客观都有,需要合并
+          spanArrEquity.value[posEquity.value] += 1;
+          spanArrEquity.value.push(0);
+        }else{
+          // 主客观都有 不需要合并,说明是服务期限或者状态不一样
+          spanArrEquity.value[posEquity.value] += 0.5;
+          spanArrEquity.value.push(0.5);
+        }
+      } else {
+        spanArrEquity.value.push(1);
+        posEquity.value = i;
+      }
+    }
+  }
+  // console.log(this.indexArrEquity,this.spanArrEquity);
+}
+
 /* 处理合并数组 */
 const rowSpanArr = ref([])
 const position = ref(0)

+ 276 - 0
src/views/custom_manage/etaTrial/addApproval.vue

@@ -0,0 +1,276 @@
+<template>
+  <div class="add-approval-wrap">
+   <!--  {{tableData}} -->
+    <el-table :data="tableData" :row-class-name="tableRowClassName" v-loading="pageLoading" border>
+      <!-- 序号 -->
+      <el-table-column
+        type="index"
+        width="50"
+        label="序号"
+        align="center"
+      />
+      <!-- 姓名 -->
+      <el-table-column label="姓名" min-width="100">
+        <template #default="{row}">
+          <el-input v-model="row.UserName" :disabled="row.isApply" placeholder="姓名"></el-input>
+        </template>
+      </el-table-column>
+      <!-- 公司名称 -->
+      <el-table-column label="公司名称" min-width="150">
+        <template #default="{row}">
+          <el-input v-model="row.CompanyName" :disabled="row.isApply" placeholder="公司名称"></el-input>
+        </template>
+      </el-table-column>
+      <!-- 职位 -->
+      <el-table-column label="职位" min-width="100">
+        <template #default="{row}">
+          <el-input v-model="row.Position" :disabled="row.isApply" placeholder="职位"></el-input>
+        </template>
+      </el-table-column>
+      <!-- 手机号码 -->
+      <el-table-column label="手机号码" min-width="120">
+        <template #default="{row}">
+          <el-input v-model.trim="row.Mobile" :disabled="row.isApply" placeholder="手机号码"></el-input>
+        </template>
+      </el-table-column>
+      <!-- 操作 -->
+      <el-table-column label="操作" align="center">
+        <template #default="{row,$index}">
+          <el-button type="text" size="small" @click="addTableData">添加</el-button>
+          <el-button type="text" size="small" class="color-hint" 
+            @click="deleteTableData($index)"
+            v-if="(tableData.length>1&&!row.isApply)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="btn-wrap">
+      <el-button type="primary" @click="handleApprove">提交申请</el-button>
+      <el-button @click="cancelApprove">取 消</el-button>
+    </div>
+    <!-- 提示弹窗 -->
+    <add-apply-hint-dialog 
+      :isAddApplyHintShow.sync="isAddApplyHintShow"
+      :applyInfo="applyInfo" 
+      @overBannedList="checkApproveData([],repeatList,internalList)"
+      @overRepeatList="checkApproveData([],[],internalList)"
+      @applyActiveSuccess="updateTableData"
+      @closeDialog="closeDialog"
+      />
+  </div>
+</template>
+
+<script>
+import addApplyHintDialog from './compontents/addApplyHintDialog.vue';
+import{etaTrialInterence}from '@/api/modules/crmApi.js';
+export default {
+  components: { addApplyHintDialog },
+  data() {
+    return {
+      tableData:[{
+        isApply:false
+      }],
+      isAddApplyHintShow:false,
+      applyInfo:{},
+      repeatList:[],
+      pageLoading:false,
+      internalList:[],
+    };
+  },
+  methods: {
+    addTableData(){
+      this.tableData.push({isApply:false})
+    },
+    deleteTableData(index){
+      this.tableData.splice(index,1)
+    },
+    tableRowClassName({row}){
+      //提交后未通过的 账号已被禁止,账号已重复
+      if(row.isApply===false&&row.isPass===false){
+        return 'warning-row'
+      }
+      //提交前未校验通过的 四项数据未填写或填写错误
+      if(row.isApply===false&&row.isChecked===false){
+        return 'warning-row'
+      }
+      return ''
+    },
+    checkTableData(data){
+      let phoneArr = []
+      for(let i=0;i<data.length;i++){
+        const item = data[i]
+        //已经添加过的不再做校验 因为这条数据不会提交
+        if(item.isApply) continue
+        if((item.UserName&&!item.UserName.length)||!item.UserName){
+          this.$message.warning(`请输入第${i+1}条数据的姓名`)
+          return false
+        }
+        if((item.CompanyName&&!item.CompanyName.length)||!item.CompanyName){
+          this.$message.warning(`请输入第${i+1}条数据的公司名称`)
+          return false
+        }
+        if((item.Position&&!item.Position.length)||!item.Position){
+          this.$message.warning(`请输入第${i+1}条数据的职位`)
+          return false
+        }
+        if((item.Mobile&&!item.Mobile.length)||!item.Mobile){
+          this.$message.warning(`请输入第${i+1}条数据的电话号码`)
+          return false
+        }
+        if(phoneArr.indexOf(data[i].Mobile)===-1){
+          phoneArr.push(data[i].Mobile)
+        }else{
+          //console.log('phoneArr',phoneArr)
+          this.updateTableData(data[i],'update')
+          this.$message.warning(`用户信息重复,请确认`)
+          return false
+        }
+      }
+      return true
+    },
+    async handleApprove(){
+      if(this.pageLoading) return
+      //检查数据
+      if(!this.checkTableData(this.tableData)) return 
+      //提交tableData中 isApply为false的
+      const data = this.tableData.filter((item)=>{return !item.isApply})
+      this.tableData = this.tableData.filter((item)=>{return item.isApply})
+      data.forEach(item=>{
+        delete item.isApply
+        delete item.isPass
+        delete item.isChecked
+      })
+      //说明该页数据全部已提交 直接跳转审批列表
+      if(!data.length){
+        this.$message.success('提交成功')
+        this.$router.push('/etaApprovalList')
+        return
+      }
+      //提交申请 data
+      this.pageLoading = true
+      const res = await etaTrialInterence.addApproval({List:data})
+      this.pageLoading = false
+      if(res.Ret!==200){
+        !this.tableData.length&&this.addTableData()
+        return
+      }
+      const {BannedList,//禁用的列表
+             RepeatList,//重复的列表
+             SuccessList,//成功返回的列表
+             InternalList,//除ETA试用平台其他部门重复的列表
+            } = res.Data
+      //如果这次提交的data全部通过,则跳转回审批列表
+      if(SuccessList&&SuccessList.length===data.length){
+        this.$message.success('提交成功')
+        this.$router.push('/etaApprovalList')
+      }
+      //如果没有全部通过 通过的设置isApply为true 没通过的执行checkApproveData
+      else{
+        const successList = SuccessList?SuccessList.map(item=>{
+          item.isApply = true
+          item.isPass = true
+          return item
+        }):[]
+        const bannedList = BannedList?BannedList.map(item=>{
+          item.isApply = false
+          item.isPass = false
+          return item
+        }):[]
+        const repeatList = RepeatList?RepeatList.map(item=>{
+          item.isApply = false
+          item.isPass = false
+          return item
+        }):[]
+        const internalList = InternalList?InternalList.map(item=>{
+          item.isApply = false
+          item.isPass = false 
+          return item
+        }):[]
+        this.tableData = this.tableData.concat(successList,bannedList,repeatList,internalList)
+        !this.tableData.length&&this.addTableData()
+        this.checkApproveData(bannedList,repeatList,internalList)
+      }
+    },
+    checkApproveData(BannedList=[],RepeatList=[],InternalList=[]){
+      if(BannedList.length){
+        this.applyInfo.applyType = 0
+        this.applyInfo.applyData = BannedList
+        this.repeatList = RepeatList
+        this.internalList = InternalList
+      }else if(RepeatList.length){
+        //展示全部信息
+        this.applyInfo.applyType = 1
+        this.applyInfo.applyData = RepeatList
+        this.internalList = InternalList
+      }
+      if(!BannedList.length&&!RepeatList.length){
+        if(InternalList.length){
+          //展示轻提示
+          this.$message.error('用户信息重复,请联系管理员或技术部门')
+        }
+        return 
+      }
+      this.$nextTick(()=>{
+        this.isAddApplyHintShow = true
+      })
+    },
+    updateTableData(data,type='remove'){
+      //console.log('update',data)
+      //手机号作为标识
+      const index = this.tableData.findLastIndex((item)=>{return item.Mobile===data.Mobile})
+      const tableItem = this.tableData[index]
+      if(type==='remove'){
+        /* tableItem.isApply = true
+        tableItem.isPass = true
+        this.tableData.splice(index,1,tableItem) */
+        this.tableData.splice(index,1)
+        //如果删掉的是最后一个
+        if(!this.tableData.length){
+          this.addTableData()
+        }
+      }else{
+        tableItem.isChecked=false
+        this.tableData.splice(index,1,tableItem)
+      }      
+    },
+    cancelApprove(){
+      //返回ETA试用列表
+      this.$router.push('/etaTrialList')
+    },
+    closeDialog(){
+      this.isAddApplyHintShow = false
+    }
+  },
+  mounted(){
+    if(sessionStorage.getItem('applyInfo')){
+      const applyInfo = JSON.parse(sessionStorage.getItem('applyInfo'))
+      sessionStorage.removeItem('applyInfo')
+      const data = applyInfo.applyData[0]
+      data.isApply = false
+      //this.tableData.unshift(data)
+      this.tableData=[data]
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+.add-approval-wrap{
+  .el-table .warning-row {
+    background: #FEF0F0;
+  }
+}
+</style>
+<style scoped lang="scss">
+.add-approval-wrap{
+  padding:30px;
+  background-color: #fff;
+  border-radius: 4px;
+  .color-hint{
+    color: red;
+  }
+  .btn-wrap{
+    margin-top:120px;
+    text-align: center;
+  }
+}
+</style>

+ 180 - 0
src/views/custom_manage/etaTrial/compontents/addApplyHintDialog.vue

@@ -0,0 +1,180 @@
+<script setup>
+import { ref,nextTick } from 'vue'
+import { ElMessage } from 'element-plus'
+import { ApprovalStatus,applyApprovalList } from '../use-config'
+
+const props = defineProps({
+  isAddApplyHintShow:{//弹窗显示
+    type:Boolean,
+    default:false
+  },
+  applyInfo:{//申请信息 
+    type:Object,
+    default:()=>{
+      return {
+        applyType:1,//0 账号禁用 1 信息重复
+        applyData:[]
+      }
+    }
+  }
+})
+
+const emit = defineEmits([
+  'closeDialog',
+  'overBannedList',
+  'applyActiveSuccess',
+  'close'
+])
+
+const columnList = applyApprovalList
+
+const  applyData = ref([])
+const dataIndex = ref(0)
+
+const tableLoading = ref(false)
+const tableData = ref([])
+const hintText = ref('')
+function getTableData(){
+
+  tableLoading.value = true
+  //如果是申请启用,则只展示一条数据
+  if(props.applyInfo.applyType===0){
+    applyData.value = props.applyInfo.applyData
+    tableData.value = [applyData.value[0]]
+    dataIndex.value = 0
+  }
+  //如果是申请信息重复,则展示全部数据
+  else{
+    tableData.value = props.applyInfo.applyData
+    dataIndex.value = 0
+  }
+  nextTick(()=>{
+    tableLoading.value=false
+  })
+}
+
+const applyReason = ref('')
+async function handleApprove(getNext=true){
+  if(getNext){
+    if(!applyReason.value.length){
+      ElMessage.warning('请输入申请理由')
+      return
+    }
+    const {UserName,CompanyName,Position,Mobile} = applyData.value[dataIndex.value]
+    //调用申请接口
+    const res = await etaTrialInterence.applyEnable({
+      UserName,CompanyName,Position,Mobile,
+      ApplyReasons:applyReason.value
+    })
+    if(res.Ret!==200) return 
+    ElMessage.success('提交成功')
+    //申请成功 在父页面将该记录删除
+    emit('applyActiveSuccess',applyData.value[dataIndex.value]) 
+  }
+  applyReason.value=''
+  getNextTableData()
+}
+
+function getNextTableData(){
+  dataIndex.value++
+  const length = applyData.value.length
+  if(length-1>=dataIndex.value){
+    tableData.value = [applyData.value[dataIndex.value]]
+  }else{
+    closeDialog()
+  }
+}
+
+function closeDialog(){
+  //清空数据什么的
+  applyReason.value=''
+  tableData.value=[]
+  applyData.value=[]
+  dataIndex.value=0
+
+  emit("close");
+  if(props.applyInfo.applyType===0){
+    emit('overBannedList')
+    return
+  }
+  if(props.applyInfo.applyType===1){
+    emit('overRepeatList')
+    return
+  }
+  
+}
+</script>
+
+<template>
+<!-- 新增申请 提示弹窗 弹窗 -->
+  <div class="add-apply-hint-dialog">
+    <el-dialog
+      v-if="isAddApplyHintShow"
+      :model-value="isAddApplyHintShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      title="提示"
+      @close="handleApprove(false)"
+      width="889px"
+      v-dialogDrag
+      center
+    >
+      <div class="dialog-container">
+        <p class="hint">{{hintText}}</p>
+        <div class="table-wrap">
+          <el-table :data="tableData" border v-loading="tableLoading">
+            <el-table-column v-for="column in columnList" :key="column.label"
+              align="center"
+              :prop="column.key"
+              :label="column.label"
+              :min-width="column.minWidth"
+            />
+          </el-table>
+        </div>
+        <div class="apply-reason" v-if="applyInfo.applyType===0">
+          <p>申请理由</p>
+          <textarea
+            placeholder="请输入申请理由"
+            v-model="applyReason"
+            :rows="3"
+          />
+        </div>
+      </div>
+      <div class="foot-container">
+        <template v-if="applyInfo.applyType===0">
+          <el-button type="primary" @click="handleApprove">申请启用</el-button>
+          <el-button @click="handleApprove(false)">取 消</el-button>
+        </template>
+        <template v-else>
+          <el-button type="primary" @click="closeDialog">知道了</el-button>
+        </template>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.add-apply-hint-dialog{
+  .dialog-container{
+    .table-wrap{
+      margin-top:20px;
+    }
+    .apply-reason{
+      margin-top:30px;
+    }
+    textarea{
+      width:100%;
+      margin-top: 10px;
+      padding:10px;
+      border-radius: 4px;
+      box-sizing: border-box;
+      border-color: #DCDFE6;
+    }
+  }
+  .foot-container{
+    text-align: center;
+    padding-bottom: 40px;
+    margin-top: 60px;
+  }
+}
+</style>

+ 190 - 0
src/views/custom_manage/etaTrial/compontents/applyApprovalDialog.vue

@@ -0,0 +1,190 @@
+<script setup>
+import { ref,nextTick } from 'vue'
+import {etaTrialInterence}from '@/api/modules/crmApi.js';
+import { ElMessage } from 'element-plus'
+import { ApprovalStatus,applyApprovalList } from '../use-config'
+
+const props = defineProps({
+  isApplyApprovalDialogShow: {
+    type: Boolean
+  },
+  applyInfo: {
+    type: Object,
+    default: () => {}
+  }
+})
+
+const emit = defineEmits([
+  'approved',
+  'close'
+])
+
+
+const columnList = applyApprovalList//表格列
+
+const tableLoading = ref(false)
+const tableData = ref([])
+function getTableData(){
+  tableLoading.value = true
+  tableData.value = props.applyInfo.applyData
+  nextTick(()=>{
+    tableLoading.value=false
+  })
+}
+
+
+const rejectReason = ref('')//驳回理由
+const applyStatus = ref(1)//审批状态
+async function handleApprove(){
+  //提交这条审批
+  let res = null
+  if(applyStatus.value===1){
+    res = await etaTrialInterence.agreeApply({
+      ApprovalId:tableData.value[0].ApprovalId
+    })
+  }else{
+    if(!rejectReason.value.length){
+      ElMessage.warning('请填写驳回理由')
+      return
+    }
+    res = await etaTrialInterence.rejectApply({
+      ApprovalId:tableData.value[0].ApprovalId,
+      RejectReason:rejectReason.value
+    })
+  }
+  if(res.Ret!==200) return 
+  ElMessage.success(applyStatus.value===1?'通过成功':'驳回成功')
+  emit('approved')
+  closeDialog()
+}
+function closeDialog(){
+  //清空数据什么的
+  applyStatus.value=1
+  rejectReason.value=''
+  emit("close");
+}
+
+</script>
+
+<template>
+<!-- 申请账号审批/申请启用审批 弹窗 -->
+  <div class="apply-approval-dialog">
+    <el-dialog
+      :model-value="isApplyApprovalDialogShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      :title="`申请${applyInfo.applyType===0?'启用':'账号'}审批`"
+      @close="closeDialog"
+      width="889px"
+      v-dialogDrag
+      center
+    >
+      <div class="dialog-container">
+        <div class="table-wrap">
+          <el-table :data="tableData" border v-loading="tableLoading">
+            <el-table-column v-for="column in columnList" :key="column.label"
+              align="center"
+              :prop="column.key"
+              :label="column.label"
+              :min-width="column.minWidth"
+            />
+          </el-table>
+        </div>
+        <div class="apply-reason" v-if="applyInfo.applyType===0">
+          <p>申请理由</p>
+          <textarea
+            v-model="applyInfo.applyData[0].ApplyReasons"
+            disabled
+            :rows="3"
+          />
+        </div>
+        <div class="apply-form">
+          <div class="apply-status">
+            <span>审批状态</span>
+             <el-radio-group v-model="applyStatus">
+              <el-radio :label="1">通过</el-radio>
+              <el-radio :label="2" class="color-hint">驳回</el-radio>
+            </el-radio-group>
+          </div>
+          <div class="reject-reason" v-if="applyStatus===2">
+            <span>驳回理由</span>
+            <textarea
+              v-model="rejectReason"
+              placeholder="请输入驳回理由"
+              :rows="3"
+            />
+          </div>
+        </div>
+      </div>
+      <div class="foot-container">
+        <el-button type="primary" @click="handleApprove">确 定</el-button>
+        <el-button @click="closeDialog">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<style lang="scss">
+</style>
+<style scoped lang="scss">
+.apply-approval-dialog{
+  .dialog-container{
+    /* .table-wrap{} */
+    .apply-reason{
+      margin-top:30px;
+      padding-bottom:30px;
+      border-bottom: 1px dashed #DCDFE6;
+    }
+    .apply-form{
+      margin-top:30px;
+      /* .apply-status{} */
+      .reject-reason{
+        margin-top:30px;
+      }
+    }
+    textarea{
+      width:100%;
+      margin-top: 10px;
+      padding:10px;
+      border-radius: 4px;
+      box-sizing: border-box;
+      border-color: #DCDFE6;
+    }
+  }
+  .foot-container{
+    text-align: center;
+    padding-bottom: 40px;
+    margin-top: 60px;
+  }
+}
+  .apply-status{
+    .el-radio{
+      //更改单选框的样式
+    :deep(.el-radio__inner){
+      background: transparent;
+      &::after{
+        width:6px;
+        height:6px;
+        background-color: #409EFF;
+      }
+    }
+    }
+  }
+  .color-hint{
+    //更改驳回的样式
+    &.is-checked{
+      .el-radio__inner{
+        border-color: #C54322;
+        &::after{
+          background-color: #C54322;
+        }
+      }
+      .el-radio__input{
+        &.is-checked{//我真服了
+          +.el-radio__label{
+            color: #C54322;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 93 - 0
src/views/custom_manage/etaTrial/compontents/questionChart.vue

@@ -0,0 +1,93 @@
+<template>
+	<div class="pie-chart" ref="pieChart" style="height: 100%;width: 100%;"></div>
+</template>
+
+<script>
+
+  export default {
+    name:"questionChart",
+    props:{
+      chartData:{
+        required:true,
+        type:Array
+      }
+    },
+    data() {
+      return {
+        options: {
+          tooltip: {
+            trigger: 'item',
+            formatter:'{b0}:{c0}%'
+          },
+          legend: {
+            bottom: '5%',
+            left: 'center',
+            icon:'circle'
+          },
+          series: [
+            {
+              type: 'pie',
+              center: ['50%', '40%'],
+              radius: ['45%', '70%'],
+              avoidLabelOverlap: false,
+              label: {
+                show: false,
+                position: 'center'
+              },
+              itemStyle: {
+                borderColor: '#fff',
+                borderWidth: 2
+              },
+              emphasis: {
+                label: {
+                  show: true,
+                  fontSize: '40',
+                  fontWeight: 'bold',
+                },
+              },
+              labelLine: {
+                show: false,
+              },
+              data:[]
+            },
+          ],
+          color: ["#0F80E7",'#61DCF3','#60E184','#7A4DDA','#F7E701','#34C8A4','#FFBB37','#E241EE','#FF9559','#FF8CE9',
+            '#FF5BAA','#E75300','#B8ED75','#3AEBC0','#A1F8FF','#03FFD2','#A56FF8','#F0960E','#2CB2EC','#63B0FF'], 
+          
+        },
+        chartData:[{value:1,name:'A'},{value:4,name:'B'},{value:5,name:'C'}]
+      }
+    },
+
+    methods:{
+      drawChart() {
+        const chart = this.$refs.pieChart;
+        if (chart) {
+          const myChart = echarts.init(chart);
+          this.options.series[0].data = this.chartData
+          myChart.setOption(this.options);
+          // '禁止'点击图例后的默认行为
+          myChart.on('legendselectchanged', ({ selected, name }) => {
+            selected[name] = true
+            myChart.setOption({ legend: { selected } })
+          })
+          window.addEventListener('resize', function () {
+            myChart.resize();
+          });
+          this.$on('hook:destroyed', () => {
+            window.removeEventListener('resize', function () {
+              myChart.resize();
+            });
+          });
+        }
+      },
+    },
+    mounted() {
+      this.drawChart();
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 411 - 0
src/views/custom_manage/etaTrial/etaTrialList.vue

@@ -0,0 +1,411 @@
+<script setup>
+import { ref,computed, reactive,toRefs } from 'vue'
+import { useRoute,useRouter } from 'vue-router'
+import { ElMessageBox,ElMessage } from 'element-plus'
+import { Search } from '@element-plus/icons-vue'
+import{etaTrialInterence}from '@/api/modules/crmApi.js';
+import { useConfig } from './use-config'
+import mPage from '@/components/mPage.vue'
+import applyApprovalDialog from './compontents/applyApprovalDialog.vue'
+import applyHintDialog from './compontents/addApplyHintDialog.vue';
+
+const $route = useRoute()
+const $router = useRouter()
+
+const { columnList,canEdit,listType,interenceName,ApprovalStatus } = useConfig()
+
+const Role = computed(() => {
+  return localStorage.getItem("Role");
+})
+
+const sortKeys =[
+  'Expiration','ModifyTime','LastLoginTime','ActiveTime',
+  'IndexNum','ChartNum','LoginNum','LastLoginDuration'
+]//需要排序的字段
+
+const approvalStateArr = [
+  { key: 2, label: "待审批" },
+  { key: 3, label: "已审批" },
+  { key: 1, label: "全部" },
+]//管理员-审批列表的可选项
+
+
+const tableLoading = ref(false)
+const total = ref(0)
+const approvalNum = ref(0)//还剩多少条待审批
+const tableData = ref([])
+const filterParam = reactive({
+    searchText: "",//搜索框文字
+    onlyMine:false,// 是否只是我申请的
+    SortParam:'',//排序字段
+    SortType:'',//升序降序
+    approvalState: 2,//管理员-审批列表的默认选项
+    currentPage:1,
+    pageSize:10,
+    
+})
+function getTableData(){
+
+  etaTrialInterence[interenceName]({
+      PageSize:filterParam.pageSize,
+      CurrentIndex:filterParam.currentPage,
+      KeyWord:filterParam.searchText,
+      SortParam:filterParam.SortParam,
+      SortType:filterParam.SortType,
+      ListParam:filterParam.approvalState,
+      IsOnlyMe:filterParam.onlyMine
+    }).then((res)=>{
+      if(res.Ret!==200) return 
+
+      const {List,Paging,ApprovalNum} = res.Data
+      tableData.value = List||[]
+      total.value = Paging.Totals
+      approvalNum.value = ApprovalNum||0
+      tableLoading.value=false
+    })
+}
+getTableData()
+
+function sortChangeHandle({prop,order}){
+  filterParam.SortParam = prop
+  filterParam.SortType = order?order==='ascending'?'asc':'desc':''
+  filterParam.currentPage=1
+  getTableData()
+}
+
+function filterChange() {
+  filterParam.currentPage=1
+  getTableData()
+}
+
+function handleCurrentChange(pageNo){
+  filterParam.currentPage=pageNo
+  getTableData()
+}
+
+function changePath(path){
+  if($route.path===path) return
+
+  $router.push(path)
+}
+
+function toQuestionnaireSurvey(){
+  $router.push({path: '/questionnaireSurvey'})
+}
+
+
+//将表格数据转换成applyInfo的形式
+const applyInfo = ref({})//当前选择的表格数据(经格式转换)
+function getApplyInfo(type,data){
+  //data.ApplyMethod 这条数据是申请账号1,还是申请启用2
+  const temp = {applyType:1,applyData:[]}
+  temp.applyType = data.ApplyMethod||2
+  //重新申请 申请启用时 applyType要设置为0
+  if((type==='apply'||type==='withdraw'||type==='approve')&&data.ApplyMethod===2){
+    temp.applyType = 0
+  }
+  if(type==='active'){
+    temp.applyType=0
+  }
+  temp.applyData = [data]
+  applyInfo.value = temp
+}
+/* 弹窗 */
+const isApplyApprovalDialogShow = ref(false)
+const isAddApplyHintShow = ref(false)
+const showDetailDialogShow = ref(false)
+async function handleOperate(type,data){
+  getApplyInfo(type,data)
+
+  //审批 申请启用 重新申请 是弹窗
+  //审批
+  if(type==='approve'){
+    isApplyApprovalDialogShow.value = true
+  }
+  //申请启用
+  if(type==='active'){
+    isAddApplyHintShow.value = true
+  }
+  //重新申请
+  if(type==='apply'){
+    const {ApplyMethod} = data
+    //如果是申请账号,则跳转至新增申请页面
+    if(ApplyMethod===1){
+      sessionStorage.setItem('applyInfo',JSON.stringify(this.applyInfo))
+      $router.push('/etaAddApproval')
+    }
+    //如果是申请启用,则显示弹窗
+    else{
+      isAddApplyHintShow.value = true
+    }
+  }
+  //驳回理由 撤回 删除 是 MessageBox
+  //撤回
+  if(type==='withdraw'){
+    //调撤回接口 getTableData()
+    const res = await etaTrialInterence.withdrawApproval({
+      Mobile:data.Mobile
+    })
+
+    if(res.Ret!==200) return
+    ElMessageBox.confirm('', '撤回成功', {
+      confirmButtonText: '重新申请',
+      cancelButtonText: '取消',
+      cancelButtonClass: "btn-custom-cancel",
+      type: 'success',
+      center: true
+    }).then(() => {
+      const {ApplyMethod} = data
+      if(ApplyMethod===1){
+        sessionStorage.setItem('applyInfo',JSON.stringify(applyInfo.value))
+        $router.push('/etaAddApproval')
+      }
+      else{
+        isAddApplyHintShow.value = true
+      }
+    }).catch(() => {
+      getTableData()
+    });
+  }
+  //驳回理由
+  if(type==='checkReplay'){
+    ElMessageBox.confirm(data.ApprovalRemark||'', '驳回理由', {
+      confirmButtonText: '知道了',
+      showCancelButton:false,
+    }).then(() => {}).catch(() => {});
+  }
+  //删除 二次确认
+  if(type==='delete'){
+    ElMessageBox.confirm('此操作将永久删除该数据, 是否继续?', '提示', {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }).then(() => {
+      //调删除接口
+      etaTrialInterence.deleteApproval({
+        ApprovalId:data.ApprovalId
+      }).then(res=>{
+        if(res.Ret!==200) return 
+        getTableData()
+        ElMessage.success('删除成功')
+      })
+    }).catch(() => {});
+  }
+  //展示账号密码
+  if(type==='showDetail'){
+      showDetailDialogShow.value = true
+  }
+}
+
+const { searchText,onlyMine,approvalState,currentPage,pageSize } = toRefs(filterParam)
+
+</script>
+<template>
+  <!-- ETA试用列表+审批列表  -->
+  <div class="eta-trial-list">
+    <div class="table-select">
+      <div class="btn-list">
+        <template v-if="Role.includes('admin')">
+          <!-- 管理员-ETA试用列表 -->
+          <template v-if="$route.path==='/etaTrialList'">
+            <el-button type="primary" @click="changePath('/etaApprovalList')">审批列表</el-button>
+            <div class="approval-hint" v-if="approvalNum">
+              ·{{ approvalNum }}条申请记录待审批
+            </div>
+            <el-button type="primary" @click="toQuestionnaireSurvey" style="margin-left: 30px;">问卷调研</el-button>
+          </template>
+          <!-- 管理员-审批列表 -->
+          <template v-if="$route.path==='/etaApprovalList'">
+            <el-select v-model="approvalState" placeholder="请选择" @change="filterChange">
+              <el-option
+                v-for="item in approvalStateArr"
+                :key="item.key"
+                :label="item.label"
+                :value="item.key"
+              >
+              </el-option>
+            </el-select>
+          </template>
+        </template>
+        <template v-else>
+          <!-- 非管理员-ETA试用列表 -->
+          <template v-if="$route.path==='/etaTrialList'">
+            <el-button type="primary" @click="changePath('/etaAddApproval')">新增申请</el-button>
+            <el-button type="primary" plain @click="changePath('/etaApprovalList')" style="margin-left: 30px;">申请列表</el-button>
+            <el-button type="primary" @click="toQuestionnaireSurvey" style="margin-left: 30px;">问卷调研</el-button>
+            <el-checkbox v-model="onlyMine" @change="filterChange" style="margin-left: 30px;">我申请的</el-checkbox>
+          </template>
+          <!-- 非管理员-我的审批+账号列表 -->
+          <template v-if="$route.path==='/etaApprovalList'">
+            <el-radio-group v-model="listType">
+              <el-radio-button label="all">我的审批</el-radio-button>
+              <el-radio-button label="approved">账号列表</el-radio-button>
+            </el-radio-group>
+            <el-tooltip class="item" effect="dark" 
+              content="该列表为初始账号密码,用于登陆ETA试用平台:https://exptest.hzinsights.com/" 
+              placement="right">
+              <span class="el-icon-question" style="margin-left:5px;color:#C0C4CC"></span>
+            </el-tooltip>
+          </template>
+        </template>
+      </div>
+      <div class="search-wrap">
+        <el-input
+          v-model="searchText"
+          :prefix-icon="Search"
+          placeholder="姓名/公司名称"
+          @input="filterChange"
+        ></el-input>
+      </div>
+    </div>
+    <div class="table-wrap">
+      <el-table :data="tableData" @sort-change="sortChangeHandle" border v-loading="tableLoading">
+        <el-table-column 
+          v-for="column in columnList" 
+          :key="column.label"
+          align="center"
+          :prop="column.key"
+          :label="column.label"
+          :sortable="sortKeys.includes(column.key)?'custom':false"
+          :min-width="column.minWidth"
+        >
+          <template #default="{row}">
+            <span v-if="column.key==='approveType'">
+              {{row['approveType']===0?'申请账号':'申请启用'}}
+            </span>
+
+            <span v-else-if="column.key==='Enabled'&&(!Role.includes('admin')&&listType==='approved')"
+              :class="{'color-hint':row['Enabled']===0}"
+            >
+              {{row['Enabled']===0?'禁用':'启用'}}
+            </span>
+            <span v-else>
+              {{row[column.key]}}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column v-if="canEdit" label="操作" align="center">
+          <template #default="{row}">
+            <!-- 非管理员-账号列表的操作: 申请启用 -->
+            <template v-if="!Role.includes('admin')&&listType==='approved'">
+              <el-button type="text" size="small" v-if="row.Enabled===0" @click="handleOperate('active',row)">申请启用</el-button>
+            </template>
+            <!-- 非管理员-我的审批操作: 撤回,重新申请,删除,驳回理由-->
+            <template v-if="!Role.includes('admin')&&listType==='all'">
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]===0" @click="handleOperate('withdraw',row)">撤回</el-button>
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]>=2" @click="handleOperate('apply',row)">重新申请</el-button>
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]===3" @click="handleOperate('checkReplay',row)">驳回理由</el-button>
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]===2" class="color-hint" @click="handleOperate('delete',row)">删除</el-button>
+              <!-- 已审批通过的:查看密码 -->
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]===1" @click="handleOperate('showDetail',row)">账号密码</el-button>
+            </template>
+            <!-- 管理员-审批列表操作: 审批-->
+            <template v-if="Role.includes('admin')">
+              <el-button type="text" size="small" v-if="ApprovalStatus[row.ApprovalStatus]===0" @click="handleOperate('approve',row)">审批</el-button>
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="height:40px;margin: 20px 0">
+        <m-page
+          :page_no="currentPage"
+          :pageSize="pageSize"
+          :total="total"
+          style="margin-top:30px;"
+          @handleCurrentChange="handleCurrentChange"
+        />
+      </div>
+    </div>
+    <!-- 申请账号审批/申请启用审批 弹窗 -->
+    <apply-approval-dialog 
+      :isApplyApprovalDialogShow="isApplyApprovalDialogShow"
+      :applyInfo="applyInfo"
+      @approved="getTableData"
+      @close="isApplyApprovalDialogShow=false"
+    />
+    <!-- 申请启用 弹窗 -->
+    <add-apply-hint-dialog 
+      :isAddApplyHintShow="isAddApplyHintShow"
+      :applyInfo="applyInfo"
+      @applyActiveSuccess="getTableData"
+      @closeDialog="closeDialog"
+    />
+      <!-- 查看账号密码弹窗 -->
+      <!-- <el-dialog 
+        v-if="showDetailDialogShow"
+        :visible.sync="showDetailDialogShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        :title="`${applyInfo.applyData[0].UserName}——账号密码`"
+        width="889px"
+        v-dialogDrag
+        center
+      >
+        <div class="apply-detail">
+            <p>公司名称:{{applyInfo.applyData[0].CompanyName}}</p>
+            <p>账号:{{applyInfo.applyData[0].Account}}</p>
+            <p>密码:{{applyInfo.applyData[0].Password}}</p>
+            <div class="button">
+                <el-button type="primary" @click="showDetailDialogShow=false">知道了</el-button>
+            </div>
+        </div>
+
+      </el-dialog> -->
+  </div>
+</template>
+<style scoped lang="scss">
+.eta-trial-list{
+  padding:30px;
+  background-color: #fff;
+  border-radius: 4px;
+  .table-select{
+    display: flex;
+    justify-content: space-between;
+    .btn-list{
+      display: flex;
+      align-items: center;
+      .approval-hint{
+        margin-left: 15px;
+        border-radius: 4px;
+        padding:5px 12px;
+        background-color: #FEF0F0;
+        color: #F56C6C;
+        position: relative;
+        &::before{
+          content: '';
+          position:absolute;
+          width:0;
+          height:0;
+          left:-12px;
+          top:calc(50% - 6px);
+          border: 6px solid transparent;
+          border-right: 6px solid #FEF0F0;
+        }
+      }
+    }
+    .search-wrap{
+      width:400px;
+    }
+  }
+  .table-wrap{
+    margin-top:30px;
+  }
+
+  .color-hint{
+    color: red;
+  }
+  .apply-detail{
+      display: flex;
+      flex-direction: column;
+      gap:20px;
+      p{
+          font-size: 16px;
+          margin-left: 20px;
+      }
+      .button{
+          text-align: center;
+          padding-bottom: 25px;
+      }
+  }
+}
+</style>  

+ 395 - 0
src/views/custom_manage/etaTrial/questionnaireOption.vue

@@ -0,0 +1,395 @@
+<template>
+  <div id="questionnaire-survey-option" class="questionnaire-survey-option">
+    <div class="questionnaire-title" style="margin-bottom: 32px;">
+      {{ optionList[0]&&optionList[0].Options[0] }}
+      <div class="question-setting-zone">
+        <img src="../../../assets/img/icons/edit-blue.png" @click="editTitle" />
+      </div>
+    </div>
+    <div class="question-row"  v-for="(item,index) in optionList.slice(1)" :key="index">
+      <div class="questionnaire-title">
+        <span style="color: #C54322;" v-show="item.IsMust==1">*</span>{{index+1}}.{{ item.Question }}
+        <div class="question-setting-zone">
+          <img src="../../../assets/img/icons/edit-blue.png" @click="editQusetion(item,index)" />
+          <img src="../../../assets/img/icons/delete-red.png" @click="deleteQusetion(item,index)" />
+        </div>
+      </div>
+      <div class="questionnaire-options">
+        <el-radio-group v-if="item.Type==1" v-model="item.answer">
+          <el-radio v-for="it in item.Options" :label="it" :key="it">{{ it }}</el-radio>
+        </el-radio-group>
+        <el-checkbox-group v-else-if="item.Type==2" v-model="item.answer">
+          <el-checkbox v-for="it in item.Options" :label="it" :key="it">{{ it }}</el-checkbox>
+        </el-checkbox-group>
+        <el-input v-else type="textarea" v-model="item.answer" :rows="5" resize="none"></el-input>
+      </div>
+    </div>
+    <div class="add-sty">
+      <span @click="addQusetion">
+        <img src="../../../assets/img/set_m/add_ico.png" />
+        添加题目
+      </span>
+    </div>
+    <div class="operation-zone">
+      <el-button type="primary" style="height: 40;width: 120px;" @click="submit">保存</el-button>
+      <el-button style="height: 40;width: 120px;margin-left: 30px;" @click="cancel">取消</el-button>
+    </div>
+    <!-- 添加/编辑题目 弹窗 -->
+    <el-dialog
+    width="510px"
+    :title="dialogTitle"
+    :visible.sync="showQuestionOption"
+    :append-to-body="true"
+    v-dialogDrag @closed="questionDiaClose">
+      <el-form style="padding: 10px 40px 0;"
+      ref="questionForm"
+      hide-required-asterisk
+      :model="questionForm">
+        <el-form-item label="" prop="Type" :rules="[{required:true,message:'请选择题目类型',trigger:'change'}]">
+          <el-radio-group v-model="questionForm.Type" :disabled="isEdit">
+            <el-radio :label="1">单选</el-radio>
+            <el-radio :label="2">多选</el-radio>
+            <el-radio :label="3">简答</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="" prop="IsMust" :rules="specialRules[0]">
+          <div style="display: flex;align-items: center;justify-content: space-between;">
+            <el-select v-model="questionForm.IsMust" style="width: 240px;">
+              <el-option label="必答题" :value="1"></el-option>
+              <el-option label="非必答题" :value="0"></el-option>
+            </el-select>
+            <el-input-number v-model="questionForm.Sort" style="width: 120px;"
+            controls-position="right" :min="0" step-strictly placeholder="输入排序"></el-input-number>
+          </div>
+        </el-form-item>
+        <el-form-item label="" prop="Question" :rules="[{required:true,message:'问题描述不能为空',trigger:'change'}]">
+          <el-input v-model="questionForm.Question" placeholder="请输入问题描述" style="width: 100%;" />
+        </el-form-item>
+        <template v-if="questionForm.Type==1 || questionForm.Type==2">
+          <el-form-item label="" :prop="`Options.${ansInd}`" :rules="[{required:true,message:'选项不能为空',trigger:'change'}]"
+          v-for="(ans,ansInd) in questionForm.Options" :key="ansInd">
+            <div style="display: flex;align-items: center;">
+              <el-input v-model="questionForm.Options[ansInd]" placeholder="请输入选项" style="flex-grow: 1;" >
+                <div slot="prefix" style="margin-left: 5px;color: #333333;font-size: 14px;"> 
+                  {{ `${OptionLetterList[ansInd]} :`}}
+                </div>
+              </el-input>
+              <img src="../../../assets/img/icons/delete-red.png" v-show="questionForm.Options.length>2"
+              style="margin-left: 18px;width: 16px;cursor: pointer;" @click="deleteAnswer(ansInd)"/>
+            </div>
+          </el-form-item>
+          <div class="add-sty" style="margin-top:0 ;">
+            <span @click="addAnswer">
+              <img src="../../../assets/img/set_m/add_ico.png" />
+              添加选项
+            </span>
+          </div>
+        </template>
+        <div class="operation-zone">
+          <el-button type="primary" style="width: 120px;height: 40px;" @click="submitQuestion">确定</el-button>
+          <el-button style="width: 120px;height: 40px;margin-left: 26px;" @click="showQuestionOption=false">取消</el-button>
+        </div>
+      </el-form>
+    </el-dialog>
+    <!-- 编辑说明弹窗 -->
+    <el-dialog
+    width="510px"
+    :title="dialogTitle"
+    :visible.sync="showTitleDia"
+    :append-to-body="true"
+    @closed="titleDiaClose"
+    v-dialogDrag>
+      <el-form style="padding: 10px 40px 0;"
+      ref="titleForm"
+      hide-required-asterisk
+      :model="titleForm">
+        <el-form-item label="" prop="title" :rules="[{required:true,message:'说明不能为空',trigger:'blur'}]">
+          <el-input v-model="titleForm.title" placeholder="请输入内容" style="width: 100%;" type="textarea"
+          :rows="4" resize="none"></el-input>
+        </el-form-item>
+        <div class="operation-zone">
+          <el-button type="primary" style="width: 120px;height: 40px;" @click="submitTitle">确定</el-button>
+          <el-button style="width: 120px;height: 40px;margin-left: 26px;" @click="showTitleDia=false">取消</el-button>
+        </div>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import{etaTrialInterence}from '@/api/modules/crmApi.js';
+
+  export default {
+    name:'questionnaireOption',
+    data() {
+      this.OptionLetterList=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+      'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
+      return {
+        optionList:[],
+        // 弹窗
+        dialogTitle:'',
+        showQuestionOption:false,
+        showTitleDia:false,
+        questionForm:{
+          Type:1,
+          IsMust:1,
+          Sort:0,
+          Question:'',
+          Options:['','']
+        },
+        isEdit:false,
+        questionIndex:0,
+        tempTile:'尊敬的用户,感谢您使用系统,以下是对系统使用的调研问卷,我们期待您真实的反馈!',
+        titleForm:{
+          title:''
+        },
+        specialRules:[
+          [
+            {required:true,message:'请选择题目是否必答',trigger:'change'},
+            {validator:(rule,value,callback)=>{
+              if(!this.questionForm.Sort&&this.questionForm.Sort!=0){
+                callback(new Error('请选择排序'))
+              }else{
+                callback()
+              }
+            },trigger:['blur','change']}
+          ]
+        ]
+      }
+    },
+    methods:{
+      getQuestionOption(){
+        etaTrialInterence.getQuestionOptionList().then(res=>{
+          if(res.Ret==200){
+            this.optionList = res.Data.List || []
+            if(this.optionList.length==0){
+              // 添加标题项
+              this.optionList.push({
+                Question:'标题',
+                Type:4,
+                Sort:-1,
+                Options:[''],
+                CreateTime:this.$moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
+              })
+            }
+            this.tempTile = this.optionList[0].Options[0]
+            this.optionList.forEach(item =>{
+              this.$set(item,'answer',item.Type==2?[]:'')
+            })
+          }
+        })
+      },
+      // 编辑说明
+      editTitle(){
+        this.titleForm.title = this.optionList[0].Options[0]
+        this.dialogTitle = "编辑说明"
+        this.showTitleDia = true
+      },
+      // 编辑说明弹窗关闭
+      titleDiaClose(){
+        this.titleForm.title=''
+        this.$nextTick(()=>{
+          this.$refs.titleForm.clearValidate()
+        })
+      },
+      // 添加题目
+      addQusetion(){
+        this.dialogTitle = "添加题目"
+        this.showQuestionOption = true
+        this.questionForm.Sort = this.optionList[this.optionList.length-1].Sort+1
+        this.isEdit=false
+      },
+      editQusetion(item,index){
+        this.dialogTitle = "编辑题目"
+        this.questionIndex = index+1
+        this.questionForm={
+          ...item,
+          Options:[...item.Options],
+          answer:item.Type==2?[]:''}
+        this.isEdit=true
+        this.showQuestionOption = true
+      },
+      // 删除题目
+      deleteQusetion(item,index){
+        this.$confirm('删除题目将一起删除该题目下所有回答,确认删除吗?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(async () => {
+          if(item.QuestionnaireId){
+            // 数据库里原本有,请求删除接口
+            const res=await etaTrialInterence.questionOptionDelete({QuestionnaireId:item.QuestionnaireId})
+            if(res.Ret !=200) return 
+          }
+          this.optionList.splice(index+1,1)
+          this.$message.success('删除成功')
+        }).catch(() => {});
+      },
+      // 添加选项 - 单选/多选
+      addAnswer(){
+        this.questionForm.Options.push('')
+      },
+      // 删除选项
+      deleteAnswer(index){
+        this.questionForm.Options.splice(index,1)
+      },
+      // 题目弹窗关闭
+      questionDiaClose(){
+        this.questionForm={
+          Type:1,
+          IsMust:1,
+          Sort:0,
+          Question:'',
+          Options:['','']
+        },
+        this.$nextTick(()=>{
+          this.$refs.questionForm.clearValidate()
+        })
+      },
+      // 提交问题
+      submitQuestion(){
+        this.$refs.questionForm.validate(valid=>{
+          if(valid){
+            // 单选、多选选项判断
+            if(this.questionForm.Type==1 || this.questionForm.Type==2){
+              let clearRepeatList = Array.from(new Set(this.questionForm.Options))
+              if(this.questionForm.Options.length!=clearRepeatList.length){
+                this.$message.warning('有选项内容重复')
+                return 
+              }
+            }
+
+            if(!(this.questionForm.QuestionnaireId || this.questionForm.virtualId)){
+              //添加
+              this.optionList.push({
+                virtualId:'00000',
+                Type:this.questionForm.Type,
+                Sort:this.questionForm.Sort,
+                IsMust:this.questionForm.IsMust,
+                Question:this.questionForm.Question,
+                Options:this.questionForm.Type==3?[]:this.questionForm.Options,
+                CreateTime:this.$moment(new Date()).format('YYYY-MM-DD HH:mm:ss'),
+                answer:this.questionForm.Type==2?[]:''
+              })
+            }else{
+              this.optionList[this.questionIndex]={
+                ...this.questionForm,
+                Options:[...this.questionForm.Options],
+                answer:this.questionForm.Type==2?[]:''}
+            }
+            // 排序
+            this.optionList.sort((a,b) =>{
+              if(b.Sort != a.Sort){
+                return a.Sort - b.Sort
+              }else{
+                return new Date(b.CreateTime) - new Date(a.CreateTime)
+              }
+            })
+            // console.log(this.questionForm,this.optionList);
+            this.$message.success(this.dialogTitle+'成功')
+            this.showQuestionOption=false
+          }
+        })
+      },
+      // 提交说明
+      submitTitle(){
+        this.$refs.titleForm.validate(valid=>{
+          if(valid){
+            this.optionList[0].Options[0] = this.titleForm.title
+            this.$message.success(this.dialogTitle+'成功')
+            this.showTitleDia=false
+          }
+        })
+      },
+      // 保存
+      submit(){
+        etaTrialInterence.questionOptionSave({List:this.optionList}).then(res=>{
+          if(res.Ret ==200){
+            this.$message.success('保存成功')
+            this.getQuestionOption()
+          }
+        })
+      },
+      // 取消保存
+      cancel(){
+        this.getQuestionOption()
+      }
+    },
+    created() {
+      this.getQuestionOption()
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .questionnaire-survey-option{
+    width: 40%;
+    min-width: 750px;
+    background-color: white;
+    padding: 30px;
+    box-sizing: border-box;
+    min-height: calc(100vh - 110px);
+    .question-row{
+      margin-top: 26px;
+      .questionnaire-options{
+        margin-top: 18px;
+        padding: 20px;
+      }
+    }
+    .operation-zone{
+      margin-top: 80px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    .questionnaire-title{
+      font-weight: 500;
+      font-size: 16px;
+      display: flex;
+      align-items: center;
+      .question-setting-zone{
+        display: flex;
+        align-items: center;
+        img{
+          cursor: pointer;
+          height: 16px;
+          margin-left: 20px;
+        }
+      }
+    }
+  }
+
+  .add-sty{
+    font-size: 14px;
+    font-weight: 400;
+    color: #409EFF;
+    margin-top: 60px;
+    span{
+      display: inline-flex;
+      align-items: center;
+      cursor: pointer;
+      img{
+        height: 16px;
+        margin-right: 8px;
+      }
+    }
+  }
+  .operation-zone{
+    padding: 50px 0 35px;
+    text-align: center;
+  }
+
+</style>
+<style lang="scss">
+  .el-dialog .el-input {
+    width: 100% !important;
+  }
+#questionnaire-survey-option{
+  .el-checkbox{
+    margin-right: 40px;
+  }
+  .el-radio{
+    margin-right: 40px;
+  }
+}
+</style>

+ 405 - 0
src/views/custom_manage/etaTrial/questionnaireSurvey.vue

@@ -0,0 +1,405 @@
+<script setup>
+import { reactive, ref } from 'vue'
+import { useRouter } from 'vue-router'
+import {etaTrialInterence} from '@/api/modules/crmApi.js';
+import mPage from '@/components/mPage.vue'
+
+const $router = useRouter()
+
+
+const questionParamList = [{label:'全部',value:1},{label:'选择题',value:2},{label:'简答题',value:3}]
+const questionTypeList = ['单选题','多选题','填空题']
+const OptionLetterList=[]
+for(let i=0;i<26;i++) OptionLetterList.push(String.fromCharCode(65+i));
+
+
+// radio-button改变
+const listType = ref(1)
+function listTypeChange(value){
+  if(value==1){
+    getQuestionData()
+  }else if(value==2){
+    getAnswererList()
+  }
+}
+
+const questionType = ref(1)
+const statisticsResult = ref([])
+function getQuestionData(){
+  etaTrialInterence.getQuestionDataList({ListParam:questionType.value}).then(res=>{
+    if(res.Ret !=200) return 
+    statisticsResult.value = res.Data ?res.Data.filter(item => item.Type!=4): []
+    statisticsResult.value.forEach(item =>{
+      if(!item.Options) return 
+      item.chartData =  item.Options.map((item,index)=>{
+        return {value:Math.round(item.Percentage*10000)/100  ,name:OptionLetterList[index]}
+      })
+    })
+  })
+}
+getQuestionData()
+
+function toTextDetail(item,index){
+  $router.push({path:'/textQuestionDetail',
+  query:{
+    quesionId:item.QuestionnaireId,
+    question:index+'.'+item.Question
+  }})
+}
+
+const detailData = []
+const detailDataSearchP = reactive({
+  CurrentIndex:1,
+  PageSize:10
+})
+const total = ref(0)
+function getAnswererList(){
+  etaTrialInterence.getDetailDataList(detailDataSearchP).then(res=>{
+    if(res.Ret == 200){
+      detailData.value = res.Data.List || []
+      total.value = res.Data.Paging.Totals || 0
+    }
+  })
+}
+function pageChange(page_no){
+  detailDataSearchP.CurrentIndex = page_no
+  getAnswererList()
+}
+
+// 查看详情
+ // 弹窗
+const questionnaireDetailShow = ref(false)
+const detailTitle = ref('')
+const detailForm = ref({ })
+const questionList = ref([])
+function questionnaireDetail(row){
+  etaTrialInterence.getDetailDataDetail({Mobile:row.Mobile,CreateTime:row.CreateTime}).then(res=>{
+    if(res.Ret == 200){
+      let temArr = res.Data || []
+      detailTitle.value = temArr.find(item => item.Type==4)?temArr.find(item => item.Type==4).Options[0]:''
+      questionList.value = temArr.filter(item => item.Type!=4)
+      questionList.value.map(item =>{
+        detailForm.value['detail'+item.QuestionnaireId] = 
+        item.Type==2?item.UserOptions?item.UserOptions.split('~#'):[]:item.UserOptions
+      })
+      questionnaireDetailShow.value=true
+    }
+  })
+}
+
+function toQuestionnaireOption(){
+  $router.push('/questionnaireOption')
+}
+
+
+</script>
+
+<template>
+  <div class="questionnaire-survey-container" id="questionnaire-survey-container">
+    <div class="survey-operation-zone">
+      <div class="survey-operation-left">
+        <el-radio-group v-model="listType" style="margin-left: 30px;" @change="listTypeChange">
+          <el-radio-button :label="1">统计结果</el-radio-button>
+          <el-radio-button :label="2">详细数据</el-radio-button>
+        </el-radio-group>
+        <el-select v-model="questionType" placeholder="请选择题目类型" v-show="listType==1" style="width: 240px;margin:0 30px;" @change="getQuestionData">
+          <el-option :label="item.label" :value="item.value" v-for="(item) in questionParamList" :key="item.value"></el-option>
+        </el-select>
+      </div>
+      <div class="survey-operation-right">
+        <el-button type="primary" @click="toQuestionnaireOption" style="margin-left: 30px;">问卷配置</el-button>
+      </div>
+    </div>
+    <!-- 统计结果 -->
+    <div class="survey-question-zone" v-show="listType==1"
+    item-selector=".survey-question-box" gutter="20" id="survey-question-zone">
+      <div class="survey-question-box"
+      v-for="(item, index) in statisticsResult" :key="item.QuestionnaireId">
+        <div class="survey-question-head">
+          <div class="survey-question-title">
+            {{ (index+1)+'.'+item.Question }}<span>{{ `【${questionTypeList[item.Type-1]}】` }}</span>
+          </div>
+          <template v-if="item.Type==3">
+            <div class="survey-question-more" @click="toTextDetail(item,index+1)">
+              <span>查看详情</span>
+              <i class="el-icon-arrow-right"></i>
+            </div>
+          </template>
+        </div>
+        <div class="survey-question-body" >
+          <div class="survey-question-table"  :style="item.Type==3?'width: 100%':''">
+            <el-table :data="item.Options" header-row-class-name="custom-table-header" 
+            v-if="item.Type!=3">
+              <el-table-column label="选项" prop="Option" show-overflow-tooltip >
+                <template #default="{row}">
+                  {{OptionLetterList[row.Index]+':'}}{{ row.Option }}
+                </template>
+              </el-table-column>
+              <el-table-column label="小计" prop="Count" sortable show-overflow-tooltip width="130">
+                <template #default="{row}">
+                  {{ row.Count }}
+                </template>
+              </el-table-column>
+              <el-table-column label="比例" prop="Percentage" width="80"
+              sortable show-overflow-tooltip>
+                <template #default="{row}">
+                  {{ Math.round(row.Percentage*10000)/100+'%' }}
+                </template>
+              </el-table-column>
+            </el-table>
+            <!-- max-height="420px" -->
+            <el-table :data="item.Answers || []" header-row-class-name="custom-table-header" 
+             v-else  class="textTable">
+              <el-table-column label="序号" prop="index" width="70">
+                <template #default="{$index}">
+                  {{ $index+1 }}
+                </template>
+              </el-table-column>
+              <el-table-column label="姓名" prop="UserName" show-overflow-tooltip width="110">
+                <template #default="{row}">
+                  {{ row.UserName }}
+                </template>
+              </el-table-column>
+              <el-table-column label="文本内容" prop="text"  show-overflow-tooltip>
+                <template #default="{row}">
+                  {{ row.Answer }}
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>          
+          <div class="survey-question-chart" v-if="item.Type!=3">
+            <!-- <questionChart :chart-data="item.chartData" :key="item.QuestionnaireId"></questionChart> -->
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 详细数据 -->
+    <div class="survey-detail-zone" v-show="listType==2">
+      <el-table :data="detailData" border>
+        <el-table-column label="姓名" prop="UserName">
+          <template #default="{row}">
+            {{ row.UserName }}
+          </template>
+        </el-table-column>
+        <el-table-column label="公司名称" prop="CompanyName">
+          <template #default="{row}">
+            {{ row.CompanyName }}
+          </template>
+        </el-table-column>        
+        <el-table-column label="职位" prop="Position">
+          <template #default="{row}">
+            {{ row.Position }}
+          </template>
+        </el-table-column>        
+        <el-table-column label="填写时间" prop="CreateTime">
+          <template #default="{row}">
+            {{ row.CreateTime }}
+          </template>
+        </el-table-column>        
+        <el-table-column label="操作">
+          <template #default="{row}">
+            <span style="color:#409EFF ;cursor: pointer;" @click="questionnaireDetail(row)">查看</span>
+          </template>
+        </el-table-column>
+        <div slot="empty" class="table-noData">
+          <img src="~@/assets/img/cus_m/nodata.png">
+          <span>暂无数据</span>
+        </div>
+      </el-table>
+      <!-- 页数选择器 -->
+      <m-page
+        :page_no="detailDataSearchP.CurrentIndex"
+        :pageSize="detailDataSearchP.PageSize"
+        :total="total"
+        style="position: absolute;right: 60px;bottom: 50px;"
+        @handleCurrentChange="pageChange"
+      />
+    </div> 
+    <el-dialog
+      title="问卷调查"
+      :model-value="questionnaireDetailShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      width="780px"
+      top="5vh"
+    >
+      <div style="padding: 10px 40px 0;" class="questionnaire-container" id="questionnaire-container">
+        <div class="questionnaire-title">
+          {{ detailTitle }}
+        </div>
+        <el-form :model="detailForm" ref="questionRef" label-position="top" size="small" disabled
+        :show-message="false">
+          <el-form-item :label="index+1+'.'+item.Question" :prop="'detail'+item.QuestionnaireId" 
+          :rules="item.IsMust?{required:true,message:'该题为必答题',trigger:['blur','change']}:{}"
+          v-for="(item,index) in questionList" :key="item.QuestionnaireId">
+            <el-radio-group v-model="detailForm['detail'+item.QuestionnaireId]" v-if="item.Type==1">
+              <el-radio :label="it" v-for="(it,ind) in item.Options" :key="it">{{ it }}</el-radio>
+            </el-radio-group>
+            <el-checkbox-group v-model="detailForm['detail'+item.QuestionnaireId]"
+            v-else-if="item.Type==2">
+              <el-checkbox  :label="it" v-for="(it,ind) in item.Options" :key="it" >{{ it }}</el-checkbox>
+            </el-checkbox-group>
+            <el-input v-else rows="5" v-model="detailForm['detail'+item.QuestionnaireId]" type="textarea" placeholder="请输入文本"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+  #questionnaire-survey-container{
+    // height: auto;
+    // min-height: 100%;
+    .survey-operation-zone{
+      background-color: white;
+      padding: 20px 30px;
+      box-sizing: border-box;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      flex-wrap: wrap;
+      border: 1px solid #DCDFE6;
+      border-radius: 4px;
+      margin-bottom: 20px;
+      .survey-operation-left{
+        margin-left: -30px;
+        display: flex;
+        align-items: center;
+      }
+      .survey-operation-right{
+        margin-left: -30px;
+      }
+    }
+    // 统计结果
+    .survey-question-zone{
+      .survey-question-box{
+        box-sizing: border-box;
+        width: calc( (100% - 20px)/2 );
+        background-color: white;
+        border: 1px solid #DCDFE6;
+        border-radius: 4px;
+        margin-bottom: 20px;
+        .survey-question-head{
+          border-bottom: 1px solid #DCDFE6;
+          padding: 20px 30px;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          .survey-question-title{
+            span{
+              color: #999999;
+            }
+          }
+          .survey-question-more{
+            cursor: pointer;
+            color: #409EFF;
+            font-size: 14px;
+            white-space: nowrap;
+          }
+        }
+        .survey-question-body{
+          padding: 20px 30px 30px;
+          box-sizing: border-box;
+          display: flex;
+          flex-wrap: nowrap;
+          justify-content: space-between;
+          .survey-question-table{
+            padding-top: 20px;
+            width: 56%;
+            margin-right: 20px;
+          }
+          .survey-question-chart{
+            height: 340px;
+            width: 44%;
+          }
+        }
+      }
+      @media screen and (max-width:1366px) {
+        .survey-question-box{
+          width: 100%;
+        }
+      }
+      @media screen and (min-width:2880px) {
+        .survey-question-box{
+          width: calc( (100% - 40px)/3 );
+        }
+      }
+      @media screen and (min-width:3840px) {
+        .survey-question-box{
+          width: calc( (100% - 60px)/4 );
+        }
+      }
+    }
+    // 详细数据
+    .survey-detail-zone{
+      background-color: white;
+      min-height: calc(100vh - 212px);
+      box-sizing: border-box;
+      padding: 30px 30px 80px;
+      .table-noData{
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        margin: 18vh 0;
+        img{
+          height: 110px;
+          width: 136px;
+        }
+        span{
+          font-weight: 400;
+          font-size: 16px;
+          color: #999999;
+        }
+      }
+    }
+    // 详情弹窗
+    .questionnaire-container{
+      margin-bottom: 120px;
+    .questionnaire-title{
+      font-weight: 500;
+      font-size: 16px;
+      color: #333333;
+      margin-bottom: 25px;
+    }
+
+  }
+  }
+</style>
+<style lang="scss">
+#questionnaire-survey-container{
+  #survey-question-zone{
+    .el-table{
+      .custom-table-header{
+        th{
+          background-color: white!important;
+          font-weight: 500;
+          padding:7px 0;
+        }
+      }
+      .cell{
+        color: #333333;
+        padding-left: 0;
+        padding-right: 10px;
+      }
+    }
+  }
+  .el-form-item__label{
+    font-weight: 5000;
+    font-size: 16px;
+    color: #333333;
+  }
+  .el-radio__label,.el-checkbox__label,.el-textarea__inner{
+    color: #666666;
+  }
+  .el-checkbox-group,.el-radio-group{
+    margin-left: -40px;
+  }
+  .el-radio,.el-checkbox{
+    margin-right: 0;
+    margin-left: 40px;
+  }
+}
+
+</style>

+ 139 - 0
src/views/custom_manage/etaTrial/textQuestionDetail.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="text-quesiton-container" id="text-quesiton-container">
+    <div class="text-quesiton-header">
+      <div class="text-header-left">
+        {{this.quesitonTitle}}<span style="white-space: nowrap;" >【填空题】</span>
+      </div>
+      <el-button type="primary" @click="toQuestionnaireSurvey" style="margin-left: 30px;">返回统计</el-button>
+    </div>
+    <div class="survey-detail-zone">
+      <el-table :data="detailData" border>
+        <el-table-column label="序号" prop="index" width="60">
+          <template slot-scope="{$index}">
+            {{ (detailDataSearchP.CurrentIndex-1)*detailDataSearchP.PageSize+1+$index }}
+          </template>
+        </el-table-column>
+        <el-table-column label="姓名" prop="UserName" width="85">
+          <template slot-scope="{row}">
+            {{ row.UserName }}
+          </template>
+        </el-table-column>        
+        <el-table-column label="文本内容" prop="Options">
+          <template slot-scope="{row}">
+            {{ row.Options }}
+          </template>
+        </el-table-column>
+        <div slot="empty" class="table-noData">
+          <img src="~@/assets/img/cus_m/nodata.png">
+          <span>暂无数据</span>
+        </div>
+      </el-table>
+      <!-- 页数选择器 -->
+      <m-page
+        :page_no="detailDataSearchP.CurrentIndex"
+        :pageSize="detailDataSearchP.PageSize"
+        :total="total"
+        style="position: absolute;right: 60px;bottom: 50px;"
+        @handleCurrentChange="pageChange"
+      />
+    </div> 
+  </div>
+</template>
+
+<script>
+import mPage from '@/components/mPage.vue';
+import{etaTrialInterence}from '@/api/modules/crmApi.js';
+
+  export default {
+    name:'textQuestionDetail',
+    components:{
+      mPage
+    },
+    data() {
+      return {
+        detailData:[],
+        quesitonTitle:'加载中······',
+        detailDataSearchP:{
+          CurrentIndex:1,
+          PageSize:10,
+          QuestionnaireId:0,
+        },
+        total:0,
+      }
+    },
+    methods:{
+      getDetailList(){
+        etaTrialInterence.getTextDetailList(this.detailDataSearchP).then(res=>{
+          if(res.Ret == 200){
+            this.detailData = res.Data.List || []
+            this.total = res.Data.Paging.Totals || 0
+          }
+        })
+      },
+      toQuestionnaireSurvey(){
+        // this.$router.push('/questionnaireSurvey')
+        this.$router.back()
+      },
+      pageChange(page_no){
+        this.detailDataSearchP.CurrentIndex = page_no
+        this.getDetailList()
+      }
+    },
+    created(){
+      this.quesitonTitle = this.$route.query.question
+      this.detailDataSearchP.QuestionnaireId = +this.$route.query.quesionId || 0
+      this.getDetailList()
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .text-quesiton-container{
+    // height: auto;
+    // min-height: 100%;
+    .text-quesiton-header{
+      background-color: white;
+      padding: 20px 30px;
+      box-sizing: border-box;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      // flex-wrap: wrap;
+      border: 1px solid #DCDFE6;
+      border-radius: 4px;
+      margin-bottom: 20px;
+      .text-header-left{
+        display: flex;
+        align-items: center;
+        color: #333333;
+        font-size: 14px;
+        span{
+          color: #999999;
+        }
+      }
+    }
+    // 详细数据
+    .survey-detail-zone{
+      background-color: white;
+      min-height: calc(100vh - 212px);
+      box-sizing: border-box;
+      padding: 30px 30px 80px;
+      .table-noData{
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        margin: 18vh 0;
+        img{
+          height: 110px;
+          width: 136px;
+        }
+        span{
+          font-weight: 400;
+          font-size: 16px;
+          color: #999999;
+        }
+      }
+    }
+  }
+</style>

+ 218 - 0
src/views/custom_manage/etaTrial/use-config.js

@@ -0,0 +1,218 @@
+import { ref,computed } from 'vue'
+import { useRoute } from 'vue-router'
+
+//ETA试用列表 表格column 和mock数据
+
+//ETA试用列表
+export const etaTrialColumn = [
+  {
+    key:'UserName',
+    label:'姓名'
+  },
+  {
+    key:'CompanyName',
+    label:'公司名称'
+  },{
+    key:'Position',
+    label:'职位',
+    minWidth:'50'
+  },{
+    key:'Seller',
+    label:'申请人',
+    minWidth:'50'
+  },{
+    key:'Expiration',
+    label:'账号到期时长',
+    minWidth:'80'
+  },{
+    key:'ModifyTime',
+    label:'账号更新时间',
+    minWidth:'120'
+  },{
+    key:'LastLoginTime',
+    label:'最近一次登录时间',
+    minWidth:'120'
+  },{
+    key:'LastLoginDuration',
+    label:'最近一次登录时长',
+  },{
+    key:'LoginNum',
+    label:'累计登录次数',
+  },{
+    key:'ActiveTime',
+    label:'累计活跃时长',
+  },{
+    key:'IndexNum',
+    label:'累计添加指标',
+    minWidth:'80'
+  },
+  {
+    key:'ChartNum',
+    label:'累计添加图表',
+    minWidth:'80'
+  },
+  {
+    key:'InterestModule',
+    label:'感兴趣模块',
+    minWidth:'140'
+  }
+]
+
+//管理员-审批列表 非管理员-我的审批
+export const adminApprovalList=[
+  {
+    key:'UserName',
+    label:'姓名'
+  },
+  {
+    key:'CompanyName',
+    label:'公司名称'
+  },{
+    key:'Position',
+    label:'职位',
+    minWidth:'50'
+  },
+  {
+    key:'Mobile',
+    label:'手机号码',
+  },{
+    key:'Seller',
+    label:'申请人',
+    minWidth:'50'
+  },{
+    key:'ModifyTime',
+    label:'提交申请时间',
+    minWidth:'120'
+  },{
+    key:'ApprovalContent',
+    label:'待审内容',
+  },{
+    key:'ApprovalStatus',
+    label:'当前状态',
+  }
+]
+
+//非管理员-账号列表
+export const approvedList=[
+  {
+    key:'UserName',
+    label:'姓名'
+  },
+  {
+    key:'CompanyName',
+    label:'公司名称'
+  },{
+    key:'Position',
+    label:'职位',
+    minWidth:'50'
+  },
+  {
+    key:'Mobile',
+    label:'手机号码',
+  },{
+    key:'Account',
+    label:'账号',
+    minWidth:'50'
+  },{
+    key:'Password',
+    label:'密码',
+    minWidth:'50'
+  },{
+    key:'Enabled',
+    label:'用户状态',
+  },{
+    key:'Expiration',
+    label:'账号到期时长',
+    minWidth:'80'
+  },{
+    key:'ModifyTime',
+    label:'更新时间',
+    minWidth:'80'
+  }
+]
+
+//申请启用审批/申请账号审批
+export const applyApprovalList=[
+  {
+    key:'UserName',
+    label:'姓名'
+  },{
+    key:'CompanyName',
+    label:'公司名称'
+  },{
+    key:'Position',
+    label:'职位'
+  },{
+    key:'Mobile',
+    label:'手机号'
+  },{
+    key:'Seller',
+    label:'申请人'
+  }]
+
+//审批列表-当前状态
+/* export const ApprovalStatus = {
+  0:'未审批',
+  1:'已审批',
+  2:'已撤回',
+  3:'已驳回'
+} */
+export const ApprovalStatus={
+  '待审批':0,
+  '已审批':1,
+  '已撤回':2,
+  '驳回':3
+}
+//启用,账号重复提示弹窗
+export const textMap = {
+  0:'该用户账号已禁用,是否申请启用',
+  1:'该用户信息已提交申请 ,请勿重复提交'
+}
+
+export function useConfig() {
+  const route = useRoute()
+
+  const columnList = ref([])
+  const canEdit = ref(false)
+  const listType = sessionStorage.getItem('etaApprovalListType')||'all';
+
+  const Role = computed(() => {
+    return localStorage.getItem("Role");
+  })
+
+  let interenceName = ''
+  if(route.path === '/etaTrialList') {
+    columnList.value = etaTrialColumn
+    canEdit.value = false
+    interenceName = 'getETATrialList'
+
+  }else if(route.path === '/etaApprovalList') {
+    canEdit.value = true
+
+    //管理员-审批列表
+    if(Role.value.includes('admin')){
+      columnList.value = adminApprovalList
+      interenceName='getAdminApprovalList'
+    }
+    //非管理员-我的审批
+    if(!Role.value.includes('admin')&&listType==='all'){
+      columnList.value = adminApprovalList
+      interenceName='getApprovalAllList'
+    }
+    //非管理员-账号列表
+    if(!Role.value.includes('admin')&&listType!=='all'){
+      columnList.value = approvedList
+      interenceName='getApprovalList'
+    }
+  }
+
+
+  return {
+    columnList,
+    canEdit,
+    listType,
+    interenceName,
+    ApprovalStatus
+  }
+
+}

+ 6 - 6
src/views/custom_manage/officalTrial/trialApplication.vue

@@ -122,11 +122,11 @@ const columns = computed(() => {
 		<div class="trialApplication_cont">
 			<!-- 中文官网 -->
 			<el-table
-			ref="table"
-			:data="tableData"
-			v-loading="isShowloadding"
-			element-loading-text="数据加载中..."
-			border
+        ref="table"
+        :data="tableData"
+        v-loading="isShowloadding"
+        element-loading-text="数据加载中..."
+        border
 			>
 			<el-table-column
 				:label="item.label"
@@ -135,7 +135,7 @@ const columns = computed(() => {
         :key="item.key"
 			>
 				<template #default="scope">
-          <span v-if="key==='Phone'">{{scope.row[item.key] || scope.row.Email}}</span>
+          <span v-if="item.key==='Phone'">{{scope.row[item.key] || scope.row.Email}}</span>
           
 					<span v-else>{{ scope.row[item.key] }}</span>
 				</template>

+ 10 - 0
src/views/custom_manage/overseas/overseasCustomList.vue

@@ -0,0 +1,10 @@
+<script setup>
+import { ref } from 'vue'
+
+</script>
+<template>
+  <div></div>
+</template>
+<style scoped lang="scss">
+
+</style>

+ 263 - 0
src/views/custom_manage/overseas/overseasCustomRoadshow.vue

@@ -0,0 +1,263 @@
+<script setup>
+import { reactive, ref,toRefs } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+import { overseasCustomInterence } from '@/api/modules/overseasCustom.js'
+import { useRouter } from 'vue-router'
+import DatePicker from 'vue-datepicker-next';
+import mPage from '@/components/mPage.vue'
+
+const $router = useRouter()
+
+const filterObj = reactive({
+  sales: [],
+  researchers: [],
+  date:[],
+  searchWord: '',
+  status:[],
+  sortParams: {
+    SortField: '',
+    SortDesc: 1,
+  },
+})
+
+const statusOptions = [
+  { label: '正式', val: '正式' },
+  { label: '试用', val: '试用' },
+  { label: '关闭', val: '关闭' },
+]
+
+//获取销售列表
+const salesOptions = ref([])
+async function getSellerList() {
+  const res = await overseasCustomInterence.getOverseasRoadShowUsers({AdminType:'seller'})
+
+  if (res.Ret === 200) {
+    salesOptions.value = res.Data || [];
+  }
+}
+getSellerList()
+
+//获取研究员列表
+const researchersOptions = ref([])
+async function getResearchersList() {
+  // 发送请求
+  const res = await overseasCustomInterence.getOverseasRoadShowUsers({AdminType:'researcher'});
+  if (res.Ret === 200) {
+    researchersOptions.value = res.Data || [];
+  }
+}
+getResearchersList()
+
+
+const tabeLoading = ref(false)
+const tableData = ref([])
+const total = ref(0)
+const currentIndex = ref(1)
+const pageSize = ref(10)
+const columns = [
+  {  label: "客户名称",key: 'CompanyName',minWidth:200 },
+  {  label: "状态",key: 'CompanyStatus' },
+  {  label: "路演日期",key: 'StartDate',sortable:true },
+  {  label: "路演形式",key: 'RoadshowType' },
+  {  label: "路演平台/路演城市",key: 'RoadshowPlatform' },
+  {  label: "研究员",key: 'ResearcherName' },
+  {  label: "对接销售",key: 'SellerName' },
+]
+async function getTableData() {
+  tabeLoading.value = true;
+  let params = {
+    ResearcherId: filterObj.researchers.join(','),
+    SellerId: filterObj.sales.join(','),
+    StartDate: filterObj.date[0]||'',
+    EndDate: filterObj.date[1]||'',
+    CompanyStatus: filterObj.status.join(','),
+    CurrentIndex: currentIndex.value,
+    PageSize: pageSize.value,
+    Keyword: filterObj.searchWord,
+    ...filterObj.sortParams
+  }
+  const res = await overseasCustomInterence.getOverseasRoadShowList(params)
+
+  tabeLoading.value = false;
+
+  if(res.Ret!==200) return
+
+  tableData.value = res.Data.List || [];
+
+  total.value = res.Data.Paging.Totals;
+}
+getTableData()
+
+function pageChange(page) {
+  currentIndex.value = page;
+  getTableData()
+}
+
+function handleSortChange({prop,order}) {
+  filterObj.sortParams = {
+    SortField: order?prop:'',
+    SortDesc: order==='ascending'?2:1
+  }
+
+  pageChange(1)
+}
+
+function toCustomDetail(data) {
+  const path = data.Source===1?'/detailCustomEn':'/customDetail'
+  const query = {
+      ...data.Source===1?{
+        companyId:data.CompanyId - 10000000
+      }:{
+          id:data.CompanyId
+      },
+      from:'overseas'
+  }
+  const href = $router.resolve({path,query}).href
+  window.open(href,"_blank")
+}
+
+const {sales,researchers,date,searchWord,status} = toRefs(filterObj)
+</script>
+<template>
+  <div class="overseas-custom-roadshow-box">
+    <div class="top-wrapper">
+      <div class="left-select">
+        <el-cascader
+            v-model="sales"
+            :options="salesOptions"
+            :show-all-levels="false"
+            :props="{
+                expandTrigger: 'hover',
+                children: 'ChildrenList',
+                emitPath: false,
+                label:'RealName',
+                value:'AdminId',
+                multiple:true
+            }"
+            collapse-tags
+            filterable
+            clearable
+            placeholder="请选择销售"
+            @change="pageChange(1)"
+        />
+        <el-cascader
+            v-model="researchers"
+            :options="researchersOptions"
+            :show-all-levels="false"
+            :props="{
+                expandTrigger: 'hover',
+                children: 'ResearcherList',
+                emitPath: false,
+                label:'RealName',
+                value:'AdminId',
+                multiple:true
+            }"
+            collapse-tags
+            filterable
+            clearable
+            placeholder="请选择研究员"
+            @change="pageChange(1)"
+        />
+
+        <el-select 
+          v-model="status" 
+          placeholder="请选择客户状态"
+          @change="pageChange(1)"
+          multiple 
+          collapse-tags 
+          clearable
+        >
+            <el-option
+                v-for="item in statusOptions"
+                :key="item.val"
+                :label="item.label"
+                :value="item.val"
+            />
+        </el-select>
+
+        <date-picker 
+						v-model:value="date" 
+						type="date" 
+						range
+						value-type="format"
+						placeholder="请选择路演日期" 
+						@change="pageChange(1)" 
+						style="width:200px;"
+        />
+
+      </div>
+      <el-input 
+          :prefix-icon="Search" 
+          placeholder="客户名称" 
+          style="width:317px;" clearable
+          v-model="searchWord"
+          @input="pageChange(1)"
+      />
+    </div>
+
+    <div class="cont-wrapper">
+      <el-table
+        ref="tableRef"
+        :data="tableData"
+        :loading="tabeLoading"
+        border
+        @sort-change="handleSortChange"
+      >
+        <el-table-column 
+            align="center"
+            v-for="item in columns" :key="item.key"
+            :prop="item.key" :label="item.label"
+            :min-width="item.minWidth"
+            :sortable="item.sortable?'custom':false"
+        >
+          <template #default="{row}">
+              <!-- 客户名称 -->
+              <div v-if="item.key==='CompanyName'" class="editor" @click="toCustomDetail(row)">{{row[item.key]}}</div>
+
+              <span v-else>{{row[item.key]}}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+
+       <!-- 页数选择器 -->
+      <m-page
+        :page_no="currentIndex"
+        :pageSize="pageSize"
+        :total="total"
+        style="position: absolute;right: 50px;bottom: 50px;"
+        @handleCurrentChange="pageChange"
+      />
+    </div>
+  </div>
+</template>
+<style scoped lang="scss">
+.overseas-custom-roadshow-box {
+    height: calc(100vh - 110px);
+    background-color: white;
+    border: 1px solid #ECECEC;
+    border-radius: 2px;
+    box-sizing: border-box;
+    padding: 20px 30px 30px 30px;
+    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+
+    .top-wrapper {
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: space-between;
+      .left-select {
+        display: flex;
+        gap: 5px;
+      }
+    }
+    .cont-wrapper {
+      margin-top: 20px;
+    }
+    .editor{
+        color:#409EFF;
+        cursor: pointer;
+        &:hover{
+            text-decoration: underline;
+        }
+    }
+}
+</style>

+ 0 - 1
src/views/dashboard_manage/components/chart.vue

@@ -13,7 +13,6 @@ const props = defineProps({
 
 const chart = ref(null)
 function initChart() {
-  console.log('initchart')
   nextTick(() => {
     chart.value = Highcharts.chart(props.id,props.options) 
   })