Sfoglia il codice sorgente

Merge branch 'master' of http://8.136.199.33:3000/Karsa/crm_front_v3 into cxmo_branch

cxmo 1 anno fa
parent
commit
5ea13aaec3

+ 2 - 0
package.json

@@ -17,11 +17,13 @@
     "element-plus": "2.4.4",
     "froala-editor": "^4.1.4",
     "highcharts": "11.2.0",
+    "html2canvas": "1.1.4",
     "jquery": "^3.7.1",
     "js-md5": "^0.8.3",
     "lodash": "^4.17.21",
     "moment": "^2.30.1",
     "pinia": "^2.1.7",
+    "v-distpicker": "2.1.0",
     "vue": "^3.4.19",
     "vue-froala-wysiwyg": "^4.1.4",
     "vue-router": "^4.3.0",

+ 32 - 0
src/hooks/mixins/index.js

@@ -0,0 +1,32 @@
+// 从原项目的 @/mixins/index.js 提取出来(有用到的)
+
+import http from "@/api/http";
+// 显示时间为 2021.07.22 
+export function formatter(row,column,cellvalue){
+  return cellvalue ? http.dateFormatter(cellvalue) : '';
+}
+
+// 显示时间为 2021.07.22 
+export function formatTime(time){
+  if(time==='0001-01-01 00:00:00'||time==='0000-00-00 00:00:00'){
+    return '--'
+  }else{
+    return time.replace(/-/g,'.')
+  }
+}
+
+//  格式化金额 三位一个逗号
+export function formatPriceHasPoint(e){
+  let str=e.toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  return num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}

+ 5 - 0
src/main.js

@@ -26,11 +26,16 @@ import 'froala-editor/css/froala_style.min.css';
 
 import VueFroala from 'vue-froala-wysiwyg';
 
+import {registerComponents} from "@/utils/registryComponents"
+
 function setupApp() {
   const app = createApp(App)
   initStore(app)
 
   initRouter(app)
+  
+  // 注册全局组件
+  registerComponents(app)
 
   app.use(ElementPlus,{locale: zhCn})
   app.use(vue3TreeOrg)

+ 163 - 0
src/router/modules/contractRoutes.js

@@ -0,0 +1,163 @@
+//合同管理路由模块
+import Home from '@/layouts/index.vue'
+
+export default [
+    {
+		path: '/',
+		component: Home,
+		name: 'contractManage',
+		meta: {
+			title: "合同管理",
+		},
+		icon_path: require('@/assets/img/home/custom_ico.png'),
+		children: [
+			{
+				path: 'contractmanagelist',
+				name: 'contractmanagelist',
+				component: () => import('@/views/contract_manage/contractList.vue'),
+				meta: {
+					title: "合同列表",
+				},
+			},
+			{
+				path: 'addcontract',
+				component: () => import('@/views/contract_manage/addContract.vue'),
+				name: "addcontract",
+				meta: {
+					title: "添加合同",
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+				}
+			},
+			{
+				path: 'contractdetail',
+				component: () => import('@/views/contract_manage/contractDetail.vue'),
+				name: "contractdetail",
+				meta: {
+					title:'合同详情',
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表'
+				}
+			},
+			{
+				path: 'editcontract',
+				component: () => import('@/views/contract_manage/editContract.vue'),
+				name: "editcontract",
+				hidden: false,
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title:'编辑合同'
+				}
+			},
+			{
+				path: 'retrialcontract',
+				component: () => import('@/views/contract_manage/editContract.vue'),
+				name: "retrialcontract",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title:"重审合同"
+				}
+			},
+			{
+				path: 'copycontract',
+				component: () => import('@/views/contract_manage/editContract.vue'),
+				name: "copycontract",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title:'复制模板'
+				}
+			},
+			{
+				path: 'modifyapply',
+				component: () => import('@/views/contract_manage/editContract.vue'),
+				name: "modifyapply",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title:'修改重审'
+				}
+			},
+			{
+				path: 'addcontractdc',
+				component: () => import('@/views/contract_manage/addContractDC.vue'),
+				name: "addcontractdc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '添加代付合同'
+				}
+			},
+			{
+				path: 'contractdetaildc',
+				component: () => import('@/views/contract_manage/contractDetailDC.vue'),
+				name: "contractdetaildc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '代付合同详情'
+				}
+			},
+			{
+				path: 'editcontractdc',
+				component: () => import('@/views/contract_manage/editContractDC.vue'),
+				name: "editcontractdc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '代付合同编辑'
+				}
+			},
+			{
+				path: 'retrialcontractdc',
+				component: () => import('@/views/contract_manage/editContractDC.vue'),
+				name: "retrialcontractdc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '代付合同重审'
+				}
+			},
+			{
+				path: 'copycontractdc',
+				component: () => import('@/views/contract_manage/editContractDC.vue'),
+				name: "copycontractdc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '代付合同复制模板'
+				}
+			},
+			{
+				path: 'modifyapplydc',
+				component: () => import('@/views/contract_manage/editContractDC.vue'),
+				name: "modifyapplydc",
+				meta: {
+					pathFrom: 'contractmanagelist',
+					pathName: '合同列表',
+					title: '代付合同修改重审'
+				}
+			},
+			// {
+			// 	path:"raiAllocationPage",
+			// 	component:()=>import("@/views/contract_manage/raiAllocationPage.vue"),
+			// 	name:"合同派点",
+			// 	hidden:false,
+			// 	meta: {
+			// 		keepAlive: false
+			// 	}
+			// },
+			// {
+			// 	path:"researcherStatistics",
+			// 	component:()=>import("@/views/contract_manage/researcherStatistics.vue"),
+			// 	name:"研究员派点统计",
+			// 	hidden:false,
+			// 	meta: {
+			// 		keepAlive: false
+			// 	}
+			// }
+		]
+	},
+]

+ 38 - 38
src/router/modules/ficcXcxRoutes.js

@@ -42,60 +42,60 @@ export default [
 					keepAlive: false
 				}
 			},
-            // {
-			// 	path:"questionManage",
-			// 	name:"问答社区",
-			// 	component:()=>import('@/views/interaction_manage/questionManage.vue')
-			// },
+            {
+				path:"questionManage",
+				name:"问答社区",
+				component:()=>import('@/views/interaction_manage/questionManage.vue')
+			},
             // {
 			// 	path: "messageboard",
 			// 	name: "留言板",
 			// 	component: () => import('@/views/interactive_manage/messageboard.vue')
 			// },
-			// {
-			// 	path: "/ybComment",
-			// 	name: "留言管理",
-			// 	component: () => import('@/views/interactive_manage/messageManage.vue')
-			// },
-			// {
-			// 	path: "/likelist",
-			// 	name: "点赞",
-			// 	component: () => import('@/views/interactive_manage/likelist.vue')
-			// },
+			{
+				path: "/ybComment",
+				name: "留言管理",
+				component: () => import('@/views/interactive_manage/messageManage.vue')
+			},
+			{
+				path: "/likelist",
+				name: "点赞",
+				component: () => import('@/views/interactive_manage/likelist.vue')
+			},
             // {
 			// 	path:"questionComment",
 			// 	name:"问答评论",
 			// 	component:()=>import('@/views/interactive_manage/questionComment.vue')
 			// },
-            // {
-			// 	path:"bulletChatList",
-			// 	name:"弹幕管理",
-			// 	component:()=>import('@/views/interactive_manage/bulletChatList.vue')
-			// },
-            // {
-			// 	path:'voicePlayStatistics',
-			// 	component: () => import('@/views/dataReport_manage/voicePlayList.vue'),
-			// 	name: '语音播报统计',
-			// 	hidden: false
-			// },
-            // {
-			// 	path:'reportlabel',
-			// 	component:()=> import('@/views/report_manage/tagLib.vue'),
-			// 	name:'标签库',
-			// 	hidden:true
-			// },
+            {
+				path:"bulletChatList",
+				name:"弹幕管理",
+				component:()=>import('@/views/interactive_manage/bulletChatList.vue')
+			},
+            {
+				path:'voicePlayStatistics',
+				component: () => import('@/views/dataReport_manage/voicePlayList.vue'),
+				name: '语音播报统计',
+				hidden: false
+			},
+            {
+				path:'reportlabel',
+				component:()=> import('@/views/report_manage/tagLib.vue'),
+				name:'标签库',
+				hidden:true
+			},
 			// {
 			// 	path:'sendlog',
 			// 	component:()=> import('@/views/report_manage/reportEn/sendMsgLog.vue'),
 			// 	name:'群发日志',
 			// 	hidden:true
 			// },
-			// {
-			// 	path:'bannerStatistics',
-			// 	component:()=> import('@/views/interaction_manage/bannerStatistics.vue'),
-			// 	name:'banner 统计',
-			// 	hidden:true
-			// },
+			{
+				path:'bannerStatistics',
+				component:()=> import('@/views/interaction_manage/bannerStatistics.vue'),
+				name:'banner 统计',
+				hidden:true
+			},
 
         ]
     }

+ 35 - 31
src/utils/registryComponents.js

@@ -1,32 +1,36 @@
-import Vue from 'vue';
-
-//地区选择
 import VDistpicker from 'v-distpicker'
-Vue.component('v-distpicker', VDistpicker)
-
-//日期选择
-import DatePicker from 'vue2-datepicker';
-import 'vue2-datepicker/index.css';
-import 'vue2-datepicker/locale/zh-cn';
-Vue.component('date-picker',DatePicker)
-
-//指标搜索浮动显示详情popover
-import edbDetailPopover from '@/components/edbDetailPopover.vue';
-Vue.component('edbDetailPopover',edbDetailPopover)
-
-//指标添加 单位
-import selectUnit from '@/components/selectUnit.vue'
-Vue.component('selectUnit',selectUnit)
-
-//查看指标历史
-import edbHistoryDialog from '@/components/edbHistoryDialog.vue';
-Vue.component('edbHistoryDialog',edbHistoryDialog);
-
-/* 很多图表模块列表页基本类似 慢慢抽离替换 */
-//列表
-import chartListWrap from '@/components/chart/chartListWrap.vue'
-Vue.component('chartListWrap',chartListWrap)
-
-//详情图表操作
-import chartDetailHandlesWrap from '@/components/chart/chartDetailHandlesWrap.vue'
-Vue.component('chartHandlesWrap',chartDetailHandlesWrap)
+
+// import DatePicker from 'vue2-datepicker';
+// import 'vue2-datepicker/index.css';
+// import 'vue2-datepicker/locale/zh-cn';
+
+// import edbDetailPopover from '@/components/edbDetailPopover.vue';
+// import selectUnit from '@/components/selectUnit.vue'
+// import edbHistoryDialog from '@/components/edbHistoryDialog.vue';
+// import chartListWrap from '@/components/chart/chartListWrap.vue'
+// import chartDetailHandlesWrap from '@/components/chart/chartDetailHandlesWrap.vue'
+
+export function registerComponents(app){
+  //地区选择
+  app.component('v-distpicker', VDistpicker)
+
+  //日期选择
+
+  // app.component('date-picker',DatePicker)
+
+  //指标搜索浮动显示详情popover
+  // app.component('edbDetailPopover',edbDetailPopover)
+
+  //指标添加 单位
+  // app.component('selectUnit',selectUnit)
+
+  //查看指标历史
+  // app.component('edbHistoryDialog',edbHistoryDialog);
+
+  /* 很多图表模块列表页基本类似 慢慢抽离替换 */
+  //列表
+  // app.component('chartListWrap',chartListWrap)
+
+  //详情图表操作
+  // app.component('chartHandlesWrap',chartDetailHandlesWrap)
+}

+ 986 - 0
src/views/contract_manage/addContract.vue

@@ -0,0 +1,986 @@
+
+<script setup>
+import { ref,reactive,computed,watch } from "vue";
+import {ElMessage,ElMessageBox} from "element-plus"
+import { useRouter} from 'vue-router';
+import _ from 'lodash'
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import { contractInterface,customInterence } from "@/api/api.js";
+import{province_sorce,city_sorce} from '@/utils/distpicker';
+import ServiceDialog from "./components/ServiceDialog.vue"; //套餐内容弹窗组件
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import {CalculationDate} from '@/utils/CalculationDate.js'
+
+const $router = useRouter()
+
+const province_sorce_value=province_sorce
+const city_sorce_value=city_sorce
+
+const contractTypeDisable1=ref(false) //能否选择新签合同
+const contractTypeDisable2=ref(false) //能否选择续约合同
+const contractTypeDisable3=ref(false) //能否选择补充协议
+const timeDisable=ref(false)
+const cusLastcontract=ref(null)
+const temContractCode=ref(null)//临时生成的合同编号
+const RoleType=ref(localStorage.getItem("Role") || "")
+const radio=ref('2')//是否有补充内容 1有 2 无
+const formData=reactive({
+  constractType: null, //合同类型
+  time: [],
+  price: "",
+  price2: "",
+  payRemark: "",
+  customeName: "",
+  customeXYM: "",
+  customeCz: "",
+  customeTel: "",
+  postCode: "",
+  province: "",
+  city: "",
+  address: "",
+  remark: "",
+  sellerRemark:'',
+  PayChannel:['无']
+})
+const formRule={
+  constractType: [{ required: true, message: "请选择合同类型", trigger: "change" }],
+  time: [{ required: true, message: "请选择有效期限", trigger: "change" }],
+  price: [
+    { required: true, message: "请填写合同金额", trigger: "blur" },
+    // { pattern: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/, message: "请输入正确的格式,可保留两位小数" },
+  ],
+  payRemark: [{ required: true, message: "请填写付款方式说明", trigger: "blur" }],
+  customeName: [{ required: true, message: "请填写名称", trigger: "blur" }],
+  customeXYM: [{ required: true, message: "请填写信用码", trigger: "blur" }],
+  province: [{ required: true, message: "请选择地址", trigger: "change" }],
+  remark: [{ required: false, message: "请填写补充说明", trigger: "blur" }],
+  PayChannel:[{ required: true, message: "请填写付款方", trigger: "change" }],
+  customeTel:[{ required: true, message: "请填写电话", trigger: "change" }],
+  address:[{ required: true, message: "请填写详细地址", trigger: "change" }],
+}
+const ficcServiceData=ref([])//ficc服务内容
+const qyServiceData=ref([])//权益服务内容
+const serviceShow=ref(false)
+const serviceCon=ref({})//选择的服务项 查看报价单/选择品种
+const selectServiceData=ref([])
+const editValue=ref({})//修改小套餐的value
+const PayChannelOptions=ref([])//搜索出付款方列表
+
+const pickerOptions=computed(()=>{
+  let obj={disabledDate() {return false}}
+  if(cusLastcontract.value){
+    if(formData.constractType==='续约合同'&&RoleType.value==='ficc_seller'){
+      // ficc 起始日期为权限中最大的时间
+      formData.time=[]
+      let timearr=cusLastcontract.value.map(item=> item.EndDate)
+      let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+      let endDate=formatDate(new Date(maxTime))
+      console.log(endDate);
+      obj={
+        disabledDate(time) {
+          return time.getTime() < new Date(endDate).getTime();
+        }
+      }
+    }else{
+      obj={
+        disabledDate() {
+            return false
+          }
+      }
+    }
+    if(formData.constractType==='补充协议'){
+      let today=formatDate(new Date())
+      // 找出权限中最晚截止的那个作为截至日期
+      let timearr=cusLastcontract.value.map(item=> item.EndDate)
+      let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+      let endDate=formatDate(new Date(maxTime))
+      formData.time=[today,endDate]
+      timeDisable.value=true
+      obj={disabledDate() {return true}}
+    }else{
+      timeDisable.value=false
+    }
+  }
+  
+  return obj
+})
+
+const formItemRemark=ref(null)
+
+watch(radio,(nval, oval)=>{
+  formItemRemark.value.resetField();
+  if (nval === "1") {
+    formRule.remark[0].required = true;
+  } else {
+    formRule.remark[0].required = false;
+  }
+})
+
+// filters
+// 金额转中文大写
+const digitUppercase=(n)=>{
+  if (n) {
+    n=n.replace(/,/g,'')
+    let fraction = ["角", "分"];
+    let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+    let unit = [
+      ["元", "万", "亿"],
+      ["", "拾", "佰", "仟"],
+    ];
+    let head = n < 0 ? "欠" : "";
+    n = Math.abs(n);
+    let s = "";
+    for (let i = 0; i < fraction.length; i++) {
+      s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+    }
+    // s = s || "整";
+
+    n = Math.floor(n);
+    for (let i = 0; i < unit[0].length && n > 0; i++) {
+      let p = "";
+      for (let j = 0; j < unit[1].length && n > 0; j++) {
+        p = digit[n % 10] + unit[1][j] + p;
+        n = Math.floor(n / 10);
+      }
+      s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+    }
+    return (
+      "大写:" + (head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零"))
+      // .replace(/^整$/, "零元整")
+    );
+  }
+}
+
+//计算多少年 X年X个月X天
+const formateYear=(e)=>{
+  if (!e) return;
+  if (e[0]) {
+    return `有效期为${CalculationDate(e[0],e[1])}`
+  }
+}
+
+// 格式化时间
+const formatDate=(date)=>{
+  let year=date.getFullYear()
+  let month=date.getMonth()+1
+  let day=date.getDate()
+  return `${year}-${month<10?'0'+month:month}-${day<10?'0'+day:day}`
+}
+
+// 社会信用码变更
+const getContractByCode=async ()=>{
+  contractTypeDisable1.value=false
+  contractTypeDisable2.value=false
+  contractTypeDisable3.value=false
+  if(!formData.customeXYM) return
+  const res=await contractInterface.getContractTypeByCode({CreditCode:formData.customeXYM})
+  if(res.Ret==200){
+    let customeId=res.Data.Item&&res.Data.Item.CompanyId
+    if(customeId){
+      const res2=await customInterence.applyTurnContractType({CompanyId:customeId}) 
+      formData.constractType=res2.Data.ContractType
+      if(res2.Data.ContractType==='新签合同'){
+        contractTypeDisable1.value=false
+        contractTypeDisable2.value=true
+        contractTypeDisable3.value=true
+      }else{
+        if(RoleType.value==='ficc_seller'){
+          contractTypeDisable1.value=true
+          contractTypeDisable2.value=false
+          contractTypeDisable3.value=false
+        }
+        if(RoleType.value==='rai_seller'){
+          contractTypeDisable1.value=true
+          contractTypeDisable2.value=false
+          contractTypeDisable3.value=true
+        }
+      }
+    }
+  }
+}
+
+
+// 解除搜索选中的客户绑定(需求修改不需要此操作2021-9-16)
+const handleUnbind=()=>{
+  ElMessageBox.confirm('此操作将解除与选择的客户绑定, 是否继续?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    contractTypeDisable1.value=false
+    contractTypeDisable2.value=false
+    contractTypeDisable3.value=false
+    cusLastcontract.value=null
+    timeDisable.value=false
+  }).catch(()=>{
+    console.log('取消解绑');
+  })
+}
+
+// 客户名称搜索框输入变化
+const handleCustomeInputChange=()=>{
+  // 将付款方初始化为客户名称
+  // const flag=this.formData.PayChannel.includes(this.formData.customeName)
+  // if(!flag){
+  //   this.formData.PayChannel.push(this.formData.customeName)
+  // }
+}
+
+// 搜索付款渠道
+// async querySearchAsyncPayChannel(queryString, cb){
+//   cb([])
+//   // if(!queryString) return
+//   let res=await contractInterface.getPayChannel({Keyword:queryString})
+//   if(res.Ret===200){
+//     if(res.Data&&res.Data.length>0){
+//       let arr=res.Data.map(item=>{
+//         return {value:item.PayChannel,...item}
+//       })
+//       cb(arr)
+//     }
+//   }
+// },
+
+// 付款渠道方式修改 多选
+const querySearchAsyncPayChannel=async (query,type)=>{
+  if(!type&&!query) return
+  let res=await contractInterface.getPayChannel({Keyword:query})
+  if(res.Ret===200){
+    PayChannelOptions.value=res.Data||[]
+  }
+}
+
+// 客户搜索
+const querySearchAsyncCustome=async (queryString, cb)=>{
+  cb([])
+  if(!queryString) return
+  let CompanyType=localStorage.getItem('RoleType')
+  let res=await customInterence.customList({KeyWord:queryString,PageSize:10000,CompanyType:CompanyType,IncludeShare:true})
+  // this.contractTypeDisable1=false
+  // this.contractTypeDisable2=false
+  // this.contractTypeDisable3=false
+  // // this.pickerOptions={}
+  // this.cusLastcontract=null
+  // this.timeDisable=false
+  if(res.Ret===200){
+    if(res.Data.List.length>0){
+      let arr=res.Data.List.map(item=>{
+        return {value:item.CompanyName,...item}
+      })
+      cb(arr)
+    }
+  }
+}
+
+//选择客户
+const handleSelectCustome=async (item)=>{
+  // 获取客户详情 更新表单数据
+  let res=await customInterence.customDetail({CompanyId:item.CompanyId})
+  if(res.Ret===200){
+    formData.customeXYM=res.Data.Item.CreditCode
+    formData.province=res.Data.Item.Province
+    formData.city=res.Data.Item.City
+  }
+  // 获取以前的合同自动 填充部分信息
+  let res4=await contractInterface.getHistoryContract({CompanyId:item.CompanyId})
+  if(res4.Ret===200){
+    formData.address=res4.Data.Address||''
+    formData.customeCz=res4.Data.Fax||''
+    formData.customeTel=res4.Data.Phone||''
+    formData.postCode=res4.Data.Postcode||''
+  }
+  // 更新合同类型
+  let res2=await customInterence.applyTurnContractType({CompanyId:item.CompanyId})
+  if(res2.Ret===200){
+    formData.constractType=res2.Data.ContractType
+    if(res2.Data.ContractType==='新签合同'){
+      contractTypeDisable1.value=false
+      contractTypeDisable2.value=true
+      contractTypeDisable3.value=true
+    }else{
+      if(RoleType.value==='ficc_seller'){
+        contractTypeDisable1.value=true
+        contractTypeDisable2.value=false
+        contractTypeDisable3.value=false
+      }
+      if(RoleType.value==='rai_seller'){
+        contractTypeDisable1.value=true
+        contractTypeDisable2.value=false
+        contractTypeDisable3.value=true
+      }
+    }
+  }
+
+  // 获取客户大于今天的最后一份有效合同详情
+  let res3=await customInterence.lastContractInfo({CompanyId:item.CompanyId})
+  if(res3.Ret===200){
+    if(res3.Data&&res3.Data.length>0){
+      cusLastcontract.value=res3.Data
+    }else{
+      cusLastcontract.value=null
+    }
+  }
+}
+
+
+//判断合同期限 不小于一个月
+const validateTime=()=>{
+  if(formData.constractType==='补充协议') return true
+  let start = new Date(formData.time[0]).getTime();
+  let end = new Date(formData.time[1]).getTime();
+  let oneMonth = 1000 * 60 * 60 * 24 * 29;
+  if (end - start >= oneMonth) {
+    return true;
+  } else {
+    ElMessage.warning("合同期限不能小于一个月");
+    return false;
+  }
+}
+
+//判断优惠后金额不能大于合同金额
+const validatePrice=()=>{
+  if (Number(formData.price2) > Number(formData.price)) {
+    ElMessage.warning("优惠后金额不得大于合同金额");
+    return false;
+  } else {
+    return true;
+  }
+}
+
+// 判断小套餐中是否选择品种/新增的行是否有数据
+const validateService=(e,data)=>{
+  if(!e.Value){
+    ElMessage.warning("请保存小套餐");
+    return false
+  }
+  //判断品种列是否有值
+  let arr = [];
+  data.forEach((item) => {
+    item.forEach((item2) => {
+      if (item2.HeadName === "品种") {
+        arr.push(...item2.ValueId);
+      }
+    });
+  });
+  // 判断行是否有值
+  let arr2 = [];
+  data.forEach((item, index) => {
+    let arr = item.filter((item2) => {
+      if (!item2.Value) return item2;
+    });
+    if (arr.length === item.length) {
+      arr2.push(index);
+    }
+  });
+
+  if (!arr.length&&formData.constractType!=='补充协议') {
+    ElMessage.warning("至少选择一个品种");
+    return false;
+  }
+
+  if (arr2.length) {
+    ElMessage.warning("行内至少填一项");
+    return false;
+  }
+
+  return true;
+}
+
+// 生成临时的合同编号
+const createTemContractCode=()=>{
+  let time = new Date();
+  let year = time.getFullYear();
+  let month = time.getMonth() + 1 < 10 ? "0" + (time.getMonth() + 1) : time.getMonth() + 1;
+  let day = time.getDate() < 10 ? "0" + time.getDate() : time.getDate();
+  let timeStr = `${year}${month}${day}`;
+  let random = parseInt(Math.random() * 1000);
+  if (random < 10) {
+    random = `00${random}`;
+  }
+  if (random < 100) {
+    random = `0${random}`;
+  }
+  let typeStr = RoleType.value === "ficc_seller" ? "FICC" : "EQ";
+  temContractCode.value = `HZ${typeStr}${timeStr}${random}`;
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+  let ProductId = RoleType.value === "ficc_seller" ? 1 : 2;
+  contractInterface.getServiceList({ ProductId }).then((res) => {
+    if (res.Ret === 200) {
+      if (RoleType.value === "ficc_seller") {
+        ficcServiceData.value = res.Data;
+      } else {
+        qyServiceData.value = res.Data;
+      }
+    }
+  });
+}
+
+//显示服务内容弹窗
+const handleShowService=(e)=>{
+  serviceCon.value = e;
+  serviceShow.value = true;
+}
+
+//关闭服务内容弹窗
+const serviceClose=()=>{
+  serviceShow.value = false;
+}
+
+//选地地区时 选则的省份改变重置city为空
+const provinceChange=(e)=>{
+  formData.province =e.value=='省'?'':e.value;
+  // this.formData.city=''
+}
+
+//选择地区
+const selectRegion=(e)=>{
+  formData.province =e.province.value=='省'?'':e.province.value;
+  formData.city = e.city.value=='市'?'':e.city.value;
+}
+
+//小套餐点击保存 更新数据
+const serviceSave=(e)=>{
+  editValue.value = { ServiceTemplateId: e.ServiceTemplateId, Value: e.Value, tableData: e.tableData, tableHeadData: e.tableHeadData };
+
+  // this.ficcServiceData.forEach((item) => {
+  //   if (item.ServiceTemplateId === e.ServiceTemplateId) {
+  //     item.Value = e.Value;
+  //   }
+  // });
+
+  // this.$refs.FiccServiceTable.tableData.forEach(item=>{
+  //   if(item.selected){
+  //     this.selectServiceData.push(item)
+  //   }
+  // })
+  // this.selectServiceData.forEach((item) => {
+  //   if (item.ServiceTemplateId === e.ServiceTemplateId) {
+  //     item.Value = e.Value;
+  //   }
+  // });
+}
+
+const formRef=ref(null)
+const FiccServiceTableRef=ref(null)
+const QyServiceTableRef=ref(null)
+const ServiceDialogRef=ref(null)
+// 预览/存草稿/提交 操作
+const handleSubmit=_.debounce(function(type){
+  formRef.value.validate((valid)=>{
+    if(valid){
+      let temarr = [];
+      if (RoleType.value === "ficc_seller") {
+        console.log(FiccServiceTableRef.value,'FiccServiceTableRef.value',FiccServiceTableRef.value.tableData);
+        FiccServiceTableRef.value.tableData.forEach((item) => {
+          let obj = {
+            ServiceTemplateId: "",
+            Value: "",
+            Detail: [],
+            Title: item.Title,
+            ChartPermissionId:item.ChartPermissionId
+          };
+          obj.ServiceTemplateId = item.ServiceTemplateId;
+          obj.Value = item.Value;
+          if (item.selected) {
+            //小套餐情况
+            if (item.Detail) {
+              obj.Detail = [ServiceDialogRef.value.tableHeadData, ...ServiceDialogRef.value.tableData];
+            }
+            temarr.push(obj);
+          }
+        });
+      } else {
+        // 只要一个分类里面 主观和客观其中一个被勾选 这个分类就要传
+        QyServiceTableRef.value.selectAllArr.map(item =>{
+          if(QyServiceTableRef.value.checkList.find(it => it.indexOf(item.name)!=-1 && it.indexOf("升级")==-1)){
+            temarr.push({
+              ServiceTemplateId: item.ServiceTemplateId,
+              Value: item.value,
+              Detail: null,
+              Title: item.name,
+              ChartPermissionId:item.ChartPermissionId
+            })
+          }
+        })
+        QyServiceTableRef.value.tableData.list.forEach((item) => {
+          let obj = {
+            ServiceTemplateId: "",
+            Value: "",
+            Detail: null,
+            Title: item.Title == item.fatherName ? item.Title:`${item.fatherName}(${item.Title})`,
+            ChartPermissionId:item.ChartPermissionId
+          };
+          obj.ServiceTemplateId = item.ServiceTemplateId;
+          obj.Value = item.Value;
+          let flag = ''
+          if(item.Title == item.fatherName){
+            //没有主客观
+            flag= QyServiceTableRef.value.checkList.indexOf(item.Title);
+          }else{
+            flag= QyServiceTableRef.value.checkList.indexOf(`${item.fatherName}(${item.Title})`);
+          }
+          if (flag !== -1) {
+            temarr.push(obj);
+          }
+        });
+      }
+      // 判断地址
+      if(formData.province!='海外'&&!formData.city){
+        ElMessage.warning('请选择地址')
+        return
+      }
+
+      // 判断合同期限 不小于一个月
+      if (!validateTime()) {
+        return;
+      }
+
+      //判断金额
+      if (!validatePrice()) {
+        return;
+      }
+      //判断是否选择了套餐
+      if (!temarr.length) {
+        ElMessage.warning("请选择套餐");
+        return;
+      }
+
+      //判断小套餐
+      let flag = true;
+      temarr.forEach((item) => {
+        if (item.Detail&&item.Detail.length) {
+          flag = validateService(item,item.Detail.slice(1));
+        }
+      });
+      if (!flag) return;
+
+
+      let params = {
+        ContractType: formData.constractType,
+        ContractBusinessType:"业务合同",
+        StartDate: formData.time[0],
+        EndDate: formData.time[1],
+        OriginalPrice: Number(formData.price.replace(/,/g,'')),
+        Price: Number(formData.price2.replace(/,/g,'')),
+        PayRemark: formData.payRemark,
+        CompanyName: formData.customeName,
+        CreditCode: formData.customeXYM,
+        Province: formData.province,
+        City: formData.city,
+        Address: formData.address,
+        Fax: formData.customeCz,
+        Phone: formData.customeTel,
+        Postcode: formData.postCode,
+        Remark: formData.remark,
+        SellerRemark:formData.sellerRemark,
+        PayChannel:formData.PayChannel.join(','),
+        TemplateId: RoleType.value === "ficc_seller" ? 1 : 2,
+        Service: temarr,
+        IsAudit: type === "提交" ? true : false,
+      };
+
+      // 判断套餐权限
+      if(cusLastcontract.value&&!handleValidate(params)) return
+      
+      if(type==='预览'){
+        handlePreview(params)
+      }else{
+        handleSave(params)
+      }
+    }else{
+      ElMessage.warning('请完善必填项')
+    }
+  })
+},200)
+
+// 判断套餐权限
+const handleValidate=(e)=>{
+  let tag=true
+  let tagsmall=false
+  // ficc 补充协议 不允许有选择过的
+  if(RoleType.value === "ficc_seller"&&e.ContractType==='补充协议'){
+    // 是否选择大套餐 大套餐为全部品种 不能选
+    e.Service.forEach(item=>{
+      // 大套餐
+      if(item.ChartPermissionId===0&&item.ServiceTemplateId===1){
+        tag=false
+        ElMessage.warning('FICC大套餐不可选')
+      }
+      // 小套餐 判断其中ficc周报是否有重复选择的权限
+      if(item.ChartPermissionId===0&&item.ServiceTemplateId===2){
+        let temIdarr=[]
+        item.Detail.forEach(item2=>{
+          item2.forEach(item3=>{
+            if(item3.Value==='FICC周报'){
+              item2.forEach(item4=>{
+                if(item4.HeadName==='品种'){
+                  temIdarr=item4.ValueId
+                }
+              })
+            }
+          })
+        })
+
+        cusLastcontract.value.forEach(item4=>{
+          if(temIdarr.indexOf(item4.ChartPermissionId)!=-1){
+            tag=false
+            tagsmall=true
+          }
+        })
+      }
+
+      // 市场策略
+      if(item.ChartPermissionId!==0){
+        cusLastcontract.value.forEach(item2=>{
+          if(item2.ChartPermissionId===item.ChartPermissionId){
+            ElMessage.warning(`${item.Title}不可选`)
+            tag=false
+          }
+        })
+      }
+    })
+  }
+
+  // 权益 续约合同 
+  if(RoleType.value === "rai_seller"&&e.ContractType==='续约合同'){
+    const time1=new Date(formData.time[0]).getTime()//选择的开始时间
+    const time2=new Date(formData.time[1]).getTime()//选择的结束时间
+    cusLastcontract.value.forEach(item=>{
+      const time3=new Date(item.StartDate).getTime()
+      const time4=new Date(item.EndDate).getTime()
+      if(time1>time4||time2<time3){
+        console.log('选择时间和上一份无重叠');
+      }else{
+        console.log('时间重叠');
+        e.Service.forEach(item2=>{
+          console.log(item2.ChartPermissionId,item.ChartPermissionId);
+          if(item2.ChartPermissionId===item.ChartPermissionId){
+            ElMessage.warning('同行业有重叠的合同期限,请核实后再提交')
+            tag=false
+          }
+        })
+
+      }
+    })
+    
+  }
+
+  // 小套餐统一一个提示
+  if(tagsmall){
+    ElMessage.warning('小套餐中权限重复')
+  }
+  return tag
+}
+
+//前去预览
+const handlePreview=(params)=>{
+  contractInterface.previewContract(params).then((res) => {
+    if (res.Ret === 200) {
+      sessionStorage.setItem("contractdtl", res.Data.Html);
+      let { href } = $router.resolve({ path: "/contractdtl" });
+      window.open(href, "_blank");
+    }
+  });
+}
+
+//保存
+const handleSave=(params)=>{
+  contractInterface.addContract(params).then((res) => {
+    if (res.Ret === 200) {
+      $router.go(-1);
+    }
+  });
+}
+
+// 金额输入框获取焦点
+const handlePriceBoxFocus=(key)=>{
+  let val=formData[key]
+  val=val.replace(/,/g,'')
+  formData[key]=val
+}
+// 金额输入框获失去焦点
+const handlePriceBoxBlur=(key)=>{
+  let str=formData[key]
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  formData[key]=num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+createTemContractCode()
+getServiceList()
+// 初始化一次 搜索付款方选择列表
+querySearchAsyncPayChannel('','init')
+</script>
+
+<template>
+  <div class="addconstract-container">
+    <el-form ref="formRef" :model="formData" label-position="left" :rules="formRule" 
+    label-width="110px" size="large">
+      <section class="section">
+        <h2 class="section-title">客户信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="甲方名称" prop="customeName" style="width: 50%">
+              <!-- <el-input v-model="formData.customeName" placeholder="请输入名称" style="width: 350px"></el-input> -->
+              <el-autocomplete
+                v-model="formData.customeName"
+                :fetch-suggestions="querySearchAsyncCustome"
+                placeholder="请输入或者搜索客户名称"
+                @select="handleSelectCustome"
+                @blur="handleCustomeInputChange"
+                style="width: 50%"
+              ></el-autocomplete>
+            </el-form-item>
+            <el-form-item label="社会信用码" prop="customeXYM" style="width: 50%">
+              <el-input v-model="formData.customeXYM" placeholder="请输入社会信用码" style="width: 350px" :disabled="cusLastcontract" @input="getContractByCode"></el-input>
+              <!-- <span style="font-size:14px;color:#4099ef;cursor: pointer;" v-if="cusLastcontract" @click="handleUnbind">解除绑定</span> -->
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item 
+              label="公司地址" 
+              prop="province" 
+              style="width: 50%">
+              <v-distpicker :province="formData.province" :city="formData.city" 
+                :province-source="province_sorce_value"
+                :city-source="city_sorce_value"
+                hide-area @province="provinceChange" @selected="selectRegion"></v-distpicker>
+            </el-form-item>
+            <el-form-item label="详细地址" prop="address" style="width: 50%">
+              <el-input v-model="formData.address" placeholder="请输入详细地址" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="传真" prop="customeCz" style="width: 50%">
+              <el-input v-model="formData.customeCz" placeholder="请输入传真" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="电话" prop="customeTel" style="width: 50%">
+              <el-input v-model="formData.customeTel" placeholder="请输入电话" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+
+          <el-form-item label="邮编" prop="postCode" class="border-top">
+            <el-input v-model="formData.postCode" placeholder="请输入邮编" style="width: 350px"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">合同信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="合同编号" style="width: 50%">
+              <span>{{ temContractCode }}</span>
+            </el-form-item>
+            <el-form-item label="合同归属" style="width: 50%">
+              <span>{{ RoleType === "ficc_seller" ? "FICC" : "权益" }}</span>
+            </el-form-item>
+          </div>
+          <el-form-item label="合同类型" prop="constractType" class="border-top">
+            <el-radio-group v-model="formData.constractType">
+              <div style="display:flex">
+                <div style="margin-right:10px;">
+                  <el-radio label="新签合同" :disabled="contractTypeDisable1">新签合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px">
+                  <el-radio label="续约合同" :disabled="contractTypeDisable2">续约合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>
+                      1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+										  2、所有客户在续约申请时提交的合同
+                    </template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px" v-if="RoleType!=='rai_seller'">
+                  <el-radio label="补充协议" :disabled="contractTypeDisable3">补充协议</el-radio>
+                </div>
+              </div>
+            </el-radio-group>
+          </el-form-item>
+          <div class="flex border-top">
+            <el-form-item label="合同期限" prop="time" style="width: 50%">
+              <el-date-picker 
+                style="max-width: 350px"
+                v-model="formData.time" 
+                type="daterange" 
+                value-format="YYYY-MM-DD" 
+                range-separator="至" 
+                start-placeholder="开始日期" 
+                end-placeholder="结束日期"
+                :picker-options="pickerOptions"
+                :disabled="timeDisable"> 
+              </el-date-picker>
+            </el-form-item>
+            <span style="line-height: 40px">{{ formateYear(formData.time) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="合同金额" prop="price" style="width: 50%">
+              <el-input v-model="formData.price" @focus="handlePriceBoxFocus('price')" @blur="handlePriceBoxBlur('price')" placeholder="请输入合同金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(formData.price) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="优惠后金额" prop="price2" style="width: 50%">
+              <el-input v-model="formData.price2" @focus="handlePriceBoxFocus('price2')" @blur="handlePriceBoxBlur('price2')" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(formData.price2) }}</span>
+          </div>
+
+          <el-form-item label="付款方式说明" prop="payRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.payRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+            <p>说明模板:甲方自合同生效日起的十日之内一次性支付一年服务费,乙方收款后为甲方开具合法有效的增值税发票。</p>
+          </el-form-item>
+          <el-form-item label="付款方" prop="PayChannel" class="border-top">
+            <!-- <el-input type="textarea" v-model="formData.PayChannel" placeholder="填写代支付的券商名称/期货公司名称,若非代付填写无" style="width: 80%"></el-input> -->
+            <!-- <el-autocomplete
+                v-model="formData.PayChannel"
+                :fetch-suggestions="querySearchAsyncPayChannel"
+                placeholder="请输入"
+                style="width: 80%"
+              ></el-autocomplete> -->
+              <el-select
+                v-model="formData.PayChannel"
+                multiple
+                filterable
+                remote
+                allow-create
+                placeholder="请输入关键词"
+                :remote-method="querySearchAsyncPayChannel"
+                style="width: 80%">
+                <el-option
+                  v-for="item in PayChannelOptions"
+                  :key="item.PayChannel"
+                  :label="item.PayChannel"
+                  :value="item.PayChannel">
+                </el-option>
+              </el-select>
+              <p>如果为代付,则需要填写代付方名称(期货公司或者证券公司),请勿填写现金/转账</p>
+          </el-form-item>
+          <!-- <el-form-item label="备注" prop="sellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.sellerRemark" placeholder="审批人查看,不在生成合同中展示" style="width: 80%"></el-input>
+          </el-form-item> -->
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">服务内容</h2>
+        <div class="section-container">
+          <!-- ficc 服务内容表格 -->
+          <FiccServiceTable
+            ref="FiccServiceTableRef"
+            :canEdit="true"
+            :serviceData="ficcServiceData"
+            :hasSercive="selectServiceData"
+            :editValue="editValue"
+            :contractType="formData.constractType"
+            @handleShowService="handleShowService"
+            v-if="RoleType === 'ficc_seller'"
+          ></FiccServiceTable>
+          <!-- 权益 服务内容表格 -->
+          <QyServiceTable ref="QyServiceTableRef" :canEdit="true" :serviceData="qyServiceData" :hasSercive="selectServiceData" @handleShowService="handleShowService" v-else></QyServiceTable>
+        </div>
+
+        <p style="margin-top: 30px; margin-bottom: 20px;font-size:14px">补充内容(eg额外赠送、路演次数规定)</p>
+        <el-radio v-model="radio" label="2">无</el-radio>
+        <el-radio v-model="radio" label="1">有</el-radio>
+        <el-form-item prop="remark" label-width="0" ref="formItemRemark">
+          <el-input v-if="radio === '1'" type="textarea" v-model="formData.remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+        </el-form-item>
+        <!-- <div style="text-align: center; margin-top: 40px; margin-bottom: 20px">
+          <el-button type="primary" plain style="width: 145px" @click="handleSubmit('预览')">预览</el-button>
+          <el-button type="primary" @click="handleSubmit('存草稿')" style="width: 145px">存草稿</el-button>
+          <el-button type="primary" @click="handleSubmit('提交')" style="width: 145px">提交</el-button>
+        </div> -->
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">审批备注</h2>
+        <div class="section-container">
+          <el-form-item label="备注" prop="sellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.sellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+          </el-form-item>
+        </div>
+        <div style="text-align: center; margin-top: 40px; margin-bottom: 20px">
+          <el-button type="primary" plain style="width: 145px" @click="handleSubmit('预览')">预览</el-button>
+          <el-button type="primary" @click="handleSubmit('存草稿')" style="width: 145px">存草稿</el-button>
+          <el-button type="primary" @click="handleSubmit('提交')" style="width: 145px">提交</el-button>
+        </div>
+      </section>
+    </el-form>
+
+    <!-- 套餐 -->
+    <ServiceDialog ref="ServiceDialogRef" :contractType="formData.constractType" :serviceShow="serviceShow" @serviceClose="serviceClose" @serviceSave="serviceSave" :serviceCon="serviceCon"></ServiceDialog>
+  </div>
+</template>
+
+
+<style>
+.el-radio__input.is-checked .el-radio__inner {
+  background-color: transparent;
+}
+.el-radio__inner::after {
+  width: 6px;
+  height: 6px;
+  background-color: #409eff;
+}
+.addconstract-container .el-checkbox__label {
+  font-size: 14px !important;
+}
+.el-form-item {
+  margin-bottom: 30px;
+}
+.addconstract-container .section-container .el-textarea__inner::placeholder{
+  color: #999;
+}
+.addconstract-container .section-container .el-input__inner::placeholder{
+  color: #999;
+}
+</style>
+
+<style lang="scss" scoped>
+.addconstract-container {
+  min-height: calc(100vh - 250px);
+  position: relative;
+  font-size: 16px;
+  color: #000;
+}
+.flex {
+  display: flex;
+}
+.section {
+  background-color: #fff;
+  border: 1px solid #aab4cc;
+  border-radius: 4px;
+  padding: 20px 30px;
+  margin-bottom: 20px;
+  .section-title {
+    margin-bottom: 30px;
+  }
+  .border-top {
+    padding-top: 30px;
+    border-top: 1px solid #dcdfe6;
+  }
+}
+</style>

+ 545 - 0
src/views/contract_manage/addContractDC.vue

@@ -0,0 +1,545 @@
+<script setup>
+import { ref,reactive,nextTick } from "vue";
+import {ElMessage,ElMessageBox} from "element-plus"
+import { useRouter} from 'vue-router';
+import _ from 'lodash'
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import { contractInterface } from "@/api/api.js";
+import{province_sorce,city_sorce} from '@/utils/distpicker';
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import { CalculationDate } from "@/utils/CalculationDate";
+import {formatTime} from '@/hooks/mixins/index.js'
+
+const $router = useRouter()
+
+const province_sorce_value=province_sorce
+const city_sorce_value=city_sorce
+
+const RoleType=ref(localStorage.getItem("Role") || "")
+const formData=reactive({
+  CompanyName: "",
+  CreditCode: "",
+  Province: "",
+  City: "",
+  Address: "",
+  Fax: "",
+  Phone: "",
+  Postcode: "",
+  OriginalPrice: "",
+  trueUseName: "",
+  sellerRemark: "",
+  PayRemark: "",
+  RelationContractId: null,
+})
+
+const formRule={
+  CompanyName: [{ required: true, message: "请填写名称", trigger: "blur" }],
+  CreditCode: [{ required: true, message: "请填写信用码", trigger: "blur" }],
+  Province: [{ required: true, message: "请选择地址", trigger: "change" }],
+  OriginalPrice: [
+    { required: true, message: "请填写代付金额", trigger: "blur" },
+    // { pattern: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/, message: "请输入正确的格式,可保留两位小数" },
+  ],
+  trueUseName: [{ required: true, message: "请输入关键字", trigger: "change" }],
+  PayRemark: [{ required: true, message: "请填写付款方式说明", trigger: "blur" }],
+  Address: [{ required: true, message: "请填写详细地址", trigger: "blur" }],
+  Phone: [{ required: true, message: "请填写电话", trigger: "blur" }],
+}
+
+const searchList=ref([])
+const contractList=ref([])
+const showContractList=ref(false)
+const relationContractData=ref(null) //选择的关联合同数据
+const ficcServiceData=ref(null) //ficc 底部表格数据
+const qyServiceData=ref(null) //权益底部表格数据
+const serviceShow=ref(false) //是否显示查看报价单
+const serviceCon=ref({}) //选择的服务项 查看报价单/选择品种
+// filters
+
+const formatPrice=(str)=>{
+  if(!str) return ''
+  str=str.toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  return num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+// 金额转中文大写
+const digitUppercase=(n)=>{
+  if(!n) return ''
+  n=n.toString().replace(/,/g,'')
+  let fraction = ["角", "分"];
+  let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+  let unit = [
+    ["元", "万", "亿"],
+    ["", "拾", "佰", "仟"],
+  ];
+  let head = n < 0 ? "欠" : "";
+  n = Math.abs(n);
+  let s = "";
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+  }
+  // s = s || "整";
+
+  n = Math.floor(n);
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = "";
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p;
+      n = Math.floor(n / 10);
+    }
+    s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+  }
+  return (
+    "大写:" + (head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零"))
+    // .replace(/^整$/, "零元整")
+  );
+}
+//计算多少年
+const formateYear=(e)=>{
+  if (e[0]) {
+    return `有效期为${CalculationDate(e[0], e[1])}`;
+  } else {
+    return "";
+  }
+}
+
+// 搜索出客户
+const customeSearch=async(query)=>{
+  if (!query) return;
+  const res = await contractInterface.getBusinessContractCompanyNameList({ Keyword: query });
+  if (res.Ret === 200) {
+    searchList.value = res.Data;
+  }
+}
+// 选择客户并搜索出其下已签回合同
+const handleSelectTrueUseName=(e)=>{
+  nextTick(async()=>{
+    if(isBlurManual.value){
+      isBlurManual.value=false
+      return 
+    }
+    if (e||!formData.trueUseName) return;
+
+    const res = await contractInterface.getBusinessContractList({ CompanyName: formData.trueUseName });
+    if (res.Ret === 200) {
+      showContractList.value = true;
+      nextTick(() => {
+        contractList.value = res.Data.List;
+      });
+    }
+  })
+
+}
+
+// 确认关联
+const handleRelation=async()=>{
+  if (!formData.RelationContractId) {
+    ElMessage.warning("请选择一份合同进行关联");
+    return;
+  }
+  const res = await contractInterface.getContractDetail({ ContractId: formData.RelationContractId });
+  if (res.Ret === 200) {
+    relationContractData.value = res.Data;
+    getServiceList();
+    showContractList.value = false;
+  }
+}
+
+const trueUseNameRef=ref(null)
+const isBlurManual=ref(false)
+const closeTrueUseNameDia=()=>{
+  // element-plus 会在弹窗关闭后,返回焦点给el-select组件……
+  console.log('失去焦点');
+  isBlurManual.value=true
+  trueUseNameRef.value.blur()
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+  contractInterface.getServiceList({ ProductId: relationContractData.value.ProductId }).then((res) => {
+    if (res.Ret === 200) {
+      if (relationContractData.value.ProductId === 1) {
+        ficcServiceData.value = res.Data;
+      } else {
+        qyServiceData.value = res.Data;
+      }
+    }
+  });
+}
+
+//显示查看报价单弹窗
+const handleShowService=(e)=>{
+  // 小套餐情况处理表格数据
+  if (e.Detail) {
+    let temarr = e.Detail.map((rowItem) => {
+      let rowArr = [];
+      for (let key in rowItem) {
+        if (key.substring(0, 3) === "Col" && rowItem[key] !== "") {
+          rowArr.push(JSON.parse(rowItem[key]));
+        }
+      }
+      return rowArr;
+    });
+    serviceCon.value = { ...e, tableHeadData: temarr[0], tableData: temarr.slice(1) };
+  } else {
+    serviceCon.value = e;
+  }
+  serviceShow.value = true;
+}
+
+//选地地区时 选则的省份改变重置city为空
+const provinceChange=(e)=>{
+  formData.Province =e.value=='省'?'':e.value;
+  formData.City=''
+}
+
+//选择地区
+const selectRegion=(e)=>{
+  formData.Province =e.province.value=='省'?'':e.province.value;
+  formData.City = e.city.value=='市'?'':e.city.value;
+}
+
+const formRef=ref(null)
+// 提交
+const handleSubmit=(type)=>{
+  formRef.value.validate(async (valid) => {
+    if (valid) {
+      let params = {
+        ContractType:"新签合同",
+        ContractBusinessType:"代付合同",
+        Address: formData.Address,
+        City: formData.City,
+        CompanyName: formData.CompanyName,
+        CreditCode: formData.CreditCode,
+        Province: formData.Province,
+        Fax: formData.Fax,
+        Phone: formData.Phone,
+        Postcode: formData.Postcode,
+        OriginalPrice: Number(formData.OriginalPrice.replace(/,/g,'')),
+        sellerRemark: formData.sellerRemark,
+        PayRemark: formData.PayRemark,
+        TemplateId: RoleType.value === "ficc_seller" ? 1 : 2,
+        RelationContractId: formData.RelationContractId,
+      };
+      if(!params.City){
+        ElMessage.warning("请选择城市")
+        return
+      }
+      if(!params.RelationContractId){
+        ElMessage.warning('请选择关联合同')
+        return
+      }
+      if (type === "预览") {
+        params = { ...params, IsAudit: false };
+        handlePreview(params);
+        return;
+      } else if (type === "存草稿") {
+        params = { ...params, IsAudit: false };
+      } else {
+        params = { ...params, IsAudit: true };
+      }
+      const res = await contractInterface.addContract(params);
+      if (res.Ret === 200) {
+        $router.go(-1);
+      }
+    }else{
+      ElMessage.warning('请完善必填项')
+    }
+  });
+}
+//前去预览
+const handlePreview=(params)=>{
+  contractInterface.previewContract(params).then((res) => {
+    if (res.Ret === 200) {
+      sessionStorage.setItem("contractdtl", res.Data.Html);
+      let { href } = $router.resolve({ path: "/contractdtl" });
+      window.open(href, "_blank");
+    }
+  });
+}
+
+// 金额输入框获取焦点
+const handlePriceBoxFocus=(key)=>{
+  let val=formData[key]
+  val=val.replace(/,/g,'')
+  formData[key]=val
+}
+// 金额输入框获失去焦点
+const handlePriceBoxBlur=(key)=>{
+  let str=formData[key]
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  formData[key]=num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+</script>
+
+<template>
+  <div class="addcontractdc-container">
+    <el-form ref="formRef" :model="formData" label-position="left" :rules="formRule" label-width="110px" size="large">
+      <section class="section">
+        <h2 class="section-title">客户信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="代付方" prop="CompanyName" style="width: 50%">
+              <el-input v-model="formData.CompanyName" placeholder="请输入名称" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="社会信用码" prop="CreditCode" style="width: 50%">
+              <el-input v-model="formData.CreditCode" placeholder="请输入社会信用码" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="公司地址" prop="Province" style="width: 50%">
+              <v-distpicker :province-source="province_sorce_value"
+                :city-source="city_sorce_value"  :province="formData.Province" :city="formData.City" hide-area @province="provinceChange" @selected="selectRegion"></v-distpicker>
+            </el-form-item>
+            <el-form-item label="详细地址" prop="Address" style="width: 50%">
+              <el-input v-model="formData.Address" placeholder="请输入详细地址" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="传真" prop="Fax" style="width: 50%">
+              <el-input v-model="formData.Fax" placeholder="请输入传真" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="电话" prop="Phone" style="width: 50%">
+              <el-input v-model="formData.Phone" placeholder="请输入电话" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="邮编" prop="Postcode" style="width: 50%">
+              <el-input v-model="formData.Postcode" placeholder="请输入邮编" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="代付金额" prop="OriginalPrice" style="width: 50%">
+              <el-input v-model="formData.OriginalPrice" @focus="handlePriceBoxFocus('OriginalPrice')" @blur="handlePriceBoxBlur('OriginalPrice')" placeholder="请输入金额" style="width: 350px"></el-input>
+              <span style="line-height: 40px;margin-left:20px">{{ digitUppercase(formData.OriginalPrice)  }}</span>
+            </el-form-item>
+          </div>
+        </div>
+      </section>
+      <section class="section">
+        <el-form-item label="付款方式说明" prop="PayRemark">
+          <el-input type="textarea" v-model="formData.PayRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+          <p>说明模板:甲方自合同生效日起的十日之内一次性支付一年服务费,乙方收款后为甲方开具合法有效的增值税发票。</p>
+        </el-form-item>
+        <el-form-item label="实际使用方" prop="trueUseName" class="border-top">
+          <el-select v-model="formData.trueUseName" filterable remote placeholder="关键词搜索" ref="trueUseNameRef"
+          :remote-method="customeSearch" @visible-change="handleSelectTrueUseName" style="width: 500px">
+            <el-option v-for="item in searchList" :key="item" :label="item" :value="item"> </el-option>
+          </el-select>
+        </el-form-item>
+      </section>
+
+      <section class="section" v-if="relationContractData">
+        <h2 class="section-title">合同信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="合同编号" style="width: 50%">
+              <span>{{ relationContractData.ContractCode }}</span>
+            </el-form-item>
+            <el-form-item label="合同归属" style="width: 50%">
+              <span>{{ relationContractData.ProductId === 1 ? "ficc" : "权益" }}</span>
+            </el-form-item>
+          </div>
+          <el-form-item label="合同类型" class="border-top">
+            <el-radio-group v-model="relationContractData.ContractType">
+              <div style="display: flex">
+                <div style="margin-right: 10px">
+                  <el-radio label="新签合同" disabled>新签合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right: 10px">
+                  <el-radio label="续约合同" disabled>续约合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>
+                      1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+										  2、所有客户在续约申请时提交的合同
+                    </template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right: 10px" v-if="RoleType !== 'rai_seller'">
+                  <el-radio label="补充协议" disabled>补充协议</el-radio>
+                </div>
+              </div>
+            </el-radio-group>
+          </el-form-item>
+          <div class="flex border-top" v-if="relationContractData.timeRange">
+            <el-form-item label="合同期限" style="width: 50%">
+              <el-date-picker disabled v-model="relationContractData.timeRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
+            </el-form-item>
+            <span style="line-height: 40px">{{ formateYear(relationContractData.timeRange) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="合同金额" style="width: 50%">
+              <el-input disabled :value="formatPrice(relationContractData.OriginalPrice)" placeholder="请输入合同金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(relationContractData.OriginalPrice)  }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="优惠后金额" style="width: 50%">
+              <el-input disabled :value="formatPrice(relationContractData.Price)" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(relationContractData.Price)  }}</span>
+          </div>
+
+          <el-form-item label="付款方式说明" class="border-top">
+            <el-input
+              type="textarea"
+              disabled
+              v-model="relationContractData.PayRemark"
+              placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)"
+              style="width: 80%"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="付款方" class="border-top">
+            <el-input
+              type="textarea"
+              disabled
+              v-model="relationContractData.PayChannel"
+              placeholder="代付,请填写代付的券商名称或期货公司名称;非代付,请填写无。(请勿填写现金/转账等支付形式)"
+              style="width: 80%"
+            ></el-input>
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="section" v-if="relationContractData">
+        <h2 class="section-title">服务内容</h2>
+          <div class="section-container">
+            <!-- ficc 服务内容表格 -->
+            <FiccServiceTable
+              :canEdit="false"
+              :serviceData="ficcServiceData"
+              :hasSercive="relationContractData.Service"
+              @handleShowService="handleShowService"
+              v-if="relationContractData.ProductId === 1 && ficcServiceData"
+            ></FiccServiceTable>
+
+            <!-- 权益 服务内容表格 -->
+            <QyServiceTable
+              :canEdit="false"
+              :serviceData="qyServiceData"
+              :hasSercive="relationContractData.Service"
+              @handleShowService="handleShowService"
+              v-if="relationContractData.ProductId === 2 && qyServiceData"
+            ></QyServiceTable>
+          </div>
+          <p style="margin-top: 30px; margin-bottom: 20px">补充内容(eg额外赠送、路演次数规定)</p>
+          <el-radio :model-value="relationContractData.Remark?'1':'2'" label="2" disabled>无</el-radio>
+          <el-radio :model-value="relationContractData.Remark?'1':'2'" label="1" disabled>有</el-radio>
+          <el-input v-if="relationContractData.Remark" disabled type="textarea" v-model="relationContractData.Remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">审批备注</h2>
+        <div class="section-container">
+          <el-form-item label="备注" prop="sellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.sellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+          </el-form-item>
+        </div>
+        <div style="text-align: center; margin-top: 40px; margin-bottom: 20px">
+          <el-button type="primary" plain style="width: 145px" @click="handleSubmit('预览')" size="large">预览</el-button>
+          <el-button type="primary" @click="handleSubmit('存草稿')" style="width: 145px" size="large">存草稿</el-button>
+          <el-button type="primary" @click="handleSubmit('提交')" style="width: 145px" size="large">提交</el-button>
+        </div>
+      </section>
+    </el-form>
+
+    <!-- 选择合同弹窗 -->
+    <el-dialog v-dialogDrag :title="`搜索到${formData.trueUseName}下合同`" :append-to-body="true" 
+    v-model="showContractList" width="50%" center @close="closeTrueUseNameDia" draggable>
+      <el-table :data="contractList" border height="60vh">
+        <el-table-column align="center" width="50">
+          <template #default="scope">
+            <el-radio v-model="formData.RelationContractId" :label="scope.row.ContractId"><span></span></el-radio>
+          </template>
+        </el-table-column>
+        <el-table-column label="合同编号" prop="ContractCode" align="center"></el-table-column>
+        <el-table-column label="销售" prop="SellerName" align="center" width="80"></el-table-column>
+        <el-table-column label="合同类型" prop="ContractType" align="center" width="80"></el-table-column>
+        <el-table-column label="合同有效期" prop="ContractType" align="center" width="180">
+          <template #default="scope">{{ formatTime(scope.row.StartDateStr)  }}~{{ formatTime(scope.row.EndDateStr) }}</template>
+        </el-table-column>
+        <el-table-column key="合同金额" align="center" prop="Price" label="合同金额" width="100"></el-table-column>
+        <el-table-column key="合同状态" align="center" prop="Status" label="合同状态" width="80"></el-table-column>
+        <el-table-column key="签回时间" align="center" prop="Status" label="签回时间" width="160">
+          <template #default="scope">{{ formatTime(scope.row.CheckBackFileTimeStr) }}</template>
+        </el-table-column>
+      </el-table>
+      <div style="text-align: center; margin: 30px 0">
+        <el-button type="primary" @click="handleRelation" size="large">确定关联</el-button>
+        <el-button type="primary" plain @click="showContractList = false" size="large">取消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 查看报价单弹窗 -->
+    <el-dialog v-dialogDrag v-model="serviceShow" width="60%" class="self-dialog-c" :modal-append-to-body="false" @close="serviceShow=false" :show-close="false">
+      <h2 class="title" style="text-align: center; color: #000">{{RoleType == 'ficc_seller'? serviceCon.Title : serviceCon.showTitle}}</h2>
+      <img class="img" style="width: 100%; display: block; margin: 20px auto" 
+      :src="RoleType == 'ficc_seller'? serviceCon.Value : serviceCon.showValue" />
+      <div style="text-align: center; margin-bottom: 30px">
+        <el-button type="primary" @click="serviceShow=false" size="large">知道了</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style>
+.addcontractdc-container .self-dialog-c .el-dialog__header {
+  background-color: #fff;
+  padding: 0;
+}
+.addcontractdc-container .self-dialog-c {
+  width: auto;
+  max-width: 60%;
+}
+</style>
+<style lang="scss" scoped>
+.addcontractdc-container {
+  min-height: calc(100vh - 250px);
+  position: relative;
+  font-size: 16px;
+  color: #000;
+}
+.flex {
+  display: flex;
+}
+.section {
+  background-color: #fff;
+  border: 1px solid #aab4cc;
+  border-radius: 4px;
+  padding: 20px 30px;
+  margin-bottom: 20px;
+  .section-title {
+    margin-bottom: 30px;
+  }
+  .border-top {
+    padding-top: 30px;
+    border-top: 1px solid #dcdfe6;
+  }
+}
+</style>

+ 260 - 0
src/views/contract_manage/components/FiccServiceTable.vue

@@ -0,0 +1,260 @@
+<script setup>
+import { ref,watch } from "vue";
+const props = defineProps({
+  serviceData: {
+    type:Array,
+    default:()=>[]
+  },
+  hasSercive: {
+    type:Array,
+    default:()=>[]
+  },
+  canEdit: false,
+  editValue: {},
+  contractType:null,
+  isEdit:false // 是新增合同还是修改合同
+});
+const emits=defineEmits(['handleShowService'])
+
+const tableData=ref([])
+
+watch(()=>props.editValue,(nval, oval)=>{
+  if (nval.ServiceTemplateId) {
+    tableData.value.forEach((item) => {
+      if (item.ServiceTemplateId === nval.ServiceTemplateId) {
+        item.Value = nval.Value;
+        // item 表格数据 nval 更新的数据
+        // 更新表头 item.Detail[0]
+        console.log([nval.tableHeadData,...nval.tableData]);
+        let arr=[nval.tableHeadData,...nval.tableData].map((item2,index2)=>{
+          
+          let obj={}
+          item2.forEach((item3,index3)=>{
+            let key=`Col${index3+1}`
+            obj={...obj,[key]:JSON.stringify(item3)}
+          })
+          return obj
+        })
+        console.log(arr);
+        item.Detail=arr
+
+
+        
+      }
+    });
+  }
+})
+
+// 初始化数据
+const initTableData=()=>{
+  if (props.serviceData.length === 0) return;
+  let arr = props.serviceData.map((item, index) => {
+    return {
+      ...item,
+      selected: false,
+      disable: false,
+      text1: item.Detail ? "选择品种" : "查看报价单",
+      text2: "下载报价单",
+    };
+  });
+  // 合同详情或者是需要编辑情况下
+  if (props.hasSercive.length !== 0) {
+  arr.forEach((item) => {
+    props.hasSercive.forEach((e) => {
+      if (e.ServiceTemplateId === item.ServiceTemplateId) {
+        item.Value = e.Value;
+        item.Detail = e.DetailList || e.Detail;
+        item.selected = true;
+        item.disable = false;
+      }
+
+      if (item.SelectType === "radio" && e.ServiceTemplateId !== item.ServiceTemplateId) {
+        if (!item.selected) {
+          item.disable = false;
+        }
+      }
+    });
+  });
+
+  // 合同详情 全部禁用
+  if (!props.canEdit) {
+    arr.forEach((item) => {
+      item.disable = true;
+      // 如果是小套餐类型 即存在Detail 则将选择品种改为查看报价单
+      if (item.Detail) {
+        item.text1 = "查看报价单";
+      }
+    });
+  }
+  // this.tableData = arr;
+  } else {
+    // 新增 默认选择第一个
+    // arr.forEach((item, index) => {
+    //   if (index === 0) {
+    //     item.selected = true;
+    //   }
+    // });
+    //如果第一个是单选的 则将其他的禁用
+    // if (arr[0] && arr[0].SelectType === "radio") {
+    //   arr.forEach((item) => {
+    //     if (item.ServiceTemplateId !== arr[0].ServiceTemplateId && item.SelectName === arr[0].SelectName) {
+    //       item.disable = true;
+    //       item.selected = false;
+    //     }
+    //   });
+    // }
+  }
+
+  if(props.contractType==='补充协议'){
+    arr.forEach(item=>{
+      //大套餐 禁用
+      if(item.ServiceTemplateId===1){
+        item.disable=true
+        item.selected=false
+      }else{
+        item.disable=false
+      }
+    })
+  }
+
+  tableData.value = arr;
+}
+
+// 一开始进来 serviceData数据为空数组
+watch(()=>props.serviceData,()=>{
+  initTableData();
+},
+{immediate:true,deep:true})
+
+watch(()=>props.contractType,(nval)=>{
+  initTableData();
+  if(nval==='补充协议'){
+    tableData.value.forEach(item=>{
+      //大套餐 禁用
+      if(item.ServiceTemplateId===1){
+        item.disable=true
+        item.selected=false
+      }else{
+        item.disable=false
+      }
+      // 需求更改,新增合同类型为"补充协议的时候",不默认勾选宏观经济
+      // 将默认的勾选的宏观经济去除
+      if(item.ServiceTemplateId===2 && !props.isEdit){
+        for (let i = 0; i < item.Detail.slice(3,6).length; i++) {
+          let temp = JSON.parse(item.Detail.slice(3,6)[i].Col2)
+          temp.ValueId.length=0
+          temp.Value=""
+          item.Detail.slice(3,6)[i].Col2 = JSON.stringify(temp)
+        }
+      }
+    })
+  }else{
+    // 如果一个都没选中过 则重置都为非禁用
+    let flag=0
+    tableData.value.forEach(item=>{
+      if(item.selected){
+        flag++
+      }
+    // "补充协议"切换会其他合同类型时,恢复默认,即勾选宏观经济的状态
+      if(item.ServiceTemplateId===2 && !props.isEdit){
+        for (let i = 0; i < item.Detail.slice(3,6).length; i++) {
+          let temp = JSON.parse(item.Detail.slice(3,6)[i].Col2)
+          if(temp.ValueId.length===0){
+            temp.ValueId[0]=1
+            temp.Value="宏观经济"
+            item.Detail.slice(3,6)[i].Col2 = JSON.stringify(temp) 
+          }
+        }
+      }
+    })
+    if(flag==0){
+      tableData.value.forEach(item=>{
+        item.disable=false
+      })
+    }
+  }
+})
+
+// 选项切换
+const handleChange=(e)=>{
+  if (e.SelectType === "radio") {
+    if (e.selected) {
+      tableData.value.forEach((item) => {
+        if (item.ServiceTemplateId !== e.ServiceTemplateId && item.SelectName === e.SelectName) {
+          item.disable = true;
+          item.selected = false;
+        }
+      });
+      // 如果是小套餐则 主动展示弹窗
+      if(e.Detail&&e.Detail.length){
+        handleShow(e)
+      }
+    } else {
+      tableData.value.forEach((item) => {
+        if (item.ServiceTemplateId !== e.ServiceTemplateId && item.SelectName === e.SelectName) {
+          // 如果选择了补充协议 则大套餐仍然不可选
+          if(props.contractType==='补充协议'&&item.ServiceTemplateId===1){
+            item.disable=true
+            item.selected=false
+          }else{
+            item.disable = false;
+            item.selected = false;
+          }
+        }
+      });
+    }
+  }
+}
+
+//下载报价单
+const handleDownloadImg=(e)=>{
+  let link = document.createElement("a");
+
+  //判断e.Value是否以 http开头
+  if (e.Value.startsWith("http")) {
+    link.href = e.Value + "?response-content-type=application/octet-stream"; //下载链接
+  } else {
+    link.href = e.Value;
+  }
+  link.setAttribute("download", e.Title);
+  link.style.display = "none"; //a标签隐藏
+  document.body.appendChild(link);
+  link.click();
+}
+
+const handleShow=(e)=>{
+  emits("handleShowService", e);
+}
+defineExpose({
+  tableData
+})
+
+</script>
+
+<template>
+  <el-table border :data="tableData" style="width: 601px">
+    <el-table-column label="套餐类型" width="200" align="center">
+      <template #default="scope">
+        <el-checkbox
+          @change="handleChange(scope.row)"
+          :label="scope.row.Title"
+          v-model="scope.row.selected"
+          :disabled="scope.row.disable"
+          style="width: 100%; text-align: left; padding-left: 50px"
+        ></el-checkbox>
+      </template>
+    </el-table-column>
+    <el-table-column width="200">
+      <template #default="scope">
+        <span style="cursor: pointer; color: #409eff; padding-left: 50px" v-if="scope.row.selected" @click="handleShow(scope.row)">{{ scope.row.text1 }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column width="200" align="center">
+      <template #default="scope">
+        <span style="cursor: pointer; color: #409eff" v-if="scope.row.selected && scope.row.Value" @click="handleDownloadImg(scope.row)">{{ scope.row.text2 }}</span>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<style></style>

+ 4 - 1
src/views/contract_manage/components/PreviewService.vue

@@ -3,7 +3,10 @@ import { reactive, watch } from 'vue'
 
 const props = defineProps({
   showPreview: false,
-  serviceCon: [],
+  serviceCon: {
+    type:Array,
+    default:()=>[]
+  },
 })
 
 const emits = defineEmits(['closePreviewService'])

+ 439 - 0
src/views/contract_manage/components/QyServiceTable.vue

@@ -0,0 +1,439 @@
+
+<script setup>
+
+import { ref,reactive,watch } from "vue";
+
+const props = defineProps({
+  serviceData: {
+    type:Array,
+    default:()=>[]
+  },
+  hasSercive: {
+    type:Array,
+    default:()=>[]
+  },
+  canEdit: false,
+});
+const emits=defineEmits(['handleShowService','serviceClose'])
+
+
+const tableData=reactive({
+  radio: "",
+  list: [],
+  spanMergeCount:[]
+})
+const checkList=ref([])
+// 有主客观的套餐列表
+const selectAllArr=ref([])
+// 有主客观的套餐名称列表
+const haveChildList=ref([])
+// 升级的套餐列表 医药、消费、科技、智造
+const upgradeServes=ref([])
+const templateData=ref([])
+
+  // watch: {
+    // checkList(nval, oval) {
+    //   let tag = "";
+    //   let temtitle = "";
+
+    //   this.tableData.list.forEach((item) => {
+    //     if (item.SelectType === "radio") {
+    //       let flag = nval.indexOf(item.Title);
+    //       if (flag !== -1) {
+    //         tag = item.SelectName;
+    //         temtitle = item.Title;
+    //       }
+    //     }
+    //   });
+
+    //   this.tableData.list.forEach((item) => {
+    //     if (item.fatherName === this.tableData.radio) {
+    //       if (item.SelectName === tag && item.Title !== temtitle) {
+    //         item.disable = true;
+    //         item.selected = false;
+    //       } else {
+    //         item.disable = false;
+    //       }
+    //     }
+    //   });
+    // },
+  // }
+
+//切换radio
+const handleRadioChange=(e)=>{
+  checkList.value = [];
+  for (const it of selectAllArr.value) {
+    it.checkAll = false
+    it.isIndeterminate = false
+  }
+  tableData.list.forEach((item) => {
+    if (item.grandFatherName !== e) {
+      item.disable = true;
+      item.selected = false;
+    } else {
+      item.disable = false;
+    }
+  });
+}
+// 全选
+const handleCheckAllChange=(row)=>{
+  // console.log(row,'row');
+  // console.log(checkList.value,'this.checkList');
+  let arr = checkList.value.filter(item => item.indexOf(row.fatherName) != -1)
+  let rowFather = selectAllArr.value.find((item) => item.name == row.fatherName)
+  rowFather.isIndeterminate = false
+  if(arr.length<2){
+    rowFather.checkAll=true
+    checkList.value = [...new Set([...checkList.value,...[`${row.fatherName}(主观)`,`${row.fatherName}(客观)`]])]
+  }else{
+    checkList.value = checkList.value.filter(item => item.indexOf(row.fatherName) == -1)
+    rowFather.checkAll=false
+  }
+  handleChange(checkList.value,row)
+}
+
+//点击选项
+const handleChange=(nval,row)=>{
+  let rowFather = selectAllArr.value.find((item) => item.name == row.fatherName)
+  if(rowFather){
+    let arr = nval.filter(item => item.indexOf(row.fatherName) !=-1)
+    rowFather.checkAll=arr.length==2
+    rowFather.isIndeterminate=arr.length==1
+  }
+  //一个都没选
+  if(!nval.length){
+    tableData.radio=""
+    tableData.list.forEach(item=>{
+      item.disable=false
+      item.selected=false
+    })
+    return
+  }
+
+  // 当this.tableData.radio 为空时 
+  if(!tableData.radio){
+    tableData.list.forEach(item=>{
+      if(item.Title===nval[0] || nval[0].indexOf(item.Title)!=-1){
+        tableData.radio=item.grandFatherName
+        tableData.list.forEach(item2=>{
+          if(item2.grandFatherName!==item.grandFatherName){
+            item2.selected=false 
+            item2.disable=true
+          }
+        })
+      }
+    })
+  }
+
+  let tag = "";
+  let temtitle = "";
+  
+  tableData.list.forEach((item) => {
+    if (item.SelectType === "radio") {
+      let flag = nval.indexOf(item.Title);
+      if (flag !== -1) {
+        tag = item.SelectName;
+        temtitle = item.Title;
+      }
+    }
+  });
+  tableData.list.forEach((item) => {
+    if (item.grandFatherName === tableData.radio) {
+      if (item.SelectName === tag && item.Title !== temtitle) {
+        item.disable = true;
+        item.selected = false;
+      } else {
+        item.disable = false;
+      }
+    }
+  });
+  // 升级套餐与基础套餐 禁用
+  // console.log(upgradeServes.value,'this.upgradeServes',tableData.list);
+  upgradeServes.value.map(item =>{
+    // 取出原始套餐标题
+    let serveTitle = item.fatherName.split("(")[0]
+    let hasBasicServe=nval.some(item => item.indexOf(serveTitle)!=-1 && item.indexOf('升级')==-1)
+    let hasUpgradeServe=nval.some(item => item.indexOf(serveTitle)!=-1 && item.indexOf('升级')!=-1)
+    if(hasBasicServe){
+      item.disable=true
+    }else if(hasUpgradeServe){
+      tableData.list.map(it => {
+        if(it.fatherName.indexOf(serveTitle)!=-1 && it.fatherName.indexOf('升级')==-1){
+          it.disable=true
+        }
+      })
+    }
+  })
+}
+
+//显示报价单
+const handleShow=(e)=>{
+  emits("handleShowService", e);
+}
+
+//显示后面的文字
+const showText=(e)=>{
+  let text=''
+  if(e.fatherName == e.Title){
+    // 没有主客观的
+    text=e.Title
+    let flag = checkList.value.indexOf(text);
+    if (flag !== -1) {
+      return true;
+    } else {
+      return false;
+    }
+  }else{
+    // 有主客观的 合并了单元格,只会传主观的进来
+    text = e.fatherName
+    let arr = checkList.value.filter(item => item.indexOf(text)!=-1 && item.indexOf("升级")==-1)
+    if(arr.length>0){
+      // 主观或者客观
+      if(arr.length==1){
+        if(arr.find(item => item.indexOf("主观")!=-1)){
+          // 主观
+          e.showValue = e.Value
+          e.showTitle = `${text}(${e.Title})`
+        }else{
+          //客观
+          e.showValue = tableData.list.find(item => item.fatherName ==text && item.Title == '客观').Value
+          e.showTitle = tableData.list.find(item => item.fatherName ==text && item.Title == '客观').showTitle
+        }
+      }else{
+        // 主客观都选 呈现的报价单为总共的那一份
+        e.showValue = selectAllArr.value.find(item => item.name == text).value
+        e.showTitle = selectAllArr.value.find(item => item.name == text).showTitle
+      }
+      return true;
+    }else{
+      return false;
+    }
+  }
+
+}
+//初始化表格数据
+const initTableData=()=>{
+  // console.log(props.serviceData,props.hasSercive,'this.serviceData,this.hasSercive');
+  let arr = [];
+  tableData.spanMergeCount=[]
+  props.serviceData.forEach((item, num) => {
+    let temarr = []
+    if (item.Children) {
+      // 第二层
+      tableData.spanMergeCount.push(item.Children.length)
+      item.Children.map((e, index) => {
+        if(e.Children){
+          // 第三层
+          tableData.spanMergeCount[num]=tableData.spanMergeCount[num]-1+e.Children.length
+          // 有分主客观的项
+          selectAllArr.value.push({
+            name:e.Title,
+            isIndeterminate:false,
+            checkAll:false,
+            ServiceTemplateId:e.ServiceTemplateId,
+            value:e.Value,
+            ChartPermissionId:e.ChartPermissionId,
+            showTitle:e.Title
+          })
+          haveChildList.value.push(e.Title)
+          e.Children.map((it,i)=>{
+            temarr.push({
+              ...it,
+              fatherName: e.Title,
+              grandFatherName:item.Title,
+              text2: "下载报价单",
+              text1: e.Detail ? "选择品种" : "查看报价单",
+              selected: false,
+              disable: false,
+              // 预览时的值 选择主观、客观 和主客观都选时展示的不一样
+              // 用于预览和展示报价单
+              showValue:e.Value,
+              showTitle:`${e.Title}(${it.Title})`
+            });
+          })
+        }else{
+          temarr.push({
+            ...e,
+            fatherName: e.Title,
+            grandFatherName:item.Title,
+            text2: "下载报价单",
+            text1: e.Detail ? "选择品种" : "查看报价单",
+            selected: false,
+            disable: false,
+            showValue:e.Value,
+            showTitle:e.Title
+          });
+        }
+      });
+      arr = [...arr, ...temarr];
+    } else {
+      arr.push({ 
+        ...item, 
+        selected: false, 
+        disable: false, 
+        fatherName: item.Title, 
+        grandFatherName: item.Title, 
+        text2: "下载报价单", 
+        text1: item.Detail ? "选择品种" : "查看报价单" });
+    }
+  });
+  // console.log(arr,'arr');
+  
+  tableData.list = arr;
+  // 升级套餐
+  upgradeServes.value = tableData.list.filter(item => item.fatherName.indexOf('升级')!=-1)
+  // console.log(selectAllArr.value,'this.selectAllArr',haveChildList.value);
+  // 回显(合同详情或者是需要编辑的,如果是合同详情则canEdit=false 则全部禁用)
+  // console.log(props.hasSercive,'this.hasSercive');
+  // 目前 医药、消费、科技、智造 分为了主客观
+  if (props.hasSercive&&props.hasSercive.length !== 0) {
+    props.hasSercive.forEach((item) => {
+      // 区分主客观的 主客观都有勾选时,只会返回一个基本套餐名称
+      let basicItem = selectAllArr.value.find(it => it.ServiceTemplateId==item.ServiceTemplateId)
+      if(basicItem){
+        checkList.value.push(`${basicItem.name}(主观)`,`${basicItem.name}(客观)`)
+        // e.fatherName == item.Title
+        arr.filter(e => [item.ServiceTemplateId*2-1,item.ServiceTemplateId*2].includes(e.ServiceTemplateId))
+        .map(it => {
+          handleChange(checkList.value,it)
+        })
+        return 
+      }
+
+      let serviceRow = arr.find(e => e.ServiceTemplateId==item.ServiceTemplateId)
+      if(!serviceRow) return
+      if(item.Title.indexOf("主观")!==-1 || item.Title.indexOf("客观")!==-1){
+        // 兼容之前合同数据的回显
+        checkList.value.push(`${serviceRow.fatherName}(${serviceRow.Title})`)
+      }else{
+        checkList.value.push(item.Title)
+      }
+      handleChange(checkList.value,serviceRow)
+    });
+
+    arr.forEach((item) => {
+      if (item.grandFatherName !== tableData.radio) {
+        item.disable = true;
+      }
+    });
+
+    // 如果是详情即 canEdit=false 则全部禁用
+    if (!props.canEdit) {
+      arr.forEach((item) => {
+        item.disable = true;
+      });
+    }
+    
+    
+  } else {
+    //不需要回显则是新添合同 则初始化将其全部禁用
+    // arr.forEach((item) => {
+    //   item.disable = true;
+    // });
+    //不需要回显则是新添合同 则初始化选择第一个
+    // arr.forEach((item, index) => {
+    //   if (index === 0) {
+    //     item.selected = true;
+    //     item.disable = false;
+    //     this.checkList.push(item.Title);
+    //     this.tableData.radio = item.fatherName;
+    //   }
+    // });
+  }
+}
+
+//合并单元格
+const objectSpanMethod=({ row, column, rowIndex, columnIndex  })=>{
+  if(columnIndex === 0){
+    if(rowIndex == 0){
+      // 权益大套餐
+      return [tableData.spanMergeCount[0],1]
+    }else if(rowIndex == tableData.spanMergeCount[0]){
+      // 行业套餐
+      return [tableData.spanMergeCount[1],1]
+    }else{
+      return [0,0]
+    }
+  }
+  // 45万 70万 医药、消费、科技、智造的升级
+  if([5,6,23,24,25,26,12].includes(row.ServiceTemplateId)){
+    if(columnIndex==1){
+      return [1,2]
+    }else if(columnIndex==2){
+      return [0,0]
+    }
+  }
+  if([1,3,4].includes(columnIndex)){
+    if([15,17,19,21].includes(row.ServiceTemplateId)){
+      // 医药、消费、科技、智造的主观
+      return [2,1]
+    }else if([16,18,20,22].includes(row.ServiceTemplateId)){
+      // 医药、消费、科技、智造的客观
+      return [0,0]
+    }
+  }
+}
+
+defineExpose({
+  selectAllArr,
+  checkList,
+  tableData
+})
+
+//   初始化数据
+watch(()=>props.serviceData,()=>{
+  initTableData();
+},
+{immediate:true})
+
+</script>
+
+<template>
+  <el-table border :data="tableData.list" style="width: 1000px" :span-method="objectSpanMethod">
+    <el-table-column label="套餐类型" width="200" align="center">
+      <template #default="scope">
+        <el-radio :disabled="!canEdit" v-model="tableData.radio" :label="scope.row.grandFatherName" @change="handleRadioChange" style="width: 100%; text-align: left; padding-left: 50px">{{
+          scope.row.grandFatherName
+        }}</el-radio>
+      </template>
+    </el-table-column>
+
+    <el-table-column>
+      <template #default="scope">
+        <el-checkbox :disabled="scope.row.disable" style="width: 100%; text-align: left; padding-left: 50px" 
+        v-model="selectAllArr.find(item => item.name == scope.row.fatherName).checkAll" 
+        :indeterminate="selectAllArr.find(item => item.name == scope.row.fatherName).isIndeterminate"
+          v-if="haveChildList.includes(scope.row.fatherName)"  @change="handleCheckAllChange(scope.row)">
+          {{scope.row.fatherName}}
+        </el-checkbox>
+        <el-checkbox-group v-model="checkList" @change="handleChange($event,scope.row)"  v-else>
+          <el-checkbox :label="scope.row.fatherName" :disabled="scope.row.disable" style="width: 100%; text-align: left; padding-left: 50px"></el-checkbox>
+        </el-checkbox-group>
+      </template>
+    </el-table-column>
+    <el-table-column>
+      <template #default="scope">
+        <el-checkbox-group v-model="checkList" @change="handleChange($event,scope.row)">
+          <el-checkbox :label="scope.row.fatherName+'('+scope.row.Title+')'" 
+           :disabled="scope.row.disable" style="width: 100%; text-align: left; padding-left: 50px">
+            {{scope.row.Title}}
+          </el-checkbox>
+        </el-checkbox-group>
+      </template>
+    </el-table-column>
+    <el-table-column width="200">
+      <template #default="scope">
+        <span style="cursor: pointer; color: #409eff; padding-left: 50px" v-if="showText(scope.row)" @click="handleShow(scope.row)">{{ scope.row.text1 }}</span>
+      </template>
+    </el-table-column>
+    <el-table-column width="200" align="center">
+      <template #default="scope">
+        <a :href="scope.row.showValue + '?response-content-type=application/octet-stream'" download style="cursor: pointer; color: #409eff" v-if="showText(scope.row)">{{
+          scope.row.text2
+        }}</a>
+      </template>
+    </el-table-column>
+  </el-table>
+</template>
+
+<style lang="scss" scoped></style>

+ 831 - 0
src/views/contract_manage/components/ServiceDialog.vue

@@ -0,0 +1,831 @@
+<script setup>
+import html2canvas from "html2canvas";
+import { contractInterface } from "@/api/api.js";
+import { ref,reactive,watch } from "vue";
+import { ElMessage } from "element-plus";
+
+const props = defineProps({
+  serviceCon: null,
+  serviceShow: {
+    type: Boolean,
+    default: false,
+  },
+  contractType:''
+});
+
+const emits=defineEmits(['serviceSave','serviceClose'])
+
+const showOpt=ref(false)//选择权限弹窗
+const optionLists=ref([]) //权限列表数据
+const tableHeadData=ref([]) //表头数据
+const tableData=ref([]) //表格数据
+const roleType=ref(localStorage.getItem("Role"))
+const tableSelectList=reactive({
+  商品复盘: [
+    { id: 2, name: "能化专栏《化里化外》", select: false },
+    { id: 4, name: "黑色专栏《知白守黑》", select: false },
+    { id: 5, name: "有色专栏《有声有色》", select: false },
+    // { id: 1, name: "《每日宏观商品复盘》", select: false },
+    { id: 3, name: "《股债日评》", select: false },
+  ],
+  深度月报:[
+    { id: 6, name: "宏观经济", select: false },
+    { id: 7, name: "草根调研", select: false },
+    // { id: 8, name: "PVC月报", select: false },
+  ]
+}) //表格中自带选择的数据
+const tableSelectCheck=reactive({
+  商品复盘: [],
+}) //表格中自带选择的数据选中项
+
+const position=reactive({
+  top: 0,
+  left: 0,
+}) //鼠标右击时的位置
+const showRightClickMenu=ref(false) //显示右键菜单
+const rightClickCon=ref([
+  { type: "selectRow", name: "选择该行" },
+  { type: "insertColumn", name: "插入一列" },
+  { type: "insertRow", name: "插入一行" },
+  { type: "deleteColumn", name: "删除该列" },
+  { type: "deleteRow", name: "删除该行" },
+]) //右键菜单内容
+
+const showInputHead=ref(false) //显示填写表头
+const form=reactive({
+  title: "",
+}) //插入一列填写的表头数据
+
+const formRule=reactive({
+  title: [{ required: true, message: "请填写名称", trigger: "blur" }],
+})
+
+const selectIndex=reactive({
+  rindex: 0, //行序号
+  cindex: 0, //列序号
+})
+const selectRowIndex=ref(null) //选择该行的序列
+
+// 初始化表格数据
+watch(()=>props.serviceCon,()=>{
+  initData();
+},{
+  // 开启深度监听,监听到‘补充协议’与其他合同类型切换时,对象里面客观经济的选中变化
+  deep:true
+})
+
+//选择上面选项更新表格数据
+// optionLists: {
+//   handler(nval, oval) {
+//     if (this.selectRowIndex === null) return;
+//     let arr = [];
+//     let arrId = [];
+
+//     let count = 0;
+//     nval.forEach((item) => {
+//       if (item.CheckAll) {
+//         count++;
+//       }
+//     });
+//     //全选
+//     if (count === nval.length) {
+//       arr = ["全品种"];
+//       nval.forEach((item) => {
+//         arrId = [...arrId, ...item.CheckList];
+//       });
+//     } else {
+//       nval.forEach((item) => {
+//         item.Items.forEach((item2) => {
+//           let flag = item.CheckList.indexOf(item2.ChartPermissionId);
+//           if (flag != -1) {
+//             arr.push(item2.PermissionName);
+//             arrId.push(item2.ChartPermissionId);
+//           }
+//         });
+//       });
+//     }
+
+//     // console.log(arr);
+//     this.tableData[this.selectRowIndex].forEach((item) => {
+//       if (item.HeadName === "品种") {
+//         item.Value = arr.join("、");
+//         item.ValueId = arrId;
+//       }
+//     });
+//   },
+//   deep: true,
+// },
+
+// 如果没有选择任何行则重置顶部选项数据
+watch(selectRowIndex,(nval, oval)=>{
+  if (nval === null) {
+    optionLists.value.forEach((item) => {
+      item.CheckList = [];
+    });
+  }
+})
+
+const initData=()=>{
+  console.log("初始化");
+  if (!props.serviceCon.Detail) return;
+  let temarr = props.serviceCon.Detail.map((rowItem) => {
+    let rowArr = [];
+    for (let key in rowItem) {
+      if (key.substr(0, 3) === "Col" && rowItem[key] !== "") {
+        rowArr.push(JSON.parse(rowItem[key]));
+      }
+    }
+    return rowArr;
+  });
+
+  tableHeadData.value = temarr[0];
+  tableData.value = temarr.slice(1);
+  // 回显表格内部选择的
+  tableData.value.forEach((item) => {
+    item.forEach((e) => {
+      if (e.Type === "select") {
+        let key = e.Value;
+        item.forEach((e2) => {
+          if (e2.HeadName === "品种") {
+            if(e2.Value){
+              tableSelectCheck[key] = e2.ValueId;
+            }else{
+              tableSelectCheck[key] = [];
+            }
+          }
+        });
+      }
+    });
+  });
+}
+
+//点击保存选择的权限数据
+const handleSaveOpt=()=>{
+  if (selectRowIndex.value === null) return;
+  let arr=[]
+  let arrId=[]
+  let count = 0
+  optionLists.value.forEach((item) => {
+    if (item.CheckAll) {
+      count++;
+    }
+  });
+  // 全选
+  if(count===optionLists.value.length){
+    arr = ["全品种"];
+    optionLists.value.forEach((item) => {
+      arrId = [...arrId, ...item.CheckList];
+    });
+  }else{
+    optionLists.value.forEach((item) => {
+      item.Items.forEach((item2) => {
+        let flag = item.CheckList.indexOf(item2.ChartPermissionId);
+        if (flag != -1) {
+          arr.push(item2.PermissionName);
+          arrId.push(item2.ChartPermissionId);
+        }
+      });
+    });
+  }
+
+  // 找出和该行关联的行 同时修改品种栏数据
+  let rowIndexarr=[]
+  if(!tableData.value[selectRowIndex.value][0].RowName){
+    rowIndexarr.push(selectRowIndex.value)
+  }else{
+    tableData.value.forEach((item,index)=>{
+      if(item[0].RowName==tableData.value[selectRowIndex.value][0].RowName){
+        rowIndexarr.push(index)
+      }
+    })
+  }
+  
+  console.log(rowIndexarr);
+
+  rowIndexarr.forEach(index=>{
+    tableData.value[index].forEach((item) => {
+      if (item.HeadName === "品种") {
+        item.Value = arr.join("、");
+        item.ValueId = arrId;
+      }
+    });
+  })
+
+  // this.tableData[this.selectRowIndex].forEach((item) => {
+  //   if (item.HeadName === "品种") {
+  //     item.Value = arr.join("、");
+  //     item.ValueId = arrId;
+  //   }
+  // });
+  showOpt.value=false
+}
+
+// 点击关闭选择权限弹窗
+const closeOpt=()=>{
+  showOpt.value=false
+  selectRowIndex.value=null
+}
+
+// 左键选择行
+const handleSelectRow=(rindex,data)=>{
+  // 如果选择的行存在 选项数据则不允许选择 或者 该行不可选择 即RowDisable=true
+  if(data.HeadName!='品种') return
+  let tag = false;
+  tableData.value[rindex].forEach((item) => {
+    if (item.Type === "select"||item.RowDisable) {
+      tag = true;
+    }
+  });
+  if (tag) return;
+  selectRowIndex.value = rindex;
+  formatOptionList();
+  showOpt.value=true
+}
+
+//获取顶部权限选项
+const getPermissionOpt=()=>{
+  let CompanyType = localStorage.getItem("Role") === "ficc_seller" ? "ficc" : "权益";
+  contractInterface.getPermissionList({ CompanyType }).then((res) => {
+    if (res.Ret === 200) {
+      let arr = res.Data.List;
+      arr.forEach((item) => {
+        item.CheckList = [];
+        item.CheckAll = false;
+        item.indeterminate = false;
+      });
+      optionLists.value = arr;
+    }
+  });
+}
+
+//每次选中一行时格式化顶部选择数据
+const formatOptionList=()=> {
+  let valueId = [];
+  tableData.value[selectRowIndex.value].forEach((item) => {
+    if (item.HeadName === "品种") {
+      valueId = item.ValueId;
+    }
+  });
+
+  optionLists.value.forEach((item) => {
+    item.CheckList = [];
+    item.Items.forEach((e) => {
+      let flag = valueId.indexOf(e.ChartPermissionId);
+      if (flag != -1) {
+        item.CheckList.push(e.ChartPermissionId);
+      }
+    });
+    if (item.CheckList.length === item.Items.length) {
+      item.CheckAll = true;
+    } else {
+      item.CheckAll = false;
+    }
+  });
+}
+
+//全选
+const handleCheckAllChange=(e)=>{
+  let arr = e.Items.map((item) => {
+    return item.ChartPermissionId;
+  });
+  e.CheckList = e.CheckAll ? arr : [];
+  e.indeterminate = false;
+  // 需求更改,合同类型为补充协议时不默认勾选宏观经济
+  if(e.ClassifyName==='宏观经济'&&props.contractType!=='补充协议'){
+    e.CheckList.push(1)
+  }
+}
+
+//选择单个
+const handleCheckChange=(e)=>{
+  e.CheckAll = e.CheckList.length === e.Items.length;
+  e.indeterminate = e.CheckList.length > 0 && e.CheckList.length < e.Items.length;
+}
+
+//点击表格中选项数据
+const handleTableSelectChange=(title, rindex)=>{
+  let arr = tableSelectCheck[title];
+  let arr2 = [];
+  tableSelectList[title].forEach((item) => {
+    arr.forEach((e) => {
+      if (e == item.id) {
+        arr2.push(item.name);
+      }
+    });
+  });
+  tableData.value[rindex].forEach((item) => {
+    if (item.HeadName === "品种") {
+      item.Value = arr2.join("、");
+      item.ValueId = arr;
+    }
+  });
+}
+
+//鼠标右键
+const rightClick=(rindex, cindex, event)=>{
+  selectIndex.rindex = Number(rindex);
+  selectIndex.cindex = Number(cindex);
+  formatRightClickCon(rindex, cindex);
+  showRightClickMenu.value = true;
+  position.top = event.clientY + "px";
+  position.left = event.clientX + "px";
+}
+
+//格式化鼠标右击显示内容
+const formatRightClickCon=(rindex, cindex)=>{
+  let arr = [
+    { type: "insertColumn", name: "向后插入一列", show: true },
+    { type: "insertRow", name: "向后插入一行", show: true },
+    { type: "deleteColumn", name: "删除该列", show: true },
+    { type: "deleteRow", name: "删除该行", show: true },
+  ];
+
+  // 品种一列不允许删除
+  if (tableHeadData.value[cindex].Tag === "品种"||tableHeadData.value[cindex].Tag === "FICC小套餐服务内容") {
+    arr[2].show = false;
+  }
+  //删除列 不能少于两列
+  if (tableHeadData.value.length <= 2) {
+    arr[2].show = false;
+  }
+
+  //删除行 不能少于两行 并且商品复盘、周报、双周报、数据点评、月报必须有一个存在
+  if (tableData.value.length <= 2) {
+    arr[3].show = false;
+  }
+
+  let temarr=[]
+  tableData.value.forEach(item=>{
+    item.forEach(item2=>{
+      if(item2.HeadName==='FICC小套餐服务内容'){
+        if(item2.Value==='数据点评'||
+        item2.Value==='FICC周报'||
+        item2.Value==='商品双周报+线上电话会讨论会<br/>(由弘则的研究员主持线上讨论)'){
+          temarr.push(item2.Value)
+        }
+      }
+    })
+  })
+  if(temarr.length<2){
+    tableData.value[selectIndex.rindex].forEach(item=>{
+      if(item.Value==='数据点评'||
+        item.Value==='FICC周报'||
+        item.Value==='商品双周报+线上电话会讨论会<br/>(由弘则的研究员主持线上讨论)'){
+          arr[3].show = false;
+      }
+    })
+  }
+
+  //新增列 不能超过6列
+  if (tableHeadData.value.length >= 6) {
+    arr[0].show = false;
+  }
+
+  rightClickCon.value = arr.filter((item) => {
+    return item.show;
+  });
+}
+
+// 点击 新增/删除 行/列  选择该行
+const handleUpdateTable=(type)=>{
+  //删除行
+  if (type === "deleteRow") {
+    selectRowIndex.value = null;
+    tableData.value.splice(selectIndex.rindex, 1);
+  }
+
+  //新增行
+  if (type === "insertRow") {
+    selectRowIndex.value = null;
+    let arr = tableHeadData.value.map((item) => {
+      let obj = { CanEdit: true, Type: "text", Value: "", ValueId: [], HeadName: item.Tag,RowDisable:false,RowName:''};
+      if (item.Tag === "品种") {
+        obj.CanEdit = false;
+      }
+      return obj;
+    });
+
+    tableData.value.splice(selectIndex.rindex + 1, 0, arr);
+  }
+
+  //删除列
+  if (type === "deleteColumn") {
+    selectRowIndex.value = null;
+    tableHeadData.value.splice(selectIndex.cindex, 1);
+    tableData.value = tableData.value.map((item) => {
+      item.splice(selectIndex.cindex, 1);
+      return item;
+    });
+  }
+
+  //新增列
+  if (type === "insertColumn") {
+    selectRowIndex.value = null;
+    showInputHead.value = true;
+  }
+
+  showRightClickMenu.value = false;
+}
+
+const headTitleForm=ref(null)
+
+//点击确定新增表头内容
+const handleConfirmHead=()=>{
+  headTitleForm.value.validate((valid) => {
+    if (!valid) return;
+    tableHeadData.value.splice(selectIndex.cindex + 1, 0, { CanEdit: false, Type: "text", Value: form.title, Tag: form.title });
+    tableData.value = tableData.value.map((item) => {
+      item.splice(selectIndex.cindex + 1, 0, { CanEdit: true, Type: "text", Value: "", HeadName: form.title,RowDisable:false,RowName:'' });
+      return item;
+    });
+    showInputHead.value = false;
+    form.title = "";
+  });
+}
+
+
+//点击取消填写新增列头弹窗
+const handleCancelHead=()=>{
+  headTitleForm.value.resetFields();
+  showInputHead.value = false;
+}
+
+//点击保存
+const handleSave=()=>{
+  let flag = validateData();
+  if (!flag) return;
+
+  // 生成表格图片
+  html2canvas(document.getElementById("table-png"), {
+    backgroundColor: "#ffffff",
+    useCORS: true, // 允许图片跨域
+    allowTaint: true, // 在渲染前测试图片
+    imageTimeout: 0, // 加载延时
+    scale:3,
+  }).then((res) => {
+    let img = res.toDataURL("image/png");
+    emits("serviceSave", {
+      Value: img,
+      ServiceTemplateId: props.serviceCon.ServiceTemplateId,
+      tableData: tableData.value,
+      tableHeadData: tableHeadData.value,
+    });
+    handleServiceClose();
+  });
+}
+
+//点击取消
+const handleCancel=()=>{
+  initData()
+  handleServiceClose();
+}
+
+//校验数据
+const validateData=()=>{
+  //判断品种列是否有值
+  let arr = [];
+  tableData.value.forEach((item) => {
+    item.forEach((item2) => {
+      if (item2.HeadName === "品种") {
+        arr.push(...item2.ValueId);
+      }
+    });
+  });
+  // 判断行是否有值
+  let arr2 = [];
+  tableData.value.forEach((item, index) => {
+    let arr = item.filter((item2) => {
+      if (!item2.Value) return item2;
+    });
+    if (arr.length === tableHeadData.value.length) {
+      arr2.push(index);
+    }
+  });
+
+  // 商品复盘、周报、双周报、数据点评、月报 品种必须有一个选择了
+  let arr3=[]
+  tableData.value.forEach(item=>{
+    item.forEach(item2=>{
+      if(item2.Value==='数据点评'||
+        item2.Value==='FICC周报'||
+        item2.Value==='商品双周报+线上电话会讨论会<br/>(由弘则的研究员主持线上讨论)'){
+          item.forEach(item3=>{
+            if (item3.HeadName === "品种") {
+              arr3.push(...item3.ValueId);
+            }
+          })
+      }
+    })
+  })
+
+  let constractType=props.contractType
+  console.log(constractType);
+  if (!arr.length&&constractType!=='补充协议') {
+    ElMessage.warning("至少选择一个品种");
+    return false;
+  }
+
+  if (arr2.length) {
+    ElMessage.warning("行内至少填一项");
+    return false;
+  }
+
+  if(!arr3.length&&constractType!=='补充协议'){
+    ElMessage.warning("周报、双周报、数据点评至少选择一个品种");
+    return false;
+  }
+
+  return true;
+}
+
+//关闭弹窗
+const handleServiceClose=()=>{
+  showRightClickMenu.value = false;
+  selectRowIndex.value = null;
+  emits("serviceClose");
+}
+
+defineExpose({
+  tableHeadData,tableData
+})
+
+getPermissionOpt();
+</script>
+
+<template>
+  <div class="service-content-wrap">
+    <!-- 大套餐或其他等直接查看图片类型的 -->
+    <el-dialog v-dialogDrag top="5vh" :model-value="props.serviceShow" class="self-dialog-c" :modal-append-to-body="false" @close="handleServiceClose" :show-close="false" v-if="!serviceCon.Detail">
+      <!-- <h2 class="title">{{ serviceCon.Title }}</h2> -->
+      <template #header>
+        <!-- 权益合同和ficc合同有别 -->
+        {{roleType}}
+        <h2 class="title">{{roleType == 'ficc_seller'?serviceCon.Title:serviceCon.showTitle }}</h2>
+      </template>
+      <img class="img" style="width: 100%; display: block; margin: 0 auto 20px auto" 
+      :src="roleType == 'ficc_seller'?serviceCon.Value:serviceCon.showValue" />
+      <div style="text-align: center; margin-bottom: 30px">
+        <el-button type="primary" @click="handleServiceClose">知道了</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 小套餐 -->
+    <el-dialog v-dialogDrag title="提示" width="65%" top="5vh"  :model-value="props.serviceShow" :modal-append-to-body="false" @close="handleCancel"  v-else>
+      <template #header>
+        <img width="20" src="../../../assets/img/constract/select.png"  style="vertical-align: middle;margin-right: 4px" />
+        <span style="vertical-align: middle">选择品种</span>
+      </template>
+
+      <!-- 选择种类部分  -->
+      <!-- <div class="type-select-wrap">
+        <h2>权限设置</h2>
+        <div class="type-select-box">
+          <div class="item flex" v-for="item in optionLists" :key="item.ClassifyName" style="margin-bottom: 20px">
+            <el-checkbox
+              :disabled="selectRowIndex === null"
+              :indeterminate="item.CheckList.length > 0 && item.CheckList.length < item.Items.length"
+              v-model="item.CheckAll"
+              @change="handleCheckAllChange(item)"
+              >{{ item.ClassifyName }}:</el-checkbox
+            >
+            <el-checkbox-group :disabled="selectRowIndex === null" v-model="item.CheckList" @change="handleCheckChange(item)">
+              <el-checkbox :label="tag.ChartPermissionId" v-for="tag in item.Items" :key="tag.ChartPermissionId">{{ tag.PermissionName }}</el-checkbox>
+            </el-checkbox-group>
+          </div>
+        </div>
+      </div> -->
+
+      <!-- 表格部分 -->
+      <div style="max-height: 70vh; overflow-y: auto; padding: 10px 0">
+        <table id="table" ref="table" class="table-wrap" @click="showRightClickMenu = false">
+          <thead>
+            <tr style="background: #f0f2f5">
+              <th class="table-item" v-for="item in tableHeadData" :key="item.Value">{{ item.Value }}</th>
+            </tr>
+          </thead>
+
+          <tbody>
+            <tr v-for="(row, rindex) in tableData" :key="rindex" :class="rindex === selectRowIndex ? 'row-dark' : null" >
+              <td 
+                class="table-item" 
+                v-for="(item, cindex) in row" 
+                :key="item" 
+                @click="handleSelectRow(rindex,item)" 
+                @contextmenu.prevent="rightClick(rindex, cindex, $event)" 
+                :data-rindex="rindex" 
+                :data-cindex="cindex"
+              >
+                <div v-if="item.Type === 'text'">
+                  <img 
+                    class="choose-icon" 
+                    width="14" 
+                    src="../../../assets/img/constract/icon-1.png" 
+                    alt="" 
+                    v-if="item.HeadName=='品种'&&!item.RowDisable"
+                  />
+                  <span 
+                    v-if="!item.CanEdit" 
+                    v-html="item.Value" 
+                    :style="{paddingLeft:item.HeadName=='品种'&&!item.RowDisable?'15px':null,display:'inline-block'}"
+                  ></span>
+                  <el-input v-else type="textarea" placeholder="请输入" v-model="item.Value"></el-input>
+                </div>
+                <div v-if="item.Type === 'select'">
+                  <div style="margin-bottom: 10px;font-size:15px">{{ item.Value }}</div>
+                  <div style="text-align: left">
+                    <el-checkbox-group v-model="tableSelectCheck[item.Value]">
+                      <el-checkbox style="font-size:14px;height: 19px" :label="opt.id" 
+                      v-for="opt in tableSelectList[item.Value]" :key="opt.id" @change="handleTableSelectChange(item.Value, rindex, cindex)">{{ opt.name }}</el-checkbox>
+                    </el-checkbox-group>
+                  </div>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+
+      <div style="margin: 40px 0; text-align: center">
+        <el-button type="primary" style="margin-right: 44px" @click="handleSave" size="large">保存</el-button>
+        <el-button type="primary" plain @click="handleCancel" size="large">取消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 权限选择弹窗 -->
+    <el-dialog v-model="showOpt" :modal-append-to-body="false" @close="closeOpt" v-dialogDrag>
+      <template #header>
+        <img style="vertical-align: middle;margin-right: 4px;" width="20" src="../../../assets/img/icons/add_icon.png" alt="" />
+        <span style="vertical-align: middle">选择品种</span>
+      </template>
+      <div class="type-select-wrap">
+        <div class="type-select-box">
+          <div class="item flex" v-for="item in optionLists" :key="item.ClassifyName" style="margin-bottom: 30px">
+            <el-checkbox
+              :disabled="selectRowIndex === null"
+              :indeterminate="item.CheckList.length > 0 && item.CheckList.length < item.Items.length"
+              v-model="item.CheckAll"
+              @change="handleCheckAllChange(item)"
+              >{{ item.ClassifyName }}:</el-checkbox
+            >
+            <el-checkbox-group :disabled="selectRowIndex === null" v-model="item.CheckList" @change="handleCheckChange(item)">
+              <el-checkbox :disabled="contractType!=='补充协议'&&tag.PermissionName==='宏观经济'" :label="tag.ChartPermissionId" v-for="tag in item.Items" :key="tag.ChartPermissionId">{{ tag.PermissionName }}</el-checkbox>
+            </el-checkbox-group>
+          </div>
+        </div>
+        <div style="text-align:center">
+          <el-button type="primary" @click="handleSaveOpt" size="large">保存</el-button>
+          <el-button type="primary" plain @click="closeOpt" size="large">取消</el-button>
+        </div>
+      </div>
+    </el-dialog>
+
+    <!-- 新增列填写表头弹窗 -->
+    <el-dialog v-model="showInputHead" :modal-append-to-body="false" width="560px" top="20vh" v-dialogDrag>
+      <template #header>
+        <img style="vertical-align: middle" width="20" src="../../../assets/img/icons/add_icon.png" alt="" />
+        <span style="vertical-align: middle">添加列</span>
+      </template>
+      <el-form :model="form" ref="headTitleForm" :rules="formRule" label-width="100px" @submit.native.prevent>
+        <el-form-item label="表格名称" prop="title">
+          <el-input v-model="form.title"></el-input>
+        </el-form-item>
+        <div style="text-align: center; margin-top: 70px; margin-bottom: 30px">
+          <el-button type="primary" @click="handleConfirmHead" size="large">保存</el-button>
+          <el-button type="primary" plain @click="handleCancelHead" size="large">取消</el-button>
+        </div>
+      </el-form>
+    </el-dialog>
+
+    <!-- 右键选项弹框 -->
+    <div class="right-click-wrap" :style="position" v-show="showRightClickMenu">
+      <div @click="handleUpdateTable(item.type)" v-for="item in rightClickCon" :key="item.type">{{ item.name }}</div>
+    </div>
+
+    <!-- 生成表格图片dom -->
+    <table id="table-png" class="table-wrap">
+      <thead>
+        <tr style="background: #f0f2f5">
+          <th class="table-item" v-for="item in tableHeadData" :key="item.Value" style="color: #333;font-size:15px">{{ item.Value }}</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(row, rindex) in tableData" :key="rindex">
+          <td class="table-item" v-for="item in row" :key="item" style="max-width: 300px">
+            <div v-html="item.Value" style="color: #333;font-size:14px"></div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+
+<style>
+.self-dialog-c .el-dialog__header {
+  background-color: #fff;
+  padding: 15px 20px 0 20px !important;
+}
+.self-dialog-c{
+  width: 60%;
+}
+
+/* .el-checkbox{
+  margin-bottom: 20px;
+} */
+.table-item .el-checkbox__label {
+  font-size: 14px !important;
+}
+.type-select-wrap .el-checkbox{
+  margin-bottom: 10px;
+}
+.type-select-wrap .el-checkbox__label {
+  font-size: 14px !important;
+}
+</style>
+
+<style lang="scss" scoped>
+.img{
+  image-rendering: -moz-crisp-edges; 
+  image-rendering: -o-crisp-edges; 
+  image-rendering: -webkit-optimize-contrast; 
+  image-rendering: crisp-edges; 
+  -ms-interpolation-mode: nearest-neighbor;
+}
+
+
+.self-dialog-c .title {
+  text-align: center;
+  font-size: 18px;
+  color: #333;
+}
+.flex {
+  display: flex;
+}
+.table-wrap {
+  position: relative;
+  width: 100%;
+  text-align: center;
+  border-top: 1px solid #ebeef5;
+  border-left: 1px solid #ebeef5;
+  .row-dark {
+    background-color: #deedff;
+  }
+  .table-item {
+    padding: 20px 10px;
+    border-right: 1px solid #ebeef5;
+    border-bottom: 1px solid #ebeef5;
+    min-width: 160px;
+    position: relative;
+    .table-item-mask {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: rgba(0, 0, 0, 0);
+      z-index: 1000;
+    }
+    .choose-icon{
+      position: absolute;
+      top: 50%;
+      left: 10px;
+      transform:translateY(-50%);
+    }
+  }
+}
+.right-click-wrap {
+  border-radius: 4px;
+  background-color: #fff;
+  padding: 10px 0;
+  position: fixed;
+  z-index: 5000;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  line-height: 2;
+  div {
+    cursor: pointer;
+    padding: 0 20px;
+    &:hover {
+      background-color: #ebeef5;
+    }
+  }
+}
+
+.type-select-wrap {
+  margin-bottom: 30px;
+  .type-select-box {
+    // margin-top: 20px;
+    padding: 20px 30px 0 30px;
+    border: 1px dashed #aab4cc;
+    border-radius: 4px;
+    margin-bottom: 20px;
+  }
+}
+
+
+#table-png {
+  position: absolute;
+  z-index: -10;
+  top: -100%;
+  left: 0;
+  width: 1000px;
+}
+</style>

+ 480 - 3
src/views/contract_manage/contractDetail.vue

@@ -1,9 +1,486 @@
+
 <script setup>
+import { ref,reactive,computed,watch } from "vue";
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import { contractInterface } from "@/api/api.js";
+import {CalculationDate} from '@/utils/CalculationDate'
+import { useRouter,useRoute} from 'vue-router';
+const $router = useRouter()
+const $route = useRoute()
+  // beforeRouteLeave(to, from, next)  {
+  //   from.matched[0].name='合同管理'
+  //   next()
+  // },
+
+const RoleType=ref(localStorage.getItem("Role") || "")
+const contractId=ref($route.query.contractId)
+const operationList=ref([])//操作记录
+const formData=ref({}) //合同详情数据
+const radio=ref("1") //用于显示底部是否有备注内容
+const ficcServiceData=ref(null) //ficc 底部表格数据
+const qyServiceData=ref(null) //权益底部表格数据
+
+const serviceShow=ref(false) //是否显示查看报价单
+const serviceCon=ref({}) //选择的服务项 查看报价单/选择品种
+
+const showEdit=ref(false)//是否显示编辑按钮
+
+
+// filters
+const formatPrice=(str)=>{
+  if(!str) return ''
+  str=str.toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  return num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+// 金额转中文大写
+const digitUppercase=(n)=>{
+  let fraction = ["角", "分"];
+  let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+  let unit = [
+    ["元", "万", "亿"],
+    ["", "拾", "佰", "仟"],
+  ];
+  let head = n < 0 ? "欠" : "";
+  n = Math.abs(n);
+  let s = "";
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+  }
+  // s = s || "整";
+
+  n = Math.floor(n);
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = "";
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p;
+      n = Math.floor(n / 10);
+    }
+    s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+  }
+  return (
+    head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零")
+    // .replace(/^整$/, "零元整")
+  );
+}
+
+//计算多少年
+const formateYear=(e)=>{
+  if (e[0]) {
+    return `有效期为${CalculationDate(e[0],e[1])}`
+  } else {
+    return "";
+  }
+}
+
+//前往预览合同
+const handleGoPreview=()=>{
+  let {href} = $router.resolve({path:"/contractdtl",query:{contractid:contractId.value}});
+  window.open(href,'_blank');
+}
+
+//获取合同操作记录
+const getContractOperationList=()=>{
+  contractInterface.getContractOperationList({ContractId:contractId.value}).then(res=>{
+    if(res.Ret===200){
+      operationList.value=res.Data.List&&res.Data.List.map(item=>{
+        return {...item,CreateTimeStr:item.CreateTimeStr.replace(/-/g,'.')}
+      })||[]
+    }
+  })
+}
+
+//点击编辑 跳转编辑页
+const handleGoEdit=()=> {
+  $router.push({
+    path: "/editcontract",
+    query: {
+      contractId: contractId.value,
+      type: "edit",
+    },
+  });
+}
+
+//显示查看报价单弹窗
+const handleShowService=(e)=>{
+  console.log(e);
+  // 小套餐情况处理表格数据
+  if (e.Detail) {
+    let temarr = e.Detail.map((rowItem) => {
+      let rowArr = [];
+      for (let key in rowItem) {
+        if (key.substr(0, 3) === "Col" && rowItem[key] !== "") {
+          rowArr.push(JSON.parse(rowItem[key]));
+        }
+      }
+      return rowArr;
+    });
+    serviceCon.value = { ...e, tableHeadData: temarr[0], tableData: temarr.slice(1) };
+  } else {
+    serviceCon.value = e;
+  }
+  serviceShow.value = true;
+}
+
+//关闭查看报价单弹窗
+const serviceClose=()=>{
+  serviceShow.value = false;
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+  contractInterface.getServiceList({ ProductId: formData.value.ProductId }).then((res) => {
+    if (res.Ret === 200) {
+      if (formData.value.ProductId === 1) {
+        ficcServiceData.value = res.Data;
+      } else {
+        qyServiceData.value = res.Data;
+      }
+    }
+  });
+}
+
+// 获取合同详情
+const getDetail=()=>{
+  contractInterface.getContractDetail({ ContractId: Number(contractId.value) }).then((res) => {
+    if (res.Ret === 200) {
+      formData.value = { ...res.Data, timeRange: [res.Data.StartDate, res.Data.EndDate] };
+      if(res.Data.RelationContractDetailList.length>0){
+        let arr=[]
+        res.Data.RelationContractDetailList.forEach(item=>{
+          arr.push(item.CompanyName)
+        })
+        formData.value.PayChannel=arr.join('、')
+      }
+      if (res.Data.Remark) {
+        radio.value = "1";
+      } else {
+        radio.value = "2";
+      }
+      // 本人的合同并且合同状态不为 已审批、待审批、已作废、已签回、已解约 则显示编辑
+      let userid=localStorage.getItem('AdminId')
+      if(userid==res.Data.SellerId
+        &&res.Data.Status!=='待审批'
+        &&res.Data.Status!=='已审批'
+        &&res.Data.Status!=='已作废'
+        &&res.Data.Status!=='已签回'
+        &&res.Data.Status!=='已解约'){
+        showEdit.value=true
+      }else{
+        showEdit.value=false
+      }
+      getServiceList();
+    }
+  });
+}
+
+getDetail();
+getContractOperationList()
 
 </script>
 
 <template>
-    <div>
-        
+  <div class="flex addconstract-container">
+    <el-form ref="form" :model="formData" label-position="left" label-width="110px" style="width: 80%" size="large">
+      <section class="section">
+        <h2 class="section-title">客户信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="甲方名称" prop="customeName" style="width: 50%">
+              <el-input v-model="formData.CompanyName" placeholder="请输入或者搜索客户名称" style="width: 350px" disabled></el-input>
+            </el-form-item>
+            <el-form-item label="社会信用码" prop="customeXYM" style="width: 50%">
+              <el-input v-model="formData.CreditCode" placeholder="请输入社会信用码" style="width: 350px" disabled></el-input>
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="公司地址" prop="province" style="width: 50%">
+              <el-input :value="formData.Province + formData.City" disabled style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="详细地址" prop="address" style="width: 50%">
+              <el-input :value="formData.Address" disabled placeholder="请输入详细地址" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="传真" prop="customeCz" style="width: 50%">
+              <el-input v-model="formData.Fax" disabled placeholder="请输入传真" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="电话" prop="customeTel" style="width: 50%">
+              <el-input v-model="formData.Phone" disabled placeholder="请输入电话" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <el-form-item label="邮编" prop="postCode" class="border-top">
+            <el-input v-model="formData.Postcode" disabled placeholder="请输入邮编" style="width: 350px"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">合同信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="合同编号" style="width: 50%">
+              <span>{{ formData.ContractCode }}</span>
+            </el-form-item>
+            <el-form-item label="合同归属" style="width: 50%">
+              <span>{{ formData.ProductId === 1 ? "ficc" : "权益" }}</span>
+            </el-form-item>
+          </div>
+          <el-form-item label="合同类型" prop="constractType" class="border-top">
+            <el-radio-group v-model="formData.ContractType">
+              <div style="display:flex">
+                <div style="margin-right:10px;">
+                  <el-radio label="新签合同" disabled>新签合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px">
+                  <el-radio label="续约合同" disabled>续约合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>
+                      1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+										  2、所有客户在续约申请时提交的合同
+                    </template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px" v-if="RoleType!=='rai_seller'">
+                  <el-radio label="补充协议" disabled>补充协议</el-radio>
+                </div>
+              </div>
+            </el-radio-group>
+          </el-form-item>
+          <div class="flex border-top" v-if="formData.timeRange">
+            <el-form-item label="合同期限" prop="time" style="width: 50%">
+              <el-date-picker disabled v-model="formData.timeRange" type="daterange" range-separator="至" 
+              start-placeholder="开始日期" end-placeholder="结束日期" style="max-width:350px"> </el-date-picker>
+            </el-form-item>
+            <span style="line-height: 40px">{{ formateYear(formData.timeRange) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="合同金额" prop="price" style="width: 50%">
+              <el-input disabled :value="formatPrice(formData.OriginalPrice)" placeholder="请输入合同金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">大写:{{ digitUppercase(formData.OriginalPrice)  }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="优惠后金额" prop="price2" style="width: 50%">
+              <el-input disabled :value="formatPrice(formData.Price)" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">大写:{{ digitUppercase(formData.Price) }}</span>
+          </div>
+
+          <el-form-item label="付款方式说明" prop="remark" class="border-top">
+            <el-input type="textarea" disabled v-model="formData.PayRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+          </el-form-item>
+          <el-form-item label="付款方" prop="PayChannel" class="border-top">
+            <el-input type="textarea" disabled v-model="formData.PayChannel" placeholder="代付,请填写代付的券商名称或期货公司名称;非代付,请填写无。(请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+          </el-form-item>
+          <!-- <el-form-item label="备注" class="border-top">
+            <el-input type="textarea" disabled v-model="formData.SellerRemark" placeholder="审批人查看,不在生成合同中展示" style="width: 80%"></el-input>
+          </el-form-item> -->
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">服务内容</h2>
+        <div class="section-container">
+          <!-- ficc 服务内容表格 -->
+          <FiccServiceTable
+            :canEdit="false"
+            :serviceData="ficcServiceData"
+            :hasSercive="formData.Service"
+            @handleShowService="handleShowService"
+            v-if="formData.ProductId === 1 && ficcServiceData"
+          ></FiccServiceTable>
+
+          <!-- 权益 服务内容表格 -->
+          <QyServiceTable
+            :canEdit="false"
+            :serviceData="qyServiceData"
+            :hasSercive="formData.Service"
+            @handleShowService="handleShowService"
+            v-if="formData.ProductId === 2 && qyServiceData"
+          ></QyServiceTable>
+        </div>
+
+        <p style="margin-top: 30px; margin-bottom: 20px">补充内容(eg额外赠送、路演次数规定)</p>
+        <el-radio v-model="radio" label="2" disabled>无</el-radio>
+        <el-radio v-model="radio" label="1" disabled>有</el-radio>
+        <el-input v-if="radio === '1'" disabled type="textarea" v-model="formData.Remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">审批备注</h2>
+        <div class="section-container">
+          <el-form-item label="备注" class="border-top">
+            <el-input type="textarea" disabled v-model="formData.SellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+    </el-form>
+    <div class="right-wrap">
+      <div style="display: flex; justify-content: space-between">
+        <el-button type="primary" style="width: 45%" @click="handleGoEdit" v-if="showEdit" size="large">编辑</el-button>
+        <el-button type="primary" plain :style="{width: showEdit?'45%':'100%'}" @click="handleGoPreview" size="large">预览</el-button>
+      </div>
+      <div class="timeline-wrap">
+        <el-timeline>
+          <el-timeline-item color="#409EFF" v-for="item in operationList" :key="item.Id" placement="top" :timestamp="item.CreateTimeStr">
+            {{item.OpUserName}}{{ item.Remark }}
+          </el-timeline-item>
+        </el-timeline>
+      </div>
     </div>
-</template>
+
+    <!-- 查看报价单弹窗 -->
+    <el-dialog v-dialogDrag v-model="serviceShow" width="60%" class="self-dialog-c" :modal-append-to-body="false" @close="serviceClose" :show-close="false">
+      <h2 class="title" style="text-align: center; color: #000">{{ serviceCon.ProductId == '1'? serviceCon.Title : serviceCon.showTitle }}</h2>
+      <img class="img" style="width: 100%; display: block; margin: 20px auto" 
+      :src="serviceCon.ProductId == '1'? serviceCon.Value : serviceCon.showValue" />
+      <!-- <table v-else class="table-wrap">
+        <thead>
+          <tr style="background: #f0f2f5">
+            <th class="table-item" v-for="item in serviceCon.tableHeadData" :key="item.Value">{{ item.Value }}</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="(row, rindex) in serviceCon.tableData" :key="rindex" :class="rindex === selectRowIndex ? 'row-dark' : null">
+            <td class="table-item" v-for="item in row" :key="item">
+              <div>{{ item.Value }}</div>
+            </td>
+          </tr>
+        </tbody>
+      </table> -->
+      <div style="text-align: center; margin-bottom: 30px">
+        <el-button type="primary" @click="serviceShow =false">知道了</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style>
+.el-radio__input.is-checked .el-radio__inner {
+  background-color: transparent;
+}
+.el-radio__inner::after {
+  width: 6px;
+  height: 6px;
+  background-color: #409eff;
+}
+.el-checkbox__label {
+  font-size: 16px !important;
+}
+.el-form-item {
+  margin-bottom: 30px;
+}
+.el-input.is-disabled .el-input__inner {
+  color: #606266;
+}
+.el-radio__input.is-disabled + span.el-radio__label {
+  color: #606266;
+}
+.el-range-editor.is-disabled input {
+  color: #606266;
+}
+.el-textarea.is-disabled .el-textarea__inner {
+  color: #606266;
+}
+.el-timeline-item__tail {
+  border-left: 2px solid #409eff;
+}
+.self-dialog-c .el-dialog__header {
+  background-color: #fff;
+  padding: 0;
+}
+.self-dialog-c{
+  width: auto;
+  max-width: 60%;
+}
+</style>
+
+<style lang="scss" scoped>
+.img{
+  image-rendering: -moz-crisp-edges; 
+  image-rendering: -o-crisp-edges; 
+  image-rendering: -webkit-optimize-contrast; 
+  image-rendering: crisp-edges; 
+  -ms-interpolation-mode: nearest-neighbor;
+}
+.addconstract-container {
+  min-height: calc(100vh - 250px);
+  position: relative;
+  font-size: 16px;
+  color: #000;
+}
+.flex {
+  display: flex;
+}
+.section {
+  background-color: #fff;
+  border: 1px solid #aab4cc;
+  border-radius: 4px;
+  padding: 20px;
+  margin-bottom: 20px;
+  .section-title {
+    margin-bottom: 30px;
+  }
+  .border-top {
+    padding-top: 30px;
+    border-top: 1px solid #dcdfe6;
+  }
+}
+
+.right-wrap {
+  margin-left: 20px;
+  flex: 1;
+  .timeline-wrap {
+    margin-top: 20px;
+    background-color: #fff;
+    border: 1px solid #aab4cc;
+    padding: 20px;
+    border-radius: 4px;
+    text-align: left;
+    height: calc(100% - 120px);
+    max-height: 1130px;
+    overflow-y: auto;
+  }
+}
+
+.table-wrap {
+  margin: 20px 0;
+  position: relative;
+  width: 100%;
+  text-align: center;
+  border-top: 1px solid #ebeef5;
+  border-left: 1px solid #ebeef5;
+  .row-dark {
+    background-color: #deedff;
+  }
+  .table-item {
+    padding: 12px 10px;
+    border-right: 1px solid #ebeef5;
+    border-bottom: 1px solid #ebeef5;
+    min-width: 200px;
+    position: relative;
+  }
+}
+</style>

+ 454 - 2
src/views/contract_manage/contractDetailDC.vue

@@ -1,7 +1,459 @@
 <script setup>
+import { ref,nextTick } from "vue";
+import {ElMessage,ElMessageBox} from "element-plus"
+import { useRouter,useRoute} from 'vue-router';
+import _ from 'lodash'
+import { InfoFilled } from '@element-plus/icons-vue'
+
+const $route = useRoute()
+const $router = useRouter()
+
+import { contractInterface } from "@/api/api.js";
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import { CalculationDate } from "@/utils/CalculationDate";
+
+
+const RoleType=ref(localStorage.getItem("Role") || "")
+const formData=ref({})
+const formRule={
+CompanyName: [{ required: true, message: "请填写名称", trigger: "blur" }],
+CreditCode: [{ required: true, message: "请填写信用码", trigger: "blur" }],
+Province: [{ required: true, message: "请选择地址", trigger: "change" }],
+OriginalPrice: [
+    { required: true, message: "请填写代付金额", trigger: "blur" },
+    { pattern: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/, message: "请输入正确的格式,可保留两位小数" },
+],
+trueUseName: [{ required: true, message: "请输入关键字", trigger: "change" }],
+PayRemark: [{ required: true, message: "请填写付款方式说明", trigger: "blur" }],
+}
+
+const relationContractData=ref(null) //选择的关联合同数据
+const ficcServiceData=ref(null) //ficc 底部表格数据
+const qyServiceData=ref(null) //权益底部表格数据
+const serviceShow=ref(false) //是否显示查看报价单
+const serviceCon=ref({}) //选择的服务项 查看报价单/选择品种
+const operationList=ref([])
+ 
+const showEdit=ref(false)//是否显示编辑按钮
+
+// filters
+const formatPrice=(str)=>{
+    if(!str) return ''
+    str=str.toString()
+    let num1='',num2=''
+    if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+        num2=''
+    }
+    }else{
+        num1=str
+    }
+    return num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+// 金额转中文大写
+const digitUppercase=(n)=>{
+    if(!n) return ''
+    let fraction = ["角", "分"];
+    let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+    let unit = [
+        ["元", "万", "亿"],
+        ["", "拾", "佰", "仟"],
+    ];
+    let head = n < 0 ? "欠" : "";
+    n = Math.abs(n);
+    let s = "";
+    for (let i = 0; i < fraction.length; i++) {
+        s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+    }
+    // s = s || "整";
+
+    n = Math.floor(n);
+    for (let i = 0; i < unit[0].length && n > 0; i++) {
+        let p = "";
+        for (let j = 0; j < unit[1].length && n > 0; j++) {
+            p = digit[n % 10] + unit[1][j] + p;
+            n = Math.floor(n / 10);
+        }
+        s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+    }
+    return (
+    "大写:" + (head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零"))
+    // .replace(/^整$/, "零元整")
+    );
+}
+//计算多少年
+const formateYear=(e)=>{
+    if (e[0]) {
+        return `有效期为${CalculationDate(e[0], e[1])}`;
+    } else {
+        return "";
+    }
+}
+
+// 获取合同详情
+const getDetail=async()=>{
+    const res=await contractInterface.getContractDetail({
+        ContractId:$route.query.contractId
+    })
+    if(res.Ret===200){
+    formData.value=res.Data
+    relationContractData.value=res.Data.RelationContractDetailList[0]
+    getServiceList()
+    // 本人的合同并且合同状态不为 已审批、待审批、已作废、已签回、已解约 则显示编辑
+        let userid=localStorage.getItem('AdminId')
+        if(userid==res.Data.SellerId
+        &&res.Data.Status!=='待审批'
+        &&res.Data.Status!=='已审批'
+        &&res.Data.Status!=='已作废'
+        &&res.Data.Status!=='已签回'
+        &&res.Data.Status!=='已解约'){
+            showEdit.value=true
+        }else{
+            showEdit.value=false
+        }
+    }
+}
+
+//获取合同操作记录
+const getContractOperationList=()=>{
+    contractInterface.getContractOperationList({ContractId:$route.query.contractId}).then(res=>{
+    if(res.Ret===200){
+        operationList.value=res.Data.List&&res.Data.List.map(item=>{
+        return {...item,CreateTimeStr:item.CreateTimeStr.replace(/-/g,'.')}
+        })||[]
+    }
+    })
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+    contractInterface.getServiceList({ ProductId: relationContractData.value.ProductId }).then((res) => {
+    if (res.Ret === 200) {
+        if (relationContractData.value.ProductId === 1) {
+        ficcServiceData.value = res.Data;
+        } else {
+        qyServiceData.value = res.Data;
+        }
+    }
+    });
+}
+
+//显示查看报价单弹窗
+const handleShowService=(e)=>{
+    // 小套餐情况处理表格数据
+    if (e.Detail) {
+    let temarr = e.Detail.map((rowItem) => {
+        let rowArr = [];
+        for (let key in rowItem) {
+        if (key.substr(0, 3) === "Col" && rowItem[key] !== "") {
+            rowArr.push(JSON.parse(rowItem[key]));
+        }
+        }
+        return rowArr;
+    });
+    serviceCon.value = { ...e, tableHeadData: temarr[0], tableData: temarr.slice(1) };
+    } else {
+    serviceCon.value = e;
+    }
+    serviceShow.value = true;
+}
+
+//前往预览合同
+const handleGoPreview=()=>{
+        let {href} = $router.resolve({
+    path:"/contractdtl",
+    query:{contractid:$route.query.contractId}
+    });
+        window.open(href,'_blank');
+}
+
+//点击编辑 跳转编辑页
+const handleGoEdit=()=>{
+    $router.push({
+    path: "/editcontractdc",
+    query: {
+        contractId: $route.query.contractId,
+        type: "edit",
+    },
+    });
+}
+
+getDetail()
+getContractOperationList()
 
 </script>
 
 <template>
-    <div></div>
-</template>
+    <div class="contractdetail-dc flex">
+      <el-form :model="formData" label-position="left" :rules="formRule" label-width="110px" disabled style="width: 80%"
+      size="large">
+        <section class="section">
+          <h2 class="section-title">客户信息</h2>
+          <div class="section-container">
+            <div class="flex border-top" style="padding-top: 30px">
+              <el-form-item label="代付方" prop="CompanyName" style="width: 50%">
+                <el-input v-model="formData.CompanyName" placeholder="请输入名称" style="width: 350px"></el-input>
+              </el-form-item>
+              <el-form-item label="社会信用码" prop="CreditCode" style="width: 50%">
+                <el-input v-model="formData.CreditCode" placeholder="请输入社会信用码" style="width: 350px"></el-input>
+              </el-form-item>
+            </div>
+            <div class="flex border-top" style="padding-top: 30px">
+              <el-form-item label="公司地址" prop="Province" style="width: 50%">
+                <el-input :value="formData.Province + formData.City" disabled style="width: 350px"></el-input>
+              </el-form-item>
+              <el-form-item label="详细地址" prop="Address" style="width: 50%">
+                <el-input v-model="formData.Address" placeholder="请输入详细地址" style="width: 350px"></el-input>
+              </el-form-item>
+            </div>
+            <div class="flex border-top" style="padding-top: 30px">
+              <el-form-item label="传真" prop="Fax" style="width: 50%">
+                <el-input v-model="formData.Fax" placeholder="请输入传真" style="width: 350px"></el-input>
+              </el-form-item>
+              <el-form-item label="电话" prop="Phone" style="width: 50%">
+                <el-input v-model="formData.Phone" placeholder="请输入电话" style="width: 350px"></el-input>
+              </el-form-item>
+            </div>
+            <div class="flex border-top" style="padding-top: 30px">
+              <el-form-item label="邮编" prop="Postcode" style="width: 50%">
+                <el-input v-model="formData.Postcode" placeholder="请输入邮编" style="width: 350px"></el-input>
+              </el-form-item>
+              <el-form-item label="代付金额" prop="OriginalPrice" style="width: 50%">
+                <el-input :value="formatPrice(formData.OriginalPrice)" placeholder="请输入金额" style="width: 350px"></el-input>
+                <span style="line-height: 40px;margin-left:20px">{{ digitUppercase(formData.OriginalPrice) }}</span>
+              </el-form-item>
+            </div>
+            <div class="border-top">
+              <el-form-item label="付款方式说明" prop="PayRemark">
+                <el-input type="textarea" v-model="formData.PayRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+              </el-form-item>
+            </div>
+          </div>
+        </section>
+  
+        <section class="section" v-if="relationContractData">
+          <h2 class="section-title">合同信息</h2>
+          <div class="section-container">
+            <div class="flex border-top" style="padding-top: 30px">
+              <el-form-item label="合同编号" style="width: 50%">
+                <span>{{ relationContractData.ContractCode }}</span>
+              </el-form-item>
+              <el-form-item label="合同归属" style="width: 50%">
+                <span>{{ relationContractData.ProductId === 1 ? "ficc" : "权益" }}</span>
+              </el-form-item>
+            </div>
+            <el-form-item label="合同类型" class="border-top">
+              <el-radio-group v-model="relationContractData.ContractType">
+                <div style="display: flex">
+                  <div style="margin-right: 10px">
+                    <el-radio label="新签合同" disabled>新签合同</el-radio>
+                    <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                        <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                        <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                    </el-tooltip>
+                  </div>
+                  <div style="margin-right: 10px">
+                    <el-radio label="续约合同" disabled>续约合同</el-radio>
+                    <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                        <template #content>
+                        1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+                        2、所有客户在续约申请时提交的合同
+                        </template>
+                        <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                    </el-tooltip>
+                  </div>
+                  <div style="margin-right: 10px" v-if="RoleType !== 'rai_seller'">
+                    <el-radio label="补充协议" disabled>补充协议</el-radio>
+                  </div>
+                </div>
+              </el-radio-group>
+            </el-form-item>
+            <div class="flex border-top" v-if="relationContractData.timeRange">
+              <el-form-item label="合同期限" style="width: 50%">
+                <el-date-picker disabled v-model="relationContractData.timeRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
+              </el-form-item>
+              <span style="line-height: 40px">{{ formateYear(relationContractData.timeRange)  }}</span>
+            </div>
+  
+            <div class="flex border-top">
+              <el-form-item label="合同金额" style="width: 50%">
+                <el-input disabled :value="formatPrice(relationContractData.OriginalPrice)" placeholder="请输入合同金额" style="width: 220px"></el-input>
+              </el-form-item>
+              <span style="line-height: 40px">{{ digitUppercase(relationContractData.OriginalPrice) }}</span>
+            </div>
+  
+            <div class="flex border-top">
+              <el-form-item label="优惠后金额" style="width: 50%">
+                <el-input disabled :value="formatPrice(relationContractData.Price)" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+              </el-form-item>
+              <span style="line-height: 40px">{{ digitUppercase(relationContractData.Price)  }}</span>
+            </div>
+  
+            <el-form-item label="付款方式说明" class="border-top">
+              <el-input
+                type="textarea"
+                disabled
+                v-model="relationContractData.PayRemark"
+                placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)"
+                style="width: 80%"
+              ></el-input>
+            </el-form-item>
+            <el-form-item label="付款方" class="border-top">
+              <el-input
+                type="textarea"
+                disabled
+                v-model="relationContractData.PayChannel"
+                placeholder="代付,请填写代付的券商名称或期货公司名称;非代付,请填写无。(请勿填写现金/转账等支付形式)"
+                style="width: 80%"
+              ></el-input>
+            </el-form-item>
+          </div>
+        </section>
+  
+        <section class="section" v-if="relationContractData">
+          <h2 class="section-title">服务内容</h2>
+            <div class="section-container">
+              <!-- ficc 服务内容表格 -->
+              <FiccServiceTable
+                :canEdit="false"
+                :serviceData="ficcServiceData"
+                :hasSercive="relationContractData.Service"
+                @handleShowService="handleShowService"
+                v-if="relationContractData.ProductId === 1 && ficcServiceData"
+              ></FiccServiceTable>
+  
+              <!-- 权益 服务内容表格 -->
+              <QyServiceTable
+                :canEdit="false"
+                :serviceData="qyServiceData"
+                :hasSercive="relationContractData.Service"
+                @handleShowService="handleShowService"
+                v-if="relationContractData.ProductId === 2 && qyServiceData"
+              ></QyServiceTable>
+            </div>
+            <p style="margin-top: 30px; margin-bottom: 20px">补充内容(eg额外赠送、路演次数规定)</p>
+            <el-radio :model-value="relationContractData.Remark?'1':'2'" label="2" disabled>无</el-radio>
+            <el-radio :model-value="relationContractData.Remark?'1':'2'" label="1" disabled>有</el-radio>
+            <el-input v-if="relationContractData.Remark" disabled type="textarea" v-model="relationContractData.Remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+        </section>
+  
+        <section class="section">
+          <h2 class="section-title">审批备注</h2>
+          <div class="section-container">
+            <el-form-item label="备注" prop="sellerRemark" class="border-top">
+              <el-input type="textarea" v-model="formData.sellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+            </el-form-item>
+          </div>
+        </section>
+      </el-form>
+      <div class="right-wrap">
+        <div style="display: flex; justify-content: space-between">
+          <el-button type="primary" style="width: 45%" @click="handleGoEdit" v-if="showEdit" size="large">编辑</el-button>
+          <el-button type="primary" plain :style="{width: showEdit?'45%':'100%'}" @click="handleGoPreview" size="large">预览</el-button>
+        </div>
+        <div class="timeline-wrap">
+          <el-timeline>
+            <el-timeline-item color="#409EFF" v-for="item in operationList" :key="item.Id" placement="top" :timestamp="item.CreateTimeStr">
+              {{item.OpUserName}}{{ item.Remark }}
+            </el-timeline-item>
+          </el-timeline>
+        </div>
+      </div>
+      <!-- 查看报价单弹窗 -->
+      <el-dialog v-dialogDrag v-model="serviceShow" width="60%" class="self-dialog-c" :modal-append-to-body="false" @close="serviceShow=false" :show-close="false">
+        <h2 class="title" style="text-align: center; color: #000">{{ RoleType == 'ficc_seller'? serviceCon.Title : serviceCon.showTitle }}</h2>
+        <img class="img" style="width: 100%; display: block; margin: 20px auto" 
+        :src="RoleType == 'ficc_seller'? serviceCon.Value : serviceCon.showValue"/>
+        <div style="text-align: center; margin-bottom: 30px">
+          <el-button type="primary" @click="serviceShow=false" size="large">知道了</el-button>
+        </div>
+      </el-dialog>
+    </div>
+  </template>
+  
+  <style>
+  .contractdetail-dc .self-dialog-c .el-dialog__header {
+    background-color: #fff;
+    padding: 0;
+  }
+  .contractdetail-dc .self-dialog-c {
+    width: auto;
+    max-width: 60%;
+  }
+  .contractdetail-dc .el-timeline-item__tail {
+    border-left: 2px solid #409eff;
+  }
+  .el-radio__input.is-checked .el-radio__inner {
+    background-color: transparent;
+  }
+  .el-radio__inner::after {
+    width: 6px;
+    height: 6px;
+    background-color: #409eff;
+  }
+  .el-checkbox__label {
+    font-size: 16px !important;
+  }
+  .el-form-item {
+    margin-bottom: 30px;
+  }
+  .el-input.is-disabled .el-input__inner {
+    color: #606266;
+  }
+   .el-radio__input.is-disabled + span.el-radio__label {
+     color: #606266;
+   }
+   .el-range-editor.is-disabled input {
+     color: #606266;
+   }
+   .el-textarea.is-disabled .el-textarea__inner {
+     color: #606266;
+   }
+   .el-timeline-item__tail {
+     border-left: 2px solid #409eff;
+   }
+  </style>
+  <style lang="scss" scoped>
+  .contractdetail-dc{
+      min-height: calc(100vh - 250px);
+      position: relative;
+      font-size: 16px;
+      color: #000;
+  }
+  .flex {
+    display: flex;
+  }
+  .right-wrap {
+    margin-left: 20px;
+    flex: 1;
+    .timeline-wrap {
+      margin-top: 20px;
+      background-color: #fff;
+      border: 1px solid #aab4cc;
+      padding: 20px;
+      border-radius: 4px;
+      text-align: left;
+      height: calc(100% - 120px);
+      max-height: 1130px;
+      overflow-y: auto;
+    }
+  }
+  .section {
+    background-color: #fff;
+    border: 1px solid #aab4cc;
+    border-radius: 4px;
+    padding: 20px 30px;
+    margin-bottom: 20px;
+    .section-title {
+      margin-bottom: 30px;
+    }
+    .border-top {
+      padding-top: 30px;
+      border-top: 1px solid #dcdfe6;
+    }
+  }
+  </style>

+ 1094 - 0
src/views/contract_manage/contractList.vue

@@ -0,0 +1,1094 @@
+
+<script>
+import { defineComponent } from 'vue'
+export default defineComponent({
+  // 进入前是否清除参数
+  beforeRouteEnter(to, from, next) {
+    if(from.path!='/contractdetail'&&from.path!='/contractdetaildc'&&from.path!='/addcontractdc'){
+      sessionStorage.removeItem('contractListBack')
+      sessionStorage.removeItem('contractListTabs')
+    }
+    next()
+  }
+})
+
+</script>
+
+<script setup>
+import { ref,reactive,computed } from "vue";
+import { onBeforeRouteLeave ,useRouter} from 'vue-router';
+import {ElMessage,ElMessageBox} from "element-plus"
+import _ from 'lodash'
+import PreviewService from "./components/PreviewService.vue";
+import { contractInterface,customInterence } from "@/api/api.js";
+import ExplainDlg from './components/ExplainDlg.vue';
+import {formatter,formatPriceHasPoint} from "@/hooks/mixins/index"
+
+
+const $router = useRouter()
+
+// 离开前缓存页面状态
+onBeforeRouteLeave((to, from, next)=>{
+  let obj={
+    page:page.value,
+    searchVal:searchVal.value,
+    searchType:searchType.value,
+    timeRange:timeRange.value,
+    constractTypeVal:constractTypeVal.value,
+    constractStatusVal:constractStatusVal.value,
+    constractBelongVal:constractBelongVal.value,
+    constractSellerVal:constractSellerVal.value,
+  }
+  sessionStorage.setItem('contractListBack',JSON.stringify(obj))
+  sessionStorage.setItem('contractListTabs',tabsActive.value)
+  next()
+})
+
+const tabs=[{name:'业务合同'},{name:"代付合同"}]
+//合同类型选项
+const constractTypeOptions=[
+        {value: "新签合同",label: "新签合同"},
+        {value: "续约合同",label: "续约合同"},
+        {value: "补充协议",label: "补充协议"}] 
+//合同状态选项
+const constractStatusOptions=ref([
+        {value: "待提交",lable: "待提交"},
+        {value: "待审批",lable: "待审批"},
+        {value: "已撤回",lable: "已撤回"},
+        {value: "已审批",lable: "已审批"},
+        {value: "已驳回",lable: "已驳回"},
+        {value: "已作废",lable: "已作废"},
+        {value: "已签回",lable: "已签回"},
+        {value: "已解约",lable: "已解约"}
+      ])
+//合同归属选项
+const constractBelongOptions=[
+        {value: "1",label: "FICC"},
+        {value: "2",label: "权益"}] 
+//销售级联配置
+const defaultSalesProps={
+  multiple: true,
+  label:'RealName',
+  children:'ChildrenList',
+  value:'AdminId'
+}
+const tabsActive=ref(sessionStorage.getItem('contractListTabs')||'业务合同')
+const searchType=ref('使用方')
+const showPreContractList=ref(false)
+const preContractList=ref([])
+const showAddTypeDia=ref(false)//新增合同类型弹窗
+const RoleType=ref(localStorage.getItem("Role") || "")
+const contractTemList=ref([])//合同模板列表
+const serviceCon=ref([])//套餐数据
+const showPreview=ref(false)//查看套餐弹窗
+const showDownload=ref(false)//下载合同模板弹窗
+const page=ref(1)
+const pageSize=ref(10)
+const total=ref(0)
+const searchVal=ref("")//搜索值
+const timeRange=ref(["", ""])//选择时间段
+const constractTypeVal=ref("")//选择的合同类型值
+const constractStatusVal=ref("")//选择的合同状态值
+const constractBelongVal=ref("")//合同归属值
+const constractSellerOptions=ref([])//申请销售列表
+const constractSellerVal=ref("")//申请销售值
+const tableList=ref([])//合同列表数据(表格)
+const uploadFileData=reactive({
+  name:"",//文件名
+  CompanyName:"",//客户名
+  url:'',
+  contractId:0,
+  file:null,
+})//销售更新合同文件数据
+const showUploadAttachment=ref(false)//销售更新合同文件弹窗
+const explainShowDownload=ref(false)//操作详情
+const showlc=ref(false)
+
+
+const exportExcel=computed(()=>{
+  let baseUrl = import.meta.env.VITE_APP_API_ROOT + "/contract/list";
+  let token = localStorage.getItem("auth") || "";
+  let paramStr = "";
+  let obj = {
+    ContractType: constractTypeVal.value,
+    ContractStatus: constractStatusVal.value,
+    ProductId: constractBelongVal.value || 0,
+    ModifyStartTime: !timeRange.value?'':timeRange.value[0] ? `${timeRange.value[0]} 00:00:00` : "",
+    ModifyEndTime: !timeRange.value?'':timeRange.value[1] ? `${timeRange.value[1]} 23:59:59` : "",
+    SellerId: '',
+    Keyword: searchVal.value,
+    KeywordType:searchType.value,
+    IsExport:true,
+    ContractBusinessType:tabsActive.value,
+  };
+  if(constractSellerVal.value.length){
+    obj.SellerId=constractSellerVal.value.map(item=>{
+      return item[item.length-1]
+    }).join(',')
+  }
+  for (let key in obj) {
+    paramStr = `${paramStr}&${key}=${obj[key]}`;
+  }
+  return `${baseUrl}?${token}${paramStr}`;
+})
+
+// 业务合同、代付合同切换
+const handleChangeTabs=(e)=>{
+  tabsActive.value=e
+  handleRefreshPage()
+}
+
+// 业务合同查看代付合同、代付合同查看
+const handlePreviewContract=(e)=>{
+  console.log(preContractList.value,'preContractList.value');
+  // 只有一个直接跳转
+  if(e.length===1){
+    let contractId=0
+    if(tabsActive.value==='业务合同'){
+      contractId=e[0].PaymentOnBehalfContractId
+    }else if(tabsActive.value==='代付合同'){
+      contractId=e[0].ContractId
+    }
+    handleGoContractdtl(contractId)
+  }else if(e.length>1){
+    preContractList.value=e
+    showPreContractList.value=true
+  }
+}
+
+// 跳转合同预览
+const handleGoContractdtl=(id)=>{
+  let {href} = $router.resolve({path:"/contractdtl",query:{contractid:id}});
+  window.open(href,'_blank');
+}
+
+const checkFileBeforeUpload=(file)=>{
+  let hostfile = file;
+  let size = Math.floor(hostfile.size / 1024 / 1024);
+  if (size > 200) {
+    ElMessage.error("上传文件大小不能大于200M!");
+    hostfile = {};
+    return false;
+  }
+  if (hostfile.name.toLowerCase().includes(".pdf")||hostfile.name.toLowerCase().includes(".doc")||hostfile.name.toLowerCase().includes(".docx")) {
+      uploadFileData.name=hostfile.name.toLowerCase()
+      uploadFileData.file=hostfile
+    } else {
+      ElMessage.error("请上传PDF/word文件!");
+    }
+  return false
+}
+
+// 销售更新合同附件
+const handleUploadAttachment=()=>{
+  $("#fileCard").click();
+}
+
+// 获取列表数据
+const getContractList=()=>{
+  // 处理销售筛选
+  let salesArr=[]
+  if(constractSellerVal.value.length){
+    salesArr=constractSellerVal.value.map(item=>{
+      return item[item.length-1]
+    })
+  }
+  let params = {
+    ContractType: constractTypeVal.value,
+    ContractStatus: constractStatusVal.value,
+    ProductId: Number(constractBelongVal.value),
+    ModifyStartTime: !timeRange.value?'':timeRange.value[0] ? `${timeRange.value[0]} 00:00:00` : "",
+    ModifyEndTime: !timeRange.value?'':timeRange.value[1] ? `${timeRange.value[1]} 23:59:59` : "",
+    SellerId: salesArr.join(','),
+    Keyword: searchVal.value,
+    KeywordType:searchType.value,
+    CurrentIndex: page.value,
+    PageSize: pageSize.value,
+    ContractBusinessType:tabsActive.value,
+  };
+  contractInterface.getContractList(params).then((res) => {
+    if (res.Ret === 200) {
+      tableList.value = res.Data.List;
+      total.value = res.Data.Paging.Totals;
+    }
+  });
+}
+
+//获取所属销售列表
+const getSellerList=()=>{
+  contractInterface.getSellerList().then((res) => {
+    if (res.Ret === 200) {
+      constractSellerOptions.value = res.Data.List;
+    }
+  });
+}
+
+// 选择文件上传
+const fileSelected=()=>{
+  //选择文件上传
+  if (document.getElementById("fileCard").files[0]) {
+    let hostfile = document.getElementById("fileCard").files[0];
+    let size = Math.floor(hostfile.size / 1024 / 1024);
+    if (size > 200) {
+      ElMessage.error("上传文件大小不能大于200M!");
+      hostfile = {};
+      return false;
+    }
+    if (hostfile.name.toLowerCase().includes(".pdf")||hostfile.name.toLowerCase().includes(".doc")||hostfile.name.toLowerCase().includes(".docx")) {
+      // let form = new FormData();
+      // form.append("file", hostfile); //hostfile.name
+      // customInterence.upload(form).then((res) => {
+      //   if (res.Ret === 200) {
+      //     // that.$message.success( '上传成功' );
+      //     this.uploadFileData.url = res.Data.ResourceUrl;
+      //     this.uploadFileData.name=hostfile.name.toLowerCase()
+      //   }
+      //   $("#fileCard").val("");
+      //   hostfile = {};
+      // });
+      uploadFileData.name=hostfile.name.toLowerCase()
+      uploadFileData.file=hostfile
+    } else {
+      ElMessage.error("请上传PDF/word文件!");
+    }
+  }
+}
+
+// 确定更新合同附件
+const handleConfirmUpdateContractFile = async ()=>{
+  if(!uploadFileData.file){
+    ElMessage.warning('请上传文件');
+    return
+  }
+  let form = new FormData();
+  form.append("file", uploadFileData.file)
+  form.append('ContractId',uploadFileData.contractId)
+  // const res=await contractInterface.updateContractFile({
+  //   ContractId:this.uploadFileData.contractId,
+  //   FileUrl:this.uploadFileData.url
+  // })
+  const res=await contractInterface.updateContractFile(form)
+  if(res.Ret===200){
+    ElMessage.success('更新成功')
+    getContractList()
+    showUploadAttachment.value=false
+  }
+}
+
+//下载合同模板
+const handleDownloadContractTemplate=()=>{
+  contractInterface.getContractTemplate().then((res) => {
+    if (res.Ret === 200) {
+      contractTemList.value = res.Data;
+      showDownload.value = true;
+    }
+  });
+}
+
+//刷新页面
+const handleRefreshPage=()=>{
+  page.value = 1;
+  searchVal.value = "";
+  timeRange.value = ["", ""];
+  constractTypeVal.value = "";
+  constractStatusVal.value = "";
+  constractBelongVal.value=''
+  constractSellerVal.value=''
+  getContractList();
+}
+
+//提交申请
+const handleSubmitApproval=(id)=>{
+  contractInterface.applyContract({ ContractId: Number(id) }).then((res) => {
+    if (res.Ret === 200) {
+      ElMessage.success("提交成功");
+      handleRefreshPage();
+    }
+  });
+}
+
+//撤回申请
+const handleCancelApplyContract=(ContractApprovalRecordId, ContractId)=>{
+  contractInterface.cancelApplyContract({ ContractApprovalRecordId, ContractId }).then((res) => {
+    if (res.Ret === 200) {
+      ElMessage.success("撤回成功");
+      handleRefreshPage();
+    }
+  });
+}
+
+/**
+  * 操作
+  * type 操作类型
+  * e    数据
+  * 查看套餐(viewPackage)、驳回理由(rejectReason)、申请重审(applyRetrial)
+  * 删除(delete)、下载(download)、作废合同(cancelConstract)、撤回申请(backApply)
+  * 修改合同(modifyConstract)、复制模板(copy)、编辑(edit)、提交审批(submitApproval)
+  * 未提交审核的下载(downloadTem) 查看附件(已签回 checkBack  已解约rescind)  销售更新合同附件(updatefConstractFile)
+  * 修改重审(modifyAndApply)
+*/
+const handleOperation=_.debounce(function(type, e) {
+  if(type==='checkBack'){
+    const url=e.CheckBackFileUrl
+    if(!url){
+      ElMessage.warning('文件错误')
+      return
+    }
+    const reg = /\.(pdf)$/;
+    // pdf
+    if(reg.test(url)){
+      window.open(url,'_blank');
+    }else{
+      window.open('https://view.officeapps.live.com/op/view.aspx?src='+url,'_blank');
+    }
+    // window.open(e.CheckBackFileUrl,'_blank');
+  }
+
+  if(type==='rescind'){
+    const url=e.RescindFileUrl
+    if(!url){
+      ElMessage.warning('文件错误')
+      return
+    }
+    const reg = /\.(pdf)$/;
+    // pdf
+    if(reg.test(url)){
+      window.open(url,'_blank');
+    }else{
+      window.open('https://view.officeapps.live.com/op/view.aspx?src='+url,'_blank');
+    }
+    // window.open(e.RescindFileUrl,'_blank');
+  }
+
+  if (type === "submitApproval") {
+    handleSubmitApproval(e.ContractId);
+    return;
+  }
+
+  if (type === "delete") {
+    ElMessageBox.confirm("是否确认删除合同?", "提示", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning",
+    }).then(() => {
+      contractInterface.contractDelete({ ContractId: e.ContractId }).then((res) => {
+        if (res.Ret === 200) {
+          ElMessage.success("删除成功");
+          handleRefreshPage();
+        }
+      });
+    }).catch(()=>{})
+    return;
+  }
+
+  if (type === "edit") {
+    if(e.ContractBusinessType==='业务合同'){
+      $router.push({
+        path: "/editcontract",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }else{
+      $router.push({
+        path: "/editcontractdc",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }
+    
+    return;
+  }
+
+  if (type === "viewPackage") {
+    if(e.ContractBusinessType==='业务合同'){
+      serviceCon.value = e.Service;
+    }else{
+      let arr=[]
+      e.RelationContractList.forEach(item=>{
+        item.Service.forEach(item2=>{
+          arr.push(item2)
+        })
+      })
+      serviceCon.value=arr
+    }
+    
+    showPreview.value = true;
+    return;
+  }
+
+  if (type === "rejectReason") {
+    ElMessageBox.alert(e.ApprovalRemark, "驳回理由", {
+      confirmButtonText: "知道了",
+      showCancelButton: false,
+    }).then(() => {});
+    return;
+  }
+
+  if (type === "cancelConstract") {
+    ElMessageBox.confirm("作废后合同将不能再使用,是否确认作废合同?", "提示", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning",
+    }).then(() => {
+      contractInterface.contractInvalid({ ContractId: e.ContractId }).then((res) => {
+        if (res.Ret === 200) {
+          ElMessage.success("作废成功");
+          handleRefreshPage();
+        }
+      });
+    }).catch(()=>{})
+    return;
+  }
+
+  if (type === "backApply") {
+    ElMessageBox.confirm("确认撤回该合同吗?", "提示", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning",
+    }).then(() => {
+      handleCancelApplyContract(e.ContractApprovalRecordId, e.ContractId);
+    }).catch(()=>{})
+    return;
+  }
+
+  if (type === "download") {
+    let link = document.createElement("a");
+    link.href = e.FileUrl + "?response-content-type=application/octet-stream";
+    link.setAttribute("download", e.ContractCode);
+    link.style.display = "none"; //a标签隐藏
+    document.body.appendChild(link);
+    link.click();
+    return;
+  }
+
+  if(type==='downloadTem'){
+    let baseUrl = import.meta.env.VITE_APP_API_ROOT + "/contract/downLoad/tmpContract";
+    let token = localStorage.getItem("auth") || "";
+    let path=`${baseUrl}?${token}&ContractId=${e.ContractId}`
+    let link = document.createElement("a");
+    link.href = path;
+    link.setAttribute("download", e.ContractCode);
+    link.style.display = "none"; //a标签隐藏
+    document.body.appendChild(link);
+    link.click();
+    return
+  }
+
+  if (type === "modifyConstract") {
+    if(e.ContractBusinessType==='业务合同'){
+      $router.push({
+        path: "/editcontract",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }else{
+      $router.push({
+        path: "/editcontractdc",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }
+    
+    return;
+  }
+
+  if (type === "applyRetrial") {
+    if(e.ContractBusinessType==='业务合同'){
+      $router.push({
+        path: "/retrialcontract",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }else{
+      $router.push({
+        path: "/retrialcontractdc",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }
+    
+    return;
+  }
+
+  if (type === "copy") {
+    if(e.ContractBusinessType==='业务合同'){
+      $router.push({
+        path: "/copycontract",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }else{
+      $router.push({
+        path: "/copycontract",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+      $router.push({
+        path: "/copycontractdc",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }
+    
+    return;
+  } 
+
+  // 销售更新合同附件
+  if(type==='updatefConstractFile'){
+    let fileName=e.FileUrl.substring(e.FileUrl.lastIndexOf('/') + 1)
+    uploadFileData.name=fileName
+    uploadFileData.CompanyName=e.CompanyName
+    uploadFileData.contractId=e.ContractId
+    uploadFileData.url=e.FileUrl
+    showUploadAttachment.value=true
+  }
+
+  // 修改重审
+  if(type==='modifyAndApply'){
+    if(e.ContractBusinessType==='业务合同'){
+      $router.push({
+        path: "/modifyapply",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }else{
+      $router.push({
+        path: "/modifyapplydc",
+        query: {
+          contractId: e.ContractId,
+          type: type,
+        },
+      });
+    }
+
+    return
+  }
+},200)
+
+
+//关闭查看套餐弹窗
+const handleClosePreviewService=()=>{
+  showPreview.value = false;
+}
+
+//类型或者状态变更
+const handleSelectChange=()=>{
+  searchVal.value = "";
+  page.value = 1;
+  getContractList();
+}
+
+
+//页码改变
+const handlePageChange=(e)=>{
+  page.value = e;
+  getContractList();
+}
+
+//跳转合同详情
+const handleGoDetail=(type,id)=>{
+  if(type==='业务合同'){
+    $router.push({ path: "/contractdetail", query: { contractId: id } });
+  }else{
+    $router.push({ path: "/contractdetaildc", query: { contractId: id } });
+  }
+}
+
+//搜索
+const handleSearch=()=>{
+  timeRange.value = ["", ""];
+  constractTypeVal.value = "";
+  constractStatusVal.value = "";
+  constractSellerVal.value=''
+  constractBelongVal.value=''
+  page.value = 1;
+  getContractList();
+}
+//查看详细操作说明
+const explainDialog=()=>{
+  explainShowDownload.value=true;
+}
+
+// created
+if(sessionStorage.getItem('contractListBack')){
+  let cache=JSON.parse(sessionStorage.getItem('contractListBack'))
+  page.value=cache.page
+  searchVal.value=cache.searchVal
+  timeRange.value=cache.timeRange
+  constractBelongVal.value=cache.constractBelongVal
+  constractTypeVal.value=cache.constractTypeVal
+  constractStatusVal.value=cache.constractStatusVal
+  constractSellerVal.value=cache.constractSellerVal
+}
+if(RoleType.value === 'admin'){
+  constractStatusOptions.value= [
+    {
+      value: "待审批",
+      lable: "待审批",
+    },
+    {
+      value: "已审批",
+      lable: "已审批",
+    },
+    {
+      value: "已驳回",
+      lable: "已驳回",
+    },
+    {
+      value: "已作废",
+      lable: "已作废",
+    },
+    {
+      value: "已签回",
+      lable: "已签回",
+    },
+    {
+      value: "已解约",
+      lable: "已解约",
+    },
+  ] //合同状态选项
+}
+getContractList();
+getSellerList();
+</script>
+
+<template>
+  <div class="contractlist-container">
+    <div class="top-wrap">
+      <template v-if="RoleType === 'ficc_seller' || RoleType === 'rai_seller'"   style="margin-right:10px;">
+        <el-button type="primary" @click="showAddTypeDia=true" size="large">添加合同</el-button>
+        <el-button type="primary" plain @click="handleDownloadContractTemplate" size="large">合同操作指南</el-button>
+      </template>
+      <template v-if="!(RoleType === 'ficc_seller' || RoleType === 'rai_seller')" >
+        <a :href="exportExcel" download>
+          <el-button type="primary" size="large">下载EXCEL</el-button>
+        </a>
+      </template>
+
+      <el-input placeholder="合同编号/客户名称/社会信用码" v-model="searchVal" size="large"
+      style="max-width: 400px; float: right" @input="handleSearch" clearable>
+        <i slot="prefix" class="el-input__icon el-icon-search"></i>
+      </el-input>
+      <el-select v-model="searchType" placeholder="请选择" style="width:120px;float: right;margin-right:10px" size="large">
+        <el-option label="客户名称" value="使用方"></el-option>
+        <el-option label="代付方" value="代付方"></el-option>
+      </el-select>
+
+    </div>
+    <div class="list-wrap">
+      <div style="margin-top: 10px; margin-bottom: 10px">
+        <el-date-picker style="margin-bottom: 20px;margin-right:20px" v-model="timeRange" type="daterange" size="large"
+        @change="handleSelectChange" value-format="yyyy-MM-dd" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
+        </el-date-picker>
+        <el-select v-if="tabsActive==='业务合同'" clearable v-model="constractTypeVal" @change="handleSelectChange" size="large"
+        placeholder="合同类型" style="margin-bottom: 20px;margin-right:20px">
+          <el-option v-for="item in constractTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+        </el-select>
+        <el-select clearable v-model="constractStatusVal" @change="handleSelectChange" placeholder="合同状态" size="large"
+        style="margin-bottom: 20px;margin-right:20px">
+          <el-option v-for="item in constractStatusOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+        </el-select>
+        <template v-if="RoleType === 'admin'||RoleType==='compliance'||RoleType==='finance'">
+          <el-select clearable v-model="constractBelongVal" @change="handleSelectChange" placeholder="合同归属" 
+          style="margin-bottom: 20px;margin-right:20px" size="large">
+            <el-option v-for="item in constractBelongOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+          </el-select>
+          <!-- <el-select clearable v-model="constractSellerVal" @change="handleSelectChange" placeholder="申请销售" style="margin-bottom: 20px;margin-right:20px">
+            <el-option v-for="item in constractSellerOptions" :key="item.AdminId" :label="item.RealName" :value="item.AdminId"> </el-option>
+          </el-select> -->
+          <el-cascader
+            v-model="constractSellerVal"
+            placeholder="申请销售"
+            style="min-width:225px;margin-bottom: 20px;margin-right:20px"
+            :options="constractSellerOptions"
+            :props="defaultSalesProps"
+            :show-all-levels="false"
+            collapse-tags
+            clearable
+            filterable
+            size="large"
+            @change="handleSelectChange">
+          </el-cascader>
+        </template>
+      </div>
+      <ul class="tabs-wrap">
+        <li 
+          v-for="item in tabs" 
+          :class="['tab',{'act':tabsActive === item.name}]"
+          @click="handleChangeTabs(item.name)"
+        >{{item.name}}</li>
+      </ul>
+      <el-table :data="tableList" border ref="table">
+        <el-table-column key="合同编号" align="center" prop="ContractCode" label="合同编号" width="160">
+          <template #default="scope">
+            <span class="constract-sn" @click="handleGoDetail(scope.row.ContractBusinessType,scope.row.ContractId)">{{ scope.row.ContractCode }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column key="客户名称" align="center" prop="CompanyName" :label="tabsActive==='业务合同'?'客户名称':'代付方'"></el-table-column>
+        <el-table-column key="社会信用码" align="center" prop="CreditCode" label="社会信用码" width="180"></el-table-column>
+        <el-table-column key="代付方" align="center" prop="RelationContractList" :label="tabsActive==='业务合同'?'付款方':'客户名称'">
+          <template #default="scope">
+            <div v-if="!scope.row.RelationContractList">{{scope.row.PayChannel=='无'?scope.row.CompanyName:scope.row.PayChannel}}</div>
+            <div v-else style="color:#409eff;cursor: pointer" @click="handlePreviewContract(scope.row.RelationContractList)">
+              <span v-if="scope.row.RelationContractList.length===1">{{scope.row.RelationContractList[0].CompanyName}}</span>
+              <span v-else>查看</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column key="销售" align="center" prop="SellerName" label="销售" width="80"></el-table-column>
+        <el-table-column key="合同类型" align="center" prop="ContractType" label="合同类型" width="80"></el-table-column>
+        <el-table-column key="合同有效期" align="center" label="合同有效期" width="180">
+          <template #default="scope">{{ scope.row.StartDateStr }}~{{ scope.row.EndDateStr }}</template>
+        </el-table-column>
+        <el-table-column key="合同金额" align="center" prop="Price" :label="tabsActive==='业务合同'?'合同金额':'代付金额'" width="100">
+          <template #default="scope">{{ formatPriceHasPoint(scope.row.Price) }}</template>
+        </el-table-column>
+        <el-table-column key="合同状态" align="center" prop="Status" label="合同状态" width="80">
+          <template #default="scope">
+            <span :style="{ color: scope.row.Status === '已驳回'||scope.row.Status === '已解约' ? '#FF0000' : null }">{{ scope.row.Status }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column key="更新时间" align="center" prop="ModifyTimeStr" label="更新时间" width="160" :formatter="formatter"></el-table-column>
+        <el-table-column key="seller操作" align="center" label="操作" width="200" v-if="RoleType === 'ficc_seller' || RoleType === 'rai_seller'">
+          <template #default="scope">
+            <div v-if="scope.row.Status === '待提交'">
+              <span class="btn" @click="handleOperation('submitApproval', scope.row)">提交审批</span>
+              <span class="btn" @click="handleOperation('edit', scope.row)">编辑</span>
+              <!-- <span class="btn" @click="handleOperation('downloadTem', scope.row)">草稿下载</span> -->
+              <span class="btn" @click="handleOperation('delete', scope.row)" style="color: #ff0000">删除</span>
+            </div>
+            <div v-if="scope.row.Status === '待审批'">
+              <span class="btn" @click="handleOperation('viewPackage', scope.row)">查看套餐</span>
+              <span class="btn" @click="handleOperation('backApply', scope.row)">撤回申请</span>
+            </div>
+            <div v-if="scope.row.Status === '已撤回'">
+              <span class="btn" @click="handleOperation('viewPackage', scope.row)">查看套餐</span>
+              <span class="btn" @click="handleOperation('modifyConstract', scope.row)">修改合同</span>
+              <span class="btn" @click="handleOperation('delete', scope.row)" style="color: #ff0000">删除</span>
+            </div>
+            <div v-if="scope.row.Status === '已审批'">
+              <span class="btn" @click="handleOperation('viewPackage', scope.row)">查看套餐</span>
+              <span class="btn" @click="handleOperation('cancelConstract', scope.row)">作废合同</span>
+              <span class="btn" @click="handleOperation('download', scope.row)">下载</span>
+              <span class="btn" @click="handleOperation('modifyAndApply', scope.row)">修改重审</span>
+              <!-- <span class="btn" @click="handleOperation('updatefConstractFile',scope.row)" v-if="scope.row.OpButton.UpdateFile">更新附件</span> -->
+            </div>
+            <div v-if="scope.row.Status === '已驳回'">
+              <span class="btn" @click="handleOperation('viewPackage', scope.row)">查看套餐</span>
+              <span class="btn" @click="handleOperation('rejectReason', scope.row)">驳回理由</span>
+              <span class="btn" @click="handleOperation('applyRetrial', scope.row)">申请重审</span>
+              <span class="btn" @click="handleOperation('delete', scope.row)" style="color: #ff0000">删除</span>
+            </div>
+            <div v-if="scope.row.Status === '已作废'">
+              <span class="btn" @click="handleOperation('copy', scope.row)">复制模板</span>
+              <span class="btn" @click="handleOperation('delete', scope.row)" style="color: #ff0000">删除</span>
+            </div>
+            <div v-if="scope.row.Status === '已签回'">
+              <span class="btn" @click="handleOperation('checkBack', scope.row)">查看附件</span>
+            </div>
+            <div v-if="scope.row.Status === '已解约'">
+              <span class="btn" @click="handleOperation('rescind', scope.row)">查看附件</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column key="admin操作" align="center" label="操作" width="150" v-if="RoleType === 'admin'">
+          <template #default="scope">
+            <span class="btn" @click="handleOperation('viewPackage', scope.row)">查看套餐</span>
+            <span class="btn" @click="handleOperation('download', scope.row)" v-if="scope.row.Status === '已审批'">下载</span>
+            <span class="btn" @click="handleOperation('rejectReason', scope.row)" v-if="scope.row.Status === '已驳回'">驳回理由</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <el-pagination layout="total,prev,pager,next,jumper" background :current-page="page" size="large"
+      @current-change="handlePageChange" :page-size="pageSize" :total="total" class="pagination-wrap">
+      </el-pagination>
+    </div>
+
+    <!-- 下载提示弹框 -->
+    <el-dialog 
+      v-dialogDrag 
+      :append-to-body="true" 
+      v-model="showDownload" 
+      width="50%" 
+      center
+    >
+    <template #header>
+      <img src="../../assets/img/icons/warn.png" width="16" height="16" style="vertical-align: middle;margin-right: 4px" />
+      <span style="vertical-align: middle">合同操作指南:</span>
+    </template>
+      <div class="download-popup-wrap">
+        <!-- <img class="top-img" src="../../assets/img/constract/liucheng.png" alt="" /> -->
+        <div class="top-img"></div>
+        <!-- <a :href="item.Url+'?response-content-type=application/octet-stream'" :download="item.Title" v-for="item in contractTemList" :key="item.ContractTemplateId">{{ item.Title }}</a> -->
+        <a @click="explainDialog">查看详细操作说明</a>
+        <a @click="showlc=true" style="display:block;margin-top:10px;position:relative">查看详细操作流程图</a>
+      </div>
+    </el-dialog>
+
+    <!-- 查看套餐弹框 -->
+    <PreviewService :showPreview="showPreview" :serviceCon="serviceCon" @closePreviewService="handleClosePreviewService"></PreviewService>
+  
+    <!-- 查看合同列表弹框 -->
+    <el-dialog 
+      v-dialogDrag 
+      :append-to-body="true" 
+      v-model="showPreContractList" 
+      width="40%" 
+      center
+    >
+      <template #header>
+        <div style="font-size:15px">{{tabsActive==='业务合同'?'付款方列表':'代付方列表'}}</div>
+      </template>
+      <ul class="precontract-list">
+        <li 
+          v-for="item in preContractList" 
+          :key="item.ContractId+item.PaymentOnBehalfContractId"
+          @click="handleGoContractdtl(tabsActive==='业务合同'?item.PaymentOnBehalfContractId:item.ContractId)"
+        >{{item.CompanyName}}</li>
+      </ul>
+      <div style="text-align:center;margin:40px 0 30px 0">
+        <el-button type="primary" style="width:200px" @click="showPreContractList=false">关闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 选择新增合同类型 -->
+    <el-dialog 
+      v-dialogDrag
+      :append-to-body="true" 
+      v-model="showAddTypeDia" 
+      width="800px" 
+      class="self-dialog"
+      center
+    >
+      <div style="text-align:center;margin:30px 0 100px 0">
+        <img width="191" src="../../assets/img/constract/bzht.png" @click="$router.push('/addcontract')" style="margin-right:80px;cursor: pointer;">
+				<img width="191" src="../../assets/img/constract/dfht.png" @click="$router.push('/addcontractdc')" style="cursor: pointer;">
+        <p style="font-size:15px;color:#333;text-align:left;padding-left:100px;margin-top:30px">
+					注:标准代付指的是无法签署三方协议和代付合同时,可以选择和代付方签署该类型合同
+				</p>
+      </div>
+    </el-dialog>
+
+    <!-- 销售更新合同文件 -->
+    <el-dialog top="30vh" v-dialogDrag width="570px" v-model="showUploadAttachment" :modal-append-to-body="false" @close="showUploadAttachment=false">
+      <div slot="title">
+        <img width='15' src="../../assets/img/icons/edit1.png" alt="">
+        <span style="display:inline-block;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:450px">更新合同附件({{uploadFileData.CompanyName}})</span>
+      </div>
+      <div class="upload-attachment-wrap">
+        <div style="display:flex;align-items: center;">
+          <input type="file" name="file" @change="fileSelected" id="fileCard" style="display: none" />
+          <div class="box"><span v-if="!uploadFileData.name" style="color:#999">请选择文件</span>{{uploadFileData.name}}</div>
+          <!-- <el-button type="primary" @click="handleUploadAttachment">选择文件</el-button> -->
+          <el-upload :before-upload="checkFileBeforeUpload">
+            <el-button type="primary">选择文件</el-button>
+          </el-upload>
+        </div>
+        <div style="text-align:center;margin:60px 0 30px 0">
+          <el-button type="primary" @click="handleConfirmUpdateContractFile">确定</el-button>
+          <el-button type="primary" plain @click="showUploadAttachment=false">取消</el-button>
+        </div>
+      </div>
+    </el-dialog>
+    <explain-dlg  :explainShowDownload="explainShowDownload" @close="explainShowDownload=false"/>
+    <el-dialog 
+      v-dialogDrag 
+      :append-to-body="true" 
+      v-model="showlc" 
+      width="90vw" 
+      top="5vh"
+      class="self-dialog"
+      center>
+        <img style="width:100%" src="../../assets/img/constract/bg.png" />
+    </el-dialog>
+  </div>
+</template>
+
+
+<style lang="scss">
+.el-message-box__header .el-message-box__title {
+  color: #333 !important;
+}
+.el-date-table td.start-date div::after {
+  content: "起";
+  display: block;
+  position: absolute;
+  top: 64%;
+  left: 50%;
+  transform: translateX(-50%) scale(0.9);
+  color: #666;
+  z-index: 1000;
+  font-size: 10px;
+}
+.el-date-table td.end-date div::after {
+  content: "终";
+  display: block;
+  position: absolute;
+  top: 64%;
+  left: 50%;
+  transform: translateX(-50%) scale(0.9);
+  color: #666;
+  z-index: 1000;
+  font-size: 10px;
+}
+.el-table__empty-block{
+  min-height: 300px;
+}
+
+.container-explain .el-dialog__header {
+    background-color: #fff !important;
+}
+
+.self-dialog{
+		// width: 100% !important;
+		max-width: 100% !important;
+		.el-dialog__header{
+			background-color: #fff;
+			.el-dialog__close.el-icon.el-icon-close{
+				color: #666;
+			}
+		}
+	}
+</style>
+
+<style lang="scss" scoped>
+.precontract-list{
+  border: 1px solid #DCDFE6;
+  li{
+    font-size: 14px;
+    color: #409eff;
+    cursor: pointer;
+    padding: 14px 20px;
+    border-bottom: 1px solid #DCDFE6;
+  }
+  li:last-child{
+    border: none;
+  }
+}
+
+.upload-attachment-wrap{
+  .box{
+    width: 380px;
+    display: inline-block;
+    box-sizing: border-box;
+    height: 40px;
+    line-height: 40px;
+    padding: 0 17px;
+    font-size: 14px;
+    border: 1px solid #DCDFE6;
+    border-radius: 4px;
+    margin-right: 20px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+.top-wrap {
+  margin-bottom: 28px;
+  padding: 20px 30px;
+  // min-height: 50px;
+  background: #fff;
+  border: 1px solid #ececec;
+  border-radius: 4px;
+  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+}
+
+.tabs-wrap{
+  display: flex;
+  margin-bottom: 20px;
+  align-items: center;
+  .tab {
+    padding-bottom: 5px;
+		text-align: center;
+		font-size: 14px;
+		cursor: pointer;
+		transition-delay: 0.05s;
+    margin-right: 40px;
+    color: #333;
+		&:hover {
+			color: #409EFF;
+		}
+		&.act {
+      color: #409eff;
+			border-bottom: 2px solid #409EFF;
+		}
+	}
+}
+
+.list-wrap {
+  min-height: calc(100vh - 390px);
+  padding: 20px 30px 80px;
+  background: #fff;
+  margin-top: 20px;
+  position: relative;
+  border: 1px solid #ececec;
+  border-radius: 4px;
+  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+  .constract-sn {
+    cursor: pointer;
+    color: #409eff;
+  }
+  .btn {
+    font-size: 14px;
+    display: inline-block;
+    color: #409eff;
+    cursor: pointer;
+    margin: 0 3px;
+  }
+  .pagination-wrap {
+    position: absolute;
+    bottom: 24px;
+    right: 30px;
+  }
+}
+
+.download-popup-wrap {
+  text-align: center;
+  padding-bottom: 80px;
+  .top-img {
+    // width: 80%;
+    // display: block;
+    // margin: 20px auto;
+    width: 100%;
+    padding-bottom: 53%;
+    background-image: url('../../assets/img/constract/liucheng.png');
+    background-size: cover;
+    background-position: center;
+  }
+  a {
+    color: #409eff;
+    cursor: pointer;
+    // &::before {
+    //   content: "";
+    //   display: inline-block;
+    //   width: 15px;
+    //   height: 15px;
+    //   background-image: url("../../assets/img/constract/download.png");
+    //   background-size: cover;
+    //   margin-right: 10px;
+    // }
+  }
+}
+
+</style>

+ 1132 - 0
src/views/contract_manage/editContract.vue

@@ -0,0 +1,1132 @@
+<script setup>
+import { ref,reactive,computed,watch } from "vue";
+import {ElMessage,ElMessageBox} from "element-plus"
+import { useRouter,useRoute} from 'vue-router';
+import _ from 'lodash'
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import { contractInterface,customInterence } from "@/api/api.js";
+import{province_sorce,city_sorce} from '@/utils/distpicker';
+import ServiceDialog from "./components/ServiceDialog.vue"; //套餐内容弹窗组件
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import {CalculationDate} from '@/utils/CalculationDate'
+
+const $route = useRoute()
+const $router = useRouter()
+
+const province_sorce_value=province_sorce
+const city_sorce_value=city_sorce
+
+const contractTypeDisable1=ref(false) //能否选择新签合同
+const contractTypeDisable2=ref(false) //能否选择续约合同
+const contractTypeDisable3=ref(false) //能否选择补充协议
+const pickerOptions=ref(null)
+const timeDisable=ref(false)
+const cusLastcontract=ref(null)
+const RoleType=ref(localStorage.getItem("Role") || "")
+const radio=ref('1')//是否有补充内容 1有 2 无
+
+const currentRouteName=ref($route.meta.title)
+const contractId=ref($route.query.contractId)
+const operationList=ref([])
+const formData=ref({})
+const formRule={
+  ContractType: [{ required: true, message: "请选择合同类型", trigger: "change" }],
+  timeRange: [{ required: true, message: "请选择有效期限", trigger: "change" }],
+  OriginalPrice: [
+    { required: true, message: "请填写合同金额", trigger: "blur" },
+    // { pattern: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/, message: "请输入正确的格式,可保留两位小数" },
+  ],
+  PayRemark: [{ required: true, message: "请填写付款方式说明", trigger: "blur" }],
+  CompanyName: [{ required: true, message: "请填写名称", trigger: "blur" }],
+  CreditCode: [{ required: true, message: "请填写信用码", trigger: "blur" }],
+  Province: [{ required: true, message: "请选择地址", trigger: "change" }],
+  Remark: [{ required: true, message: "请填写补充说明", trigger: "blur" }],
+  PayChannel:[{ required: true, message: "请填写付款方", trigger: "change" }]
+}
+
+const ficcServiceData=ref([])//ficc服务内容
+const qyServiceData=ref([])//权益服务内容
+const serviceShow=ref(false)
+const serviceCon=ref({})//选择的服务项 查看报价单/选择品种
+const selectServiceData=ref([])
+const editValue=ref({})//修改小套餐的value
+const PayChannelOptions=ref([])//搜索出付款方列表
+
+watch(()=>formData.value.ContractType,(nval)=>{
+  let obj={disabledDate() {return false}}
+  if(cusLastcontract.value){
+    if(nval==='续约合同'&&RoleType.value==='ficc_seller'){
+      // ficc 起始日期为权限中最大的时间
+      formData.value.timeRange=[]
+      let timearr=cusLastcontract.value.map(item=> item.EndDate)
+      let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+      let endDate=formatDate(new Date(maxTime))
+      console.log(endDate);
+      obj={
+        disabledDate(time) {
+          return time.getTime() < new Date(endDate).getTime();
+        }
+      }
+    }else{
+      obj={
+        disabledDate() {
+            return false
+          }
+      }
+    }
+    if(nval==='补充协议'){
+      let today=formatDate(new Date())
+      // 找出权限中最晚截止的那个作为截至日期
+      let timearr=cusLastcontract.value.map(item=> item.EndDate)
+      let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+      let endDate=formatDate(new Date(maxTime))
+      formData.value.timeRange=[today,endDate]
+      obj={disabledDate() {return true}}
+    }
+  }
+
+  if(nval==='补充协议'){
+    timeDisable.value=true
+  }else{
+    timeDisable.value=false
+  }
+  pickerOptions.value=obj
+},
+{immediate:true})
+
+const formItemRemark=ref(null)
+watch(radio,(nval, oval)=>{
+  formItemRemark.value.resetField();
+  if (nval === "1") {
+    formRule.Remark[0].required = true;
+  } else {
+    formRule.Remark[0].required = false;
+  }
+})
+
+// computed: {
+//   pickerOptions(){
+//     console.log('aaa');
+//     let obj={disabledDate() {return false}}
+//     if(this.cusLastcontract){
+//       if(this.formData.ContractType==='续约合同'&&this.RoleType==='ficc_seller'){
+//         // ficc 起始日期为权限中最大的时间
+//         this.formData.timeRange=[]
+//         let timearr=this.cusLastcontract.map(item=> item.EndDate)
+//         let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+//         let endDate=this.formatDate(new Date(maxTime))
+//         obj={
+//           disabledDate(time) {
+//             return time.getTime() < new Date(endDate).getTime();
+//           }
+//         }
+//       }else{
+//         obj={
+//           disabledDate() { 
+//               return false
+//             }
+//         }
+//       }
+//       if(this.formData.ContractType==='补充协议'){
+//         let today=this.formatDate(new Date())
+//         // 找出权限中最晚截止的那个作为截至日期
+//         let timearr=this.cusLastcontract.map(item=> item.EndDate)
+//         let maxTime=Math.max.apply(null, timearr.map(item => (new Date(item)).getTime()));
+//         let endDate=this.formatDate(new Date(maxTime))
+//         this.formData.timeRange=[today,endDate]
+//         this.timeDisable=true
+//         obj={disabledDate() {return true}}
+//       }else{
+//         this.timeDisable=false
+//       }
+//     }
+    
+//     return obj
+//   },
+// },
+
+// filters
+// 金额转中文大写
+const digitUppercase=(n)=>{
+  if (n) {
+    n=n.toString().replace(/,/g,'')
+    let fraction = ["角", "分"];
+    let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+    let unit = [
+      ["元", "万", "亿"],
+      ["", "拾", "佰", "仟"],
+    ];
+    let head = n < 0 ? "欠" : "";
+    n = Math.abs(n);
+    let s = "";
+    for (let i = 0; i < fraction.length; i++) {
+      s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+    }
+    // s = s || "整";
+
+    n = Math.floor(n);
+    for (let i = 0; i < unit[0].length && n > 0; i++) {
+      let p = "";
+      for (let j = 0; j < unit[1].length && n > 0; j++) {
+        p = digit[n % 10] + unit[1][j] + p;
+        n = Math.floor(n / 10);
+      }
+      s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+    }
+    return (
+      "大写:" + (head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零"))
+      // .replace(/^整$/, "零元整")
+    );
+  }
+}
+
+//计算多少年
+const formateYear=(e)=>{
+  if (!e) return;
+  if (e[0]) {
+    return `有效期为${CalculationDate(e[0],e[1])}`
+  } else {
+    return "";
+  }
+}
+
+// 社会信用码变更
+const getContractByCode=async()=>{
+  contractTypeDisable1.value=false
+  contractTypeDisable2.value=false
+  contractTypeDisable3.value=false
+  if(!formData.value.CreditCode) return
+  const res=await contractInterface.getContractTypeByCode({CreditCode:formData.value.CreditCode})
+  if(res.Ret==200){
+    let customeId=res.Data.Item&&res.Data.Item.CompanyId
+    if(customeId){
+      const res2=await customInterence.applyTurnContractType({CompanyId:customeId}) 
+      
+      if(res2.Ret===200){
+        if(res2.Data.ContractType==='新签合同'){
+          formData.value.ContractType=res2.Data.ContractType
+          contractTypeDisable1.value=false
+          contractTypeDisable2.value=true
+          contractTypeDisable3.value=true
+        }else{
+          if(RoleType.value==='ficc_seller'){
+            contractTypeDisable1.value=true
+            contractTypeDisable2.value=false
+            contractTypeDisable3.value=false
+          }
+          if(RoleType.value==='rai_seller'){
+            formData.value.ContractType=res2.Data.ContractType
+            contractTypeDisable1.value=true
+            contractTypeDisable2.value=false
+            contractTypeDisable3.value=true
+          }
+        }
+      }
+    }
+  }
+}
+
+// 解除搜索选中的客户绑定(需求修改不需要此操作2021-9-16)
+const handleUnbind=()=>{
+  ElMessageBox.confirm('此操作将解除与选择的客户绑定, 是否继续?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    contractTypeDisable1.value=false
+    contractTypeDisable2.value=false
+    contractTypeDisable3.value=false
+    // this.pickerOptions={}
+    cusLastcontract.value=null
+    timeDisable.value=false
+  }).catch(()=>{
+    console.log('取消解绑');
+  })
+}
+
+// 客户名称搜索框输入变化
+const handleCustomeInputChange=(e)=>{
+  // 将付款方初始化为客户名称
+  // const flag=this.formData.PayChannel.includes(this.formData.CompanyName)
+  // if(!flag){
+  //   this.formData.PayChannel.push(this.formData.CompanyName)
+  // }
+}
+
+// 搜索付款渠道
+// async querySearchAsyncPayChannel(queryString, cb){
+//   cb([])
+//   // if(!queryString) return
+//   let res=await contractInterface.getPayChannel({Keyword:queryString})
+//   if(res.Ret===200){
+//     if(res.Data&&res.Data.length>0){
+//       let arr=res.Data.map(item=>{
+//         return {value:item.PayChannel,...item}
+//       })
+//       cb(arr)
+//     }
+//   }
+// },
+
+// 付款渠道方式修改 多选
+const querySearchAsyncPayChannel=async (query,type)=>{
+  if(!type&&!query) return
+  let res=await contractInterface.getPayChannel({Keyword:query})
+  if(res.Ret===200){
+    PayChannelOptions.value=res.Data||[]
+  }
+}
+
+// 格式化时间
+const formatDate=(date)=>{
+  let year=date.getFullYear()
+  let month=date.getMonth()+1
+  let day=date.getDate()
+  return `${year}-${month<10?'0'+month:month}-${day<10?'0'+day:day}`
+}
+
+// 初始化客户搜索动作
+const initCustomeSelect=async(queryString)=>{
+  let CompanyType=localStorage.getItem('RoleType')
+  let res=await customInterence.customList({KeyWord:queryString,PageSize:10000,CompanyType:CompanyType,IncludeShare:true})
+  if(res.Ret===200){
+    if(res.Data.List&&res.Data.List.length>0){
+      handleSelectCustome(res.Data.List[0])
+    }
+  }
+}
+
+// 客户搜索
+const querySearchAsyncCustome=async(queryString, cb)=>{
+  cb([])
+  if(!queryString) return
+  let CompanyType=localStorage.getItem('RoleType')
+  let res=await customInterence.customList({KeyWord:queryString,PageSize:10000,CompanyType:CompanyType,IncludeShare:true})
+  // this.contractTypeDisable1=false
+  // this.contractTypeDisable2=false
+  // this.contractTypeDisable3=false
+  // // this.pickerOptions={}
+  // this.cusLastcontract=null
+  // this.timeDisable=false
+  if(res.Ret===200){
+    if(res.Data.List.length>0){
+      let arr=res.Data.List.map(item=>{
+        return {value:item.CompanyName,...item}
+      })
+      cb(arr)
+    }
+  }
+}
+
+//选择客户
+const handleSelectCustome=async(item)=>{
+  // 获取客户详情 更新表单数据
+  let res=await customInterence.customDetail({CompanyId:item.CompanyId})
+  if(res.Ret===200){
+    formData.value.CreditCode=res.Data.Item.CreditCode
+    formData.value.Province=res.Data.Item.Province
+    formData.value.City=res.Data.Item.City
+  }
+  // 更新合同类型
+  let res2=await customInterence.applyTurnContractType({CompanyId:item.CompanyId})
+  if(res2.Ret===200){
+    
+    if(res2.Data.ContractType==='新签合同'){
+      formData.value.ContractType=res2.Data.ContractType
+      contractTypeDisable1.value=false
+      contractTypeDisable2.value=true
+      contractTypeDisable3.value=true
+    }else{
+      if(RoleType.value==='ficc_seller'){
+        contractTypeDisable1.value=true
+        contractTypeDisable2.value=false
+        contractTypeDisable3.value=false
+      }
+      if(RoleType.value==='rai_seller'){
+        formData.value.ContractType=res2.Data.ContractType
+        contractTypeDisable1.value=true
+        contractTypeDisable2.value=false
+        contractTypeDisable3.value=true
+      }
+    }
+  }
+
+  // 获取客户大于今天的最后一份有效合同详情
+  let res3=await customInterence.lastContractInfo({CompanyId:item.CompanyId})
+  if(res3.Ret===200){
+    if(res3.Data&&res3.Data.length>0){
+      cusLastcontract.value=res3.Data
+    }else{
+      cusLastcontract.value=null
+    }
+  }
+}
+
+//判断合同期限 不小于一个月
+const validateTime=()=>{
+  if(formData.value.ContractType==='补充协议') return true
+  let start = new Date(formData.value.timeRange[0]).getTime();
+  let end = new Date(formData.value.timeRange[1]).getTime();
+  let oneMonth = 1000 * 60 * 60 * 24 * 29;
+  if (end - start >= oneMonth) {
+    return true;
+  } else {
+    ElMessage.warning("合同期限不能小于一个月");
+    return false;
+  }
+}
+
+//判断优惠后金额不能大于合同金额
+const validatePrice=()=>{
+  if (Number(formData.value.Price) > Number(formData.value.OriginalPrice)) {
+    ElMessage.warning("优惠后金额不得大于合同金额");
+    return false;
+  } else {
+    return true;
+  }
+}
+
+// 判断小套餐中是否选择品种/新增的行是否有数据
+const validateService=(data)=>{
+  //判断品种列是否有值
+  let arr = [];
+  data.forEach((item) => {
+    item.forEach((item2) => {
+      if (item2.HeadName === "品种") {
+        arr.push(...item2.ValueId);
+      }
+    });
+  });
+  // 判断行是否有值
+  let arr2 = [];
+  data.forEach((item, index) => {
+    let arr = item.filter((item2) => {
+      if (!item2.Value) return item2;
+    });
+    if (arr.length === item.length) {
+      arr2.push(index);
+    }
+  });
+
+  if (!arr.length&&formData.value.ContractType!=='补充协议') {
+    ElMessage.warning("至少选择一个品种");
+    return false;
+  }
+
+  if (arr2.length) {
+    ElMessage.warning("行内至少填一项");
+    return false;
+  }
+
+  return true;
+}
+
+//获取合同操作记录
+const getContractOperationList=()=>{
+  contractInterface.getContractOperationList({ ContractId: contractId.value }).then((res) => {
+    if (res.Ret === 200) {
+      operationList.value = res.Data.List;
+    }
+  });
+}
+
+//选地地区时 选则的省份改变重置city为空
+const provinceChange=(e)=>{
+  if(e.value!=formData.value.Province){
+    // this.formData.City=''
+    formData.value.Province=e.value
+  }
+}
+
+//选择地区
+const selectRegion=(e)=>{
+  formData.value.Province = e.province.value=='省'?'':e.province.value;
+  formData.value.City = e.city.value=='市'?'':e.city.value;
+}
+
+// 判断套餐权限
+const handleValidate=(e)=>{
+  let tag=true
+  let tagsmall=false
+  // ficc 补充协议 不允许有选择过的
+  if(RoleType.value === "ficc_seller"&&e.ContractType==='补充协议'){
+    // 是否选择大套餐 大套餐为全部品种 不能选
+    e.Service.forEach(item=>{
+      // 大套餐
+      if(item.ChartPermissionId===0&&item.ServiceTemplateId===1){
+        tag=false
+        ElMessage.warning('FICC大套餐不可选')
+      }
+      // 小套餐 判断其中ficc周报是否有重复选择的权限
+      if(item.ChartPermissionId===0&&item.ServiceTemplateId===2){
+        let temIdarr=[]
+        item.Detail.forEach(item2=>{
+          item2.forEach(item3=>{
+            if(item3.Value==='FICC周报'){
+              item2.forEach(item4=>{
+                if(item4.HeadName==='品种'){
+                  temIdarr=item4.ValueId
+                }
+              })
+            }
+          })
+        })
+
+        cusLastcontract.value.forEach(item4=>{
+          if(temIdarr.indexOf(item4.ChartPermissionId)!=-1){
+            tag=false
+            tagsmall=true
+            // this.$message.warning('小套餐中权限重复')
+          }
+        })
+      }
+
+      // 市场策略
+      if(item.ChartPermissionId!==0){
+        cusLastcontract.value.forEach(item2=>{
+          if(item2.ChartPermissionId===item.ChartPermissionId){
+            ElMessage.warning(`${item.Title}不可选`)
+            tag=false
+          }
+        })
+      }
+    })
+  }
+
+  // 权益 续约合同 
+  if(RoleType.value === "rai_seller"&&e.ContractType==='续约合同'){
+    const time1=new Date(formData.value.timeRange[0]).getTime()//选择的开始时间
+    const time2=new Date(formData.value.timeRange[1]).getTime()//选择的结束时间
+    cusLastcontract.value.forEach(item=>{
+      const time3=new Date(item.StartDate).getTime()
+      const time4=new Date(item.EndDate).getTime()
+      if(time1>time4||time2<time3){
+        console.log('选择时间和上一份无重叠');
+      }else{
+        console.log('时间重叠');
+        e.Service.forEach(item2=>{
+          console.log(item2.ChartPermissionId,item.ChartPermissionId);
+          if(item2.ChartPermissionId===item.ChartPermissionId){
+            ElMessage.warning('同行业有重叠的合同期限,请核实后再提交')
+            tag=false
+          }
+        })
+
+      }
+    })
+    
+  }
+
+  // 小套餐统一一个提示
+  if(tagsmall){
+    ElMessage.warning('小套餐中权限重复')
+  }
+  return tag
+}
+
+const formRef=ref(null)
+const FiccServiceTableRef=ref(null)
+const QyServiceTableRef=ref(null)
+const ServiceDialogRef=ref(null)
+//提交表单 存草稿、预览、提交
+const handleOperation=_.debounce(function(submitType) {
+  formRef.value.validate((valid) => {
+    if (valid) {
+      let temarr = [];
+      if (RoleType.value === "ficc_seller") {
+        FiccServiceTableRef.value.tableData.forEach((item) => {
+          let obj = {
+            ServiceTemplateId: "",
+            Value: "",
+            Detail: [],
+            Title: item.Title,
+            ChartPermissionId:item.ChartPermissionId
+          };
+          obj.ServiceTemplateId = item.ServiceTemplateId;
+          obj.Value = item.Value;
+          if (item.selected) {
+            //小套餐情况
+            if (item.Detail) {
+              if (ServiceDialogRef.value.tableHeadData.length === 0) {
+                let arr = [];
+                formData.value.Service.forEach((item2) => {
+                  if (item2.ServiceTemplateId === item.ServiceTemplateId) {
+                    arr = item2.DetailList.map((rowItem) => {
+                      let rowArr = [];
+                      for (let key in rowItem) {
+                        if (key.substr(0, 3) === "Col" && rowItem[key] !== "") {
+                          rowArr.push(JSON.parse(rowItem[key]));
+                        }
+                      }
+                      return rowArr;
+                    });
+                  }
+                });
+                obj.Detail = arr;
+              } else {
+                obj.Detail = [ServiceDialogRef.value.tableHeadData, ...ServiceDialogRef.value.tableData];
+              }
+            }
+            temarr.push(obj);
+          }
+        });
+      } else {
+        // 只要一个分类里面 主观和客观其中一个被勾选 这个分类就要传
+        QyServiceTableRef.value.selectAllArr.map(item =>{
+          if(QyServiceTableRef.value.checkList.find(it => it.indexOf(item.name)!=-1 && it.indexOf("升级")==-1)){
+            temarr.push({
+              ServiceTemplateId: item.ServiceTemplateId,
+              Value: item.value,
+              Detail: null,
+              Title: item.name,
+              ChartPermissionId:item.ChartPermissionId
+            })
+          }
+        })
+        QyServiceTableRef.value.tableData.list.forEach((item) => {
+          let obj = {
+            ServiceTemplateId: "",
+            Value: "",
+            Detail: null,
+            Title: item.Title,
+            Title: item.Title == item.fatherName ? item.Title:`${item.fatherName}(${item.Title})`,
+            ChartPermissionId:item.ChartPermissionId
+          };
+          obj.ServiceTemplateId = item.ServiceTemplateId;
+          obj.Value = item.Value;
+          let flag = ''
+          if(item.Title == item.fatherName){
+            //没有主客观
+              flag= QyServiceTableRef.value.checkList.indexOf(item.Title);
+          }else{
+            flag= QyServiceTableRef.value.checkList.indexOf(`${item.fatherName}(${item.Title})`);
+          }
+          if (flag !== -1) {
+            temarr.push(obj);
+          }
+        });
+      }
+
+      // 判断地址
+      if(formData.value.Province!='海外'&&!formData.value.City){
+        ElMessage.warning('请选择地址')
+        return
+      }
+
+      // 判断合同期限 不小于一个月
+      if (!validateTime()) {
+        return;
+      }
+      //判断金额
+      if (!validatePrice()) {
+        return;
+      }
+      //判断是否选择了套餐
+      if(!temarr.length){
+        ElMessage.warning('请选择套餐')
+        return
+      }
+
+      //判断小套餐
+      let flag = true;
+      temarr.forEach((item) => {
+        if (item.Detail&&item.Detail.length) {
+          flag = validateService(item.Detail.slice(1));
+        }
+      });
+      if (!flag) return;
+
+      let params = {
+        ContractType: formData.value.ContractType,
+        ContractBusinessType:"业务合同",
+        StartDate: formData.value.timeRange[0],
+        EndDate: formData.value.timeRange[1],
+        OriginalPrice: Number(formData.value.OriginalPrice.toString().replace(/,/g,'')),
+        Price: Number(formData.value.Price.toString().replace(/,/g,'')),
+        PayRemark: formData.value.PayRemark,
+        CompanyName: formData.value.CompanyName,
+        CreditCode: formData.value.CreditCode,
+        Province: formData.value.Province,
+        City: formData.value.City,
+        Address: formData.value.Address,
+        Fax: formData.value.Fax,
+        Phone: formData.value.Phone,
+        Postcode: formData.value.Postcode,
+        Remark: formData.value.Remark,
+        PayChannel:formData.value.PayChannel.join(','),
+        SellerRemark:formData.value.SellerRemark,
+        TemplateId: RoleType.value === "ficc_seller" ? 1 : 2,
+        Service: temarr,
+      };
+
+      console.log(params);
+      // 判断套餐权限
+      if(cusLastcontract.value&&!handleValidate(params)) return
+
+      // 存草稿
+      if(submitType==='存草稿'){
+        handleSave(params)
+      }
+
+      //预览
+      if(submitType==='预览'){
+        handlePreview(params)
+      }
+
+      //提交
+      if(submitType==='提交'){
+        handleSubmit(params)
+      }
+    }else{
+      ElMessage.warning('请完善必填项')
+    }
+  });
+},200)
+
+//存草稿
+const handleSave=(params)=>{
+  
+  let type = $route.query.type;
+  // 复制模板(copy) 
+  if (type === "copy") {
+    params = {
+      SourceId: formData.value.ContractId,
+      ...params,
+      IsAudit: false,
+    };
+    contractInterface.addContract(params).then((res) => {
+      if (res.Ret === 200) {
+        $router.go(-1);
+      }
+    });
+  }
+  // 申请重审(applyRetrial) 修改合同(modifyConstract) 编辑(edit)修改重审(modifyAndApply)
+  if (type === "applyRetrial" || type === "modifyConstract" || type === "edit"||type==='modifyAndApply') {
+    params = {
+      ContractId: formData.value.ContractId,
+      ...params,
+      ReAudit:false
+    };
+    contractInterface.editContract(params).then((res) => {
+      if (res.Ret === 200) {
+        $router.go(-1);
+      }
+    });
+  }
+    
+}
+
+//预览
+const handlePreview=(params)=>{
+  contractInterface.previewContract(params).then((res) => {
+    if (res.Ret === 200) {
+      sessionStorage.setItem("contractdtl", res.Data.Html);
+      let { href } = $router.resolve({ path: "/contractdtl" });
+      window.open(href, "_blank");
+    }
+  }); 
+}
+
+//提交
+const handleSubmit=(params)=>{
+  let type = $route.query.type;
+  // 复制模板(copy) 
+  if (type === "copy") {
+    params = {
+      SourceId: formData.value.ContractId,
+      ...params,
+      IsAudit: true,
+    };
+    contractInterface.addContract(params).then((res) => {
+      if (res.Ret === 200) {
+        $router.go(-1);
+      }
+    });
+  }
+  // 申请重审(applyRetrial) 修改合同(modifyConstract) 编辑(edit)修改重审(modifyAndApply)
+  if (type === "applyRetrial" || type === "modifyConstract" || type === "edit"||type==='modifyAndApply') {
+    params = {
+      ContractId: formData.value.ContractId,
+      ...params,
+    };
+    contractInterface.editContract(params).then((res) => {
+      if (res.Ret === 200) {
+        $router.go(-1);
+      }
+    });
+  }
+}
+
+
+//获取合同详情
+const getContractDetail=()=>{
+  contractInterface.getContractDetail({ ContractId: Number(contractId.value) }).then((res) => {
+    if (res.Ret === 200) {
+      formData.value = { ...res.Data, timeRange: [res.Data.StartDateStr, res.Data.EndDateStr] };
+      formData.value.PayChannel=res.Data.PayChannel.split(',')||['无']
+      if (res.Data.Remark) {
+        radio.value = "1";
+      } else {
+        radio.value = "2";
+      }
+      selectServiceData.value = res.Data.Service;
+      getServiceList();
+      initCustomeSelect(res.Data.CreditCode)
+    }
+  });
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+  let ProductId = RoleType.value === "ficc_seller" ? 1 : 2;
+  contractInterface.getServiceList({ ProductId }).then((res) => {
+    if (res.Ret === 200) {
+      if (RoleType.value === "ficc_seller") {
+        ficcServiceData.value = res.Data;
+      } else {
+        qyServiceData.value = res.Data;
+      }
+    }
+  });
+}
+
+//小套餐点击保存 更新数据
+const serviceSave=(e)=>{
+  editValue.value = { ServiceTemplateId: e.ServiceTemplateId, Value: e.Value, tableData: e.tableData, tableHeadData: e.tableHeadData };
+  // this.ficcServiceData.forEach((item) => {
+  //   if (item.ServiceTemplateId === e.ServiceTemplateId) {
+  //     item.Value = e.Value;
+  //   }
+  // });
+  // this.selectServiceData.forEach((item) => {
+  //   if (item.ServiceTemplateId === e.ServiceTemplateId) {
+  //     item.Value = e.Value;
+  //   }
+  // });
+}
+
+//显示查看报价单弹窗
+const handleShowService=(e)=>{
+  serviceCon.value = e;
+  serviceShow.value = true;
+}
+
+//关闭查看报价单弹窗
+const serviceClose=()=>{
+  serviceShow.value = false;
+}
+
+// 金额输入框获取焦点
+const handlePriceBoxFocus=(key)=>{
+  let val=formData.value[key].toString()
+  val=val.replace(/,/g,'')
+  formData.value[key]=val
+}
+// 金额输入框获失去焦点
+const handlePriceBoxBlur=(key)=>{
+  let str=formData.value[key].toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  formData.value[key]=num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+getContractDetail();
+getContractOperationList();
+
+</script>
+
+<template>
+  <div class="flex addconstract-container">
+    <el-form ref="formRef" :model="formData" label-position="left" :rules="formRule" label-width="110px" style="width: 80%"
+    size="large">
+      <section class="section">
+        <h2 class="section-title">客户信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="甲方名称" prop="CompanyName" style="width: 50%">
+              <!-- <el-input v-model="formData.CompanyName" placeholder="请输入名称" style="width: 350px"></el-input> -->
+              <el-autocomplete
+                v-model="formData.CompanyName"
+                :fetch-suggestions="querySearchAsyncCustome"
+                placeholder="请输入或者搜索客户名称"
+                @select="handleSelectCustome"
+                @blur="handleCustomeInputChange"
+                style="width: 50%"
+              ></el-autocomplete>
+            </el-form-item>
+            <el-form-item label="社会信用码" prop="CreditCode" style="width: 50%">
+              <el-input v-model="formData.CreditCode" placeholder="请输入社会信用码" style="width: 350px" :disabled="cusLastcontract" @input="getContractByCode"></el-input>
+              <!-- <span style="font-size:14px;color:#4099ef;cursor: pointer;" v-if="cusLastcontract" @click="handleUnbind">解除绑定</span> -->
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="公司地址" prop="Province" style="width: 50%">
+              <v-distpicker :province-source="province_sorce_value"
+                :city-source="city_sorce_value"  :province="formData.Province" :city="formData.City" hide-area @province="provinceChange" @selected="selectRegion"></v-distpicker>
+            </el-form-item>
+            <el-form-item label="详细地址" prop="address" style="width: 50%">
+              <el-input v-model="formData.Address" placeholder="请输入详细地址" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="传真" prop="customeCz" style="width: 50%">
+              <el-input v-model="formData.Fax" placeholder="请输入传真" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="电话" prop="customeTel" style="width: 50%">
+              <el-input v-model="formData.Phone" placeholder="请输入电话" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <el-form-item label="邮编" prop="postCode" class="border-top">
+            <el-input v-model="formData.Postcode" placeholder="请输入邮编" style="width: 350px"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">合同信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="合同编号" style="width: 50%">
+              <span>{{ formData.ContractCode }}</span>
+            </el-form-item>
+            <el-form-item label="合同归属" style="width: 50%">
+              <span>{{ formData.ProductId === 1 ? "FICC" : "权益" }}</span>
+            </el-form-item>
+          </div>
+          <el-form-item label="合同类型" prop="ContractType" class="border-top">
+            <el-radio-group v-model="formData.ContractType">
+              <div style="display:flex">
+                <div style="margin-right:10px;">
+                  <el-radio label="新签合同" :disabled="contractTypeDisable1">新签合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px">
+                  <el-radio label="续约合同" :disabled="contractTypeDisable2">续约合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>
+                      1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+										  2、所有客户在续约申请时提交的合同
+                    </template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right:10px" v-if="RoleType!=='rai_seller'">
+                  <el-radio label="补充协议" :disabled="contractTypeDisable3">补充协议</el-radio>
+                </div>
+              </div>
+            </el-radio-group>
+          </el-form-item>
+          <div class="flex border-top">
+            <el-form-item label="合同期限" prop="timeRange" style="width: 50%">
+              <el-date-picker 
+                style="max-width: 350px;"
+                v-model="formData.timeRange" 
+                type="daterange" 
+                value-format="yyyy-MM-dd" 
+                range-separator="至" 
+                start-placeholder="开始日期" 
+                end-placeholder="结束日期" 
+                :disabled="timeDisable"
+                :picker-options="pickerOptions"> 
+              </el-date-picker>
+            </el-form-item>
+            <span style="line-height: 40px">{{ formateYear(formData.timeRange) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="合同金额" prop="OriginalPrice" style="width: 50%">
+              <el-input v-model="formData.OriginalPrice"  @focus="handlePriceBoxFocus('OriginalPrice')" @blur="handlePriceBoxBlur('OriginalPrice')" placeholder="请输入合同金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(formData.OriginalPrice) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item label="优惠后金额" prop="Price" style="width: 50%">
+              <el-input v-model="formData.Price" @focus="handlePriceBoxFocus('Price')" @blur="handlePriceBoxBlur('Price')" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(formData.Price) }}</span>
+          </div>
+
+          <el-form-item label="付款方式说明" prop="PayRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.PayRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+            <p>说明模板:甲方自合同生效日起的十日之内一次性支付一年服务费,乙方收款后为甲方开具合法有效的增值税发票。</p>
+          </el-form-item>
+          <el-form-item label="付款方" prop="PayChannel" class="border-top">
+            <!-- <el-input type="textarea" v-model="formData.PayChannel" placeholder="填写代支付的券商名称/期货公司名称,若非代付填写无" style="width: 80%"></el-input> -->
+            <!-- <el-autocomplete
+              v-model="formData.PayChannel"
+              :fetch-suggestions="querySearchAsyncPayChannel"
+              placeholder="请输入"
+              style="width: 80%"
+            ></el-autocomplete> -->
+            <el-select
+                v-model="formData.PayChannel"
+                multiple
+                filterable
+                remote
+                allow-create
+                placeholder="请输入关键词"
+                :remote-method="querySearchAsyncPayChannel"
+                style="width: 80%">
+                <el-option
+                  v-for="item in PayChannelOptions"
+                  :key="item.PayChannel"
+                  :label="item.PayChannel"
+                  :value="item.PayChannel">
+                </el-option>
+              </el-select>
+            <p>如果为代付,则需要填写代付方名称(期货公司或者证券公司),请勿填写现金/转账</p>
+          </el-form-item>
+          <!-- <el-form-item label="备注" prop="SellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.SellerRemark" placeholder="审批人查看,不在生成合同中展示" style="width: 80%"></el-input>
+          </el-form-item> -->
+        </div>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">服务内容</h2>
+        <div class="section-container">
+          <!-- ficc 服务内容表格 -->
+          <FiccServiceTable
+            ref="FiccServiceTableRef"
+            :canEdit="true"
+            :serviceData="ficcServiceData"
+            :hasSercive="selectServiceData"
+            :editValue="editValue"
+            :contractType="formData.ContractType"
+            :isEdit="true"
+            @handleShowService="handleShowService"
+            v-if="RoleType === 'ficc_seller'"
+          ></FiccServiceTable>
+          <!-- 权益 服务内容表格 -->
+          <QyServiceTable ref="QyServiceTableRef" :canEdit="true" :serviceData="qyServiceData" :hasSercive="selectServiceData" @handleShowService="handleShowService" v-else></QyServiceTable>
+        </div>
+
+        <p style="margin-top: 30px; margin-bottom: 20px">补充内容(eg额外赠送、路演次数规定)</p>
+        <el-radio v-model="radio" label="2">无</el-radio>
+        <el-radio v-model="radio" label="1">有</el-radio>
+        <el-form-item prop="Remark" label-width="0" ref="formItemRemark">
+          <el-input v-if="radio === '1'" type="textarea" v-model="formData.Remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+        </el-form-item>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">审批备注</h2>
+        <div class="section-container">
+          <el-form-item label="备注" prop="SellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.SellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+    </el-form>
+    <div class="right-wrap">
+      <div style="text-align: center" v-if="currentRouteName === '编辑合同' || currentRouteName === '复制模板'||currentRouteName === '重审合同'||currentRouteName === '修改重审'">
+        <el-button type="primary" plain style="width: 30%;min-width:70px;margin-bottom:10px" @click="handleOperation('存草稿')" size="large">存草稿</el-button>
+        <el-button type="primary" style="width: 30%;margin-left:3%;min-width:70px;margin-bottom:10px" @click="handleOperation('预览')" size="large">预览</el-button>
+        <el-button type="primary" style="width: 30%;margin-left:3%;min-width:70px;margin-bottom:10px" @click="handleOperation('提交')" size="large">提交</el-button>
+      </div>
+      <!-- <div style="text-align: center" v-if="currentRouteName === '重审合同'">
+        <el-button type="primary" plain style="width: 30%;min-width:70px;margin-bottom:10px" @click="handleSave">存草稿</el-button>
+        <el-button type="primary" style="width: 30%;min-width:70px" @click="handlePreview">预览</el-button>
+        <el-button type="primary" style="width: 30%;min-width:70px" @click="handleSubmit">提交</el-button>
+      </div> -->
+      <div class="timeline-wrap">
+        <el-timeline>
+          <el-timeline-item color="#409EFF" v-for="item in operationList" :key="item.Id" placement="top" :timestamp="item.CreateTimeStr"> {{ item.OpUserName }}{{ item.Remark }} </el-timeline-item>
+        </el-timeline>
+      </div>
+    </div>
+
+    <!-- 套餐 -->
+    <ServiceDialog ref="ServiceDialogRef" :serviceShow="serviceShow" @serviceClose="serviceClose" @serviceSave="serviceSave" :serviceCon="serviceCon"></ServiceDialog>
+  </div>
+</template>
+
+<style>
+.el-radio__input.is-checked .el-radio__inner {
+  background-color: transparent;
+}
+.el-radio__inner::after {
+  width: 6px;
+  height: 6px;
+  background-color: #409eff;
+}
+.el-checkbox__label {
+  font-size: 16px !important;
+}
+.el-form-item {
+  margin-bottom: 30px;
+}
+.el-input.is-disabled .el-input__inner {
+  color: #606266;
+}
+.el-radio__input.is-disabled + span.el-radio__label {
+  color: #606266;
+}
+.el-range-editor.is-disabled input {
+  color: #606266;
+}
+.el-textarea.is-disabled .el-textarea__inner {
+  color: #606266;
+}
+.el-timeline-item__tail {
+  border-left: 2px solid #409eff;
+}
+</style>
+
+<style lang="scss" scoped>
+.addconstract-container {
+  min-height: calc(100vh - 250px);
+  position: relative;
+  font-size: 16px;
+  color: #000;
+}
+.flex {
+  display: flex;
+}
+.section {
+  background-color: #fff;
+  border: 1px solid #aab4cc;
+  border-radius: 4px;
+  padding: 20px;
+  margin-bottom: 20px;
+  .section-title {
+    margin-bottom: 30px;
+  }
+  .border-top {
+    padding-top: 30px;
+    border-top: 1px solid #dcdfe6;
+  }
+}
+
+.right-wrap {
+  margin-left: 20px;
+  flex: 1;
+  .timeline-wrap {
+    margin-top: 10px;
+    background-color: #fff;
+    border: 1px solid #aab4cc;
+    padding: 20px;
+    border-radius: 4px;
+    text-align: left;
+    height: calc(100% - 120px);
+    max-height: 1130px;
+    overflow-y: auto;
+  }
+}
+</style>

+ 609 - 0
src/views/contract_manage/editContractDC.vue

@@ -0,0 +1,609 @@
+<script setup>
+import { ref,nextTick } from "vue";
+import {ElMessage} from "element-plus"
+import { useRouter,useRoute} from 'vue-router';
+import _ from 'lodash'
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import { contractInterface } from "@/api/api.js";
+import{province_sorce,city_sorce} from '@/utils/distpicker';
+import QyServiceTable from "./components/QyServiceTable.vue"; //权益服务内容表格组件
+import FiccServiceTable from "./components/FiccServiceTable.vue"; //ficc服务内容表格组件
+import { CalculationDate } from "@/utils/CalculationDate";
+import {formatTime} from '@/hooks/mixins/index.js'
+
+const $router = useRouter()
+const $route = useRoute()
+
+const province_sorce_value=province_sorce
+const city_sorce_value=city_sorce
+
+const RoleType=ref(localStorage.getItem("Role") || "")
+const formData=ref({})
+const formRule={
+  CompanyName: [{ required: true, message: "请填写名称", trigger: "blur" }],
+  CreditCode: [{ required: true, message: "请填写信用码", trigger: "blur" }],
+  Province: [{ required: true, message: "请选择地址", trigger: "change" }],
+  OriginalPrice: [
+    { required: true, message: "请填写代付金额", trigger: "blur" },
+    // { pattern: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/, message: "请输入正确的格式,可保留两位小数" },
+  ],
+  trueUseName: [{ required: true, message: "请输入关键字", trigger: "change" }],
+  PayRemark: [{ required: true, message: "请填写付款方式说明", trigger: "blur" }],
+}
+
+const searchList=ref([])
+const contractList=ref([])
+const showContractList=ref(false)
+const relationContractData=ref(null) //选择的关联合同数据
+const ficcServiceData=ref(null) //ficc 底部表格数据
+const qyServiceData=ref(null) //权益底部表格数据
+const serviceShow=ref(false) //是否显示查看报价单
+const serviceCon=ref({}) //选择的服务项 查看报价单/选择品种
+const operationList=ref([])
+const showEdit=ref(false)//是否显示编辑按钮
+
+const formatPrice=(str)=>{
+  if(!str) return ''
+  str=str.toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  return num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+// 金额转中文大写
+const digitUppercase=(n)=>{
+  if(!n) return ''
+  n=n.toString().replace(/,/g,'')
+  let fraction = ["角", "分"];
+  let digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
+  let unit = [
+    ["元", "万", "亿"],
+    ["", "拾", "佰", "仟"],
+  ];
+  let head = n < 0 ? "欠" : "";
+  n = Math.abs(n);
+  let s = "";
+  for (let i = 0; i < fraction.length; i++) {
+    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, "");
+  }
+  // s = s || "整";
+
+  n = Math.floor(n);
+  for (let i = 0; i < unit[0].length && n > 0; i++) {
+    let p = "";
+    for (let j = 0; j < unit[1].length && n > 0; j++) {
+      p = digit[n % 10] + unit[1][j] + p;
+      n = Math.floor(n / 10);
+    }
+    s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
+  }
+  return (
+    "大写:" + (head + s.replace(/(零.)*零元/, "元").replace(/(零.)+/g, "零"))
+    // .replace(/^整$/, "零元整")
+  );
+}
+//计算多少年
+const formateYear=(e)=>{
+  if (e[0]) {
+    return `有效期为${CalculationDate(e[0], e[1])}`;
+  } else {
+    return "";
+  }
+}
+
+// 搜索出客户
+const customeSearch=async(query)=>{
+  if (!query) return;
+  const res = await contractInterface.getBusinessContractCompanyNameList({ Keyword: query });
+  if (res.Ret === 200) {
+    searchList.value = res.Data;
+  }
+}
+
+// 选择客户并搜索出其下已签回合同
+const handleSelectTrueUseName=(e)=>{
+  nextTick(async()=>{
+    if(isBlurManual.value){
+      isBlurManual.value=false
+      return 
+    }
+    if (e) return;
+    const res = await contractInterface.getBusinessContractList({ CompanyName: formData.value.trueUseName });
+    if (res.Ret === 200) {
+      showContractList.value = true;
+      nextTick(() => {
+        contractList.value = res.Data.List;
+      });
+    }
+  })
+}
+
+// 确认关联
+const handleRelation=async()=>{
+  if (!formData.value.RelationContractId) {
+    ElMessage.warning("请选择一份合同进行关联");
+    return;
+  }
+  const res = await contractInterface.getContractDetail({ ContractId: formData.value.RelationContractId });
+  if (res.Ret === 200) {
+    relationContractData.value = res.Data;
+    getServiceList();
+    showContractList.value = false;
+  }
+}
+const trueUseNameRef=ref(null)
+const isBlurManual=ref(false)
+const closeTrueUseNameDia=()=>{
+  // element-plus 会在弹窗关闭后,返回焦点给el-select组件……
+  console.log('失去焦点');
+  isBlurManual.value=true
+  trueUseNameRef.value.blur()
+}
+
+// 获取合同详情
+const getDetail=async()=>{
+  const res=await contractInterface.getContractDetail({
+    ContractId:$route.query.contractId
+  })
+  if(res.Ret===200){
+    formData.value={
+      ...res.Data,
+      trueUseName:res.Data.RelationContractDetailList[0].CompanyName,
+      RelationContractId:res.Data.RelationContractDetailList[0].ContractId
+    }
+    relationContractData.value=res.Data.RelationContractDetailList[0]
+    getServiceList()
+    
+  }
+}
+
+//获取合同操作记录
+const getContractOperationList=()=>{
+  contractInterface.getContractOperationList({ContractId:$route.query.contractId}).then(res=>{
+    if(res.Ret===200){
+      operationList.value=res.Data.List&&res.Data.List.map(item=>{
+        return {...item,CreateTimeStr:item.CreateTimeStr.replace(/-/g,'.')}
+      })||[]
+    }
+  })
+}
+
+//获取服务套餐模板数据
+const getServiceList=()=>{
+  contractInterface.getServiceList({ ProductId: relationContractData.value.ProductId }).then((res) => {
+    if (res.Ret === 200) {
+      if (relationContractData.value.ProductId === 1) {
+        ficcServiceData.value = res.Data;
+      } else {
+        qyServiceData.value = res.Data;
+      }
+    }
+  });
+}
+
+//显示查看报价单弹窗
+const handleShowService=(e)=>{
+  // 小套餐情况处理表格数据
+  if (e.Detail) {
+    let temarr = e.Detail.map((rowItem) => {
+      let rowArr = [];
+      for (let key in rowItem) {
+        if (key.substring(0, 3) === "Col" && rowItem[key] !== "") {
+          rowArr.push(JSON.parse(rowItem[key]));
+        }
+      }
+      return rowArr;
+    });
+    serviceCon.value = { ...e, tableHeadData: temarr[0], tableData: temarr.slice(1) };
+  } else {
+    serviceCon.value = e;
+  }
+  serviceShow.value = true;
+}
+
+//选地地区时 选则的省份改变重置city为空
+const provinceChange=(e)=>{
+  if(e.value!=formData.value.Province){
+    formData.value.Province=e.value
+  }
+}
+
+//选择地区
+const selectRegion=(e)=>{
+  formData.value.Province =e.province.value=='省'?'':e.province.value;
+  formData.value.City = e.city.value=='市'?'':e.city.value;
+}
+const formRef=ref(null)
+// 提交
+const handleSubmit=(type)=>{
+  formRef.value.validate(async (valid) => {
+    if (valid) {
+      let params = {
+        ContractType:"新签合同",
+        ContractBusinessType:"代付合同",
+        Address: formData.value.Address,
+        City: formData.value.City,
+        CompanyName: formData.value.CompanyName,
+        CreditCode: formData.value.CreditCode,
+        Province: formData.value.Province,
+        Fax: formData.value.Fax,
+        Phone: formData.value.Phone,
+        Postcode: formData.value.Postcode,
+        OriginalPrice: Number(formData.value.OriginalPrice.toString().replace(/,/g,'')),
+        sellerRemark: formData.value.sellerRemark,
+        PayRemark: formData.value.PayRemark,
+        TemplateId: RoleType.value === "ficc_seller" ? 1 : 2,
+        RelationContractId: formData.value.RelationContractId,
+      };
+      if(!params.City){
+        ElMessage.warning("请选择城市")
+        return
+      }
+      let routertype=$route.query.type;
+      if(routertype==='copy'){
+        params={
+          SourceId:Number($route.query.contractId),
+          ...params,
+        }
+      }
+      if(routertype === "applyRetrial" || routertype === "modifyConstract" || routertype === "edit"||routertype==='modifyAndApply'){
+        params={
+          ContractId:Number($route.query.contractId),
+          ...params
+        }
+      }
+
+      if (type === "预览") {
+        params = { ...params, IsAudit: false,ReAudit:false };
+        handlePreview(params);
+        return;
+      } else if (type === "存草稿") {
+        params = { ...params, IsAudit: false,ReAudit:false };
+      } else {
+        params = { ...params, IsAudit: true,ReAudit:true };
+      }
+
+      if(routertype==='copy'){
+        const res = await contractInterface.addContract(params);
+        if (res.Ret === 200) {
+          $router.go(-1);
+        }
+      }
+      if(routertype === "applyRetrial" || routertype === "modifyConstract" || routertype === "edit"||routertype==='modifyAndApply'){
+        const res = await contractInterface.editContract(params);
+        if (res.Ret === 200) {
+          $router.go(-1);
+        }
+      }
+    }else{
+      ElMessage.warning('请完善必填项')
+    }
+  });
+}
+//前去预览
+const handlePreview=(params)=>{
+  contractInterface.previewContract(params).then((res) => {
+    if (res.Ret === 200) {
+      sessionStorage.setItem("contractdtl", res.Data.Html);
+      let { href } = $router.resolve({ path: "/contractdtl" });
+      window.open(href, "_blank");
+    }
+  });
+}//选地地区时 选则的省份改变重置city为空
+
+
+// 金额输入框获取焦点
+const handlePriceBoxFocus=(key)=>{
+  let val=formData.value[key].toString()
+  val=val.replace(/,/g,'')
+  formData.value[key]=val
+}
+
+// 金额输入框获失去焦点
+const handlePriceBoxBlur=(key)=>{
+  let str=formData.value[key].toString()
+  let num1='',num2=''
+  if(str.indexOf(".")!=-1){
+    num1=str.substring(0,str.indexOf("."))
+    num2=str.substring(str.length,str.indexOf("."))
+    if(Number(num2)<=0){
+      num2=''
+    }
+  }else{
+    num1=str
+  }
+  formData.value[key]=num1.replace(/(?!^)(?=(\d{3})+$)/g, ',')+num2
+}
+
+getDetail()
+getContractOperationList()
+
+</script>
+
+<template>
+  <div class="contractdetail-dc flex">
+    <el-form ref="formRef" :model="formData" label-position="left" :rules="formRule" label-width="110px" style="width: 80%"
+    size="large">
+      <section class="section">
+        <h2 class="section-title">客户信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="代付方" prop="CompanyName" style="width: 50%">
+              <el-input v-model="formData.CompanyName" placeholder="请输入名称" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="社会信用码" prop="CreditCode" style="width: 50%">
+              <el-input v-model="formData.CreditCode" placeholder="请输入社会信用码" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="公司地址" prop="Province" style="width: 50%">
+              <v-distpicker :province-source="province_sorce_value"
+                :city-source="city_sorce_value"  :province="formData.Province" :city="formData.City" hide-area @province="provinceChange" @selected="selectRegion"></v-distpicker>
+            </el-form-item>
+            <el-form-item label="详细地址" prop="Address" style="width: 50%">
+              <el-input v-model="formData.Address" placeholder="请输入详细地址" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="传真" prop="Fax" style="width: 50%">
+              <el-input v-model="formData.Fax" placeholder="请输入传真" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="电话" prop="Phone" style="width: 50%">
+              <el-input v-model="formData.Phone" placeholder="请输入电话" style="width: 350px"></el-input>
+            </el-form-item>
+          </div>
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item label="邮编" prop="Postcode" style="width: 50%">
+              <el-input v-model="formData.Postcode" placeholder="请输入邮编" style="width: 350px"></el-input>
+            </el-form-item>
+            <el-form-item label="代付金额" prop="OriginalPrice" style="width: 50%">
+              <el-input v-model="formData.OriginalPrice" @focus="handlePriceBoxFocus('OriginalPrice')" @blur="handlePriceBoxBlur('OriginalPrice')" placeholder="请输入金额" style="width: 350px"></el-input>
+              <span style="line-height: 40px;margin-left:20px">{{ digitUppercase(formData.OriginalPrice) }}</span>
+            </el-form-item>
+          </div>
+        </div>
+      </section>
+
+      <section class="section">
+        <el-form-item label="付款方式说明" prop="PayRemark">
+          <el-input type="textarea" v-model="formData.PayRemark" placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)" style="width: 80%"></el-input>
+          <p>说明模板:甲方自合同生效日起的十日之内一次性支付一年服务费,乙方收款后为甲方开具合法有效的增值税发票。</p>
+        </el-form-item>
+        <el-form-item label="实际使用方" prop="trueUseName" class="border-top">
+          <el-select v-model="formData.trueUseName" filterable remote placeholder="关键词搜索" ref="trueUseNameRef"
+          :remote-method="customeSearch" @visible-change="handleSelectTrueUseName" style="width: 500px">
+            <el-option v-for="item in searchList" :key="item" :label="item" :value="item"> </el-option>
+          </el-select>
+        </el-form-item>
+      </section>
+
+      <section class="section" v-if="relationContractData">
+        <h2 class="section-title">合同信息</h2>
+        <div class="section-container">
+          <div class="flex border-top" style="padding-top: 30px">
+            <el-form-item disabled label="合同编号" style="width: 50%">
+              <span>{{ relationContractData.ContractCode }}</span>
+            </el-form-item>
+            <el-form-item disabled label="合同归属" style="width: 50%">
+              <span>{{ relationContractData.ProductId === 1 ? "ficc" : "权益" }}</span>
+            </el-form-item>
+          </div>
+          <el-form-item disabled label="合同类型" class="border-top">
+            <el-radio-group v-model="relationContractData.ContractType">
+              <div style="display: flex">
+                <div style="margin-right: 10px">
+                  <el-radio label="新签合同" disabled>新签合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>没有正式转试用记录的客户,在申请转正时提交的合同</template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right: 10px">
+                  <el-radio label="续约合同" disabled>续约合同</el-radio>
+                  <el-tooltip effect="dark" style="cursor: pointer;width:20px;">
+                    <template #content>
+                      1、有正式转试用记录的客户,在申请转正时提交的合同<br>
+										  2、所有客户在续约申请时提交的合同
+                    </template>
+                    <el-icon :size="16" style="margin-left:-20px"><InfoFilled /></el-icon>
+                  </el-tooltip>
+                </div>
+                <div style="margin-right: 10px" v-if="RoleType !== 'rai_seller'">
+                  <el-radio label="补充协议" disabled>补充协议</el-radio>
+                </div>
+              </div>
+            </el-radio-group>
+          </el-form-item>
+          <div class="flex border-top" v-if="relationContractData.timeRange">
+            <el-form-item disabled label="合同期限" style="width: 50%">
+              <el-date-picker disabled v-model="relationContractData.timeRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
+            </el-form-item>
+            <span style="line-height: 40px">{{ formateYear(relationContractData.timeRange) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item disabled label="合同金额" style="width: 50%">
+              <el-input disabled :value="formatPrice(relationContractData.OriginalPrice)" placeholder="请输入合同金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(relationContractData.OriginalPrice) }}</span>
+          </div>
+
+          <div class="flex border-top">
+            <el-form-item disabled label="优惠后金额" style="width: 50%">
+              <el-input disabled :value="formatPrice(relationContractData.Price)" placeholder="请输入优惠后金额" style="width: 220px"></el-input>
+            </el-form-item>
+            <span style="line-height: 40px">{{ digitUppercase(relationContractData.Price) }}</span>
+          </div>
+
+          <el-form-item label="付款方式说明" disabled class="border-top">
+            <el-input
+              type="textarea"
+              disabled
+              v-model="relationContractData.PayRemark"
+              placeholder="请输入付款方式说明(请参考以下说明模板填写,请勿填写现金/转账等支付形式)"
+              style="width: 80%"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="付款方" disabled class="border-top">
+            <el-input
+              type="textarea"
+              disabled
+              v-model="relationContractData.PayChannel"
+              placeholder="代付,请填写代付的券商名称或期货公司名称;非代付,请填写无。(请勿填写现金/转账等支付形式)"
+              style="width: 80%"
+            ></el-input>
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="section" v-if="relationContractData">
+        <h2 class="section-title">服务内容</h2>
+          <div class="section-container">
+            <!-- ficc 服务内容表格 -->
+            <FiccServiceTable
+              :canEdit="false"
+              :serviceData="ficcServiceData"
+              :hasSercive="relationContractData.Service"
+              @handleShowService="handleShowService"
+              v-if="relationContractData.ProductId === 1 && ficcServiceData"
+            ></FiccServiceTable>
+
+            <!-- 权益 服务内容表格 -->
+            <QyServiceTable
+              :canEdit="false"
+              :serviceData="qyServiceData"
+              :hasSercive="relationContractData.Service"
+              @handleShowService="handleShowService"
+              v-if="relationContractData.ProductId === 2 && qyServiceData"
+            ></QyServiceTable>
+          </div>
+          <p style="margin-top: 30px; margin-bottom: 20px">补充内容(eg额外赠送、路演次数规定)</p>
+          <el-radio :model-value="relationContractData.Remark?'1':'2'" label="2" disabled>无</el-radio>
+          <el-radio :model-value="relationContractData.Remark?'1':'2'" label="1" disabled>有</el-radio>
+          <el-input v-if="relationContractData.Remark" disabled type="textarea" v-model="relationContractData.Remark" rows="5" placeholder="请输入内容" style="margin: 20px 0; display: block; box-sizing: border-box"></el-input>
+      </section>
+
+      <section class="section">
+        <h2 class="section-title">审批备注</h2>
+        <div class="section-container">
+          <el-form-item label="备注" prop="sellerRemark" class="border-top">
+            <el-input type="textarea" v-model="formData.sellerRemark" placeholder="请输入备注(仅供审批人查看,不在生成合同中展示)" style="width: 80%"></el-input>
+          </el-form-item>
+        </div>
+      </section>
+    </el-form>
+    <div class="right-wrap">
+      <div style="display: flex; justify-content: space-between">
+        <el-button type="primary" plain style="width: 30%;min-width:70px;margin-bottom:10px" @click="handleSubmit('存草稿')" size="large">存草稿</el-button>
+        <el-button type="primary" style="width: 30%;margin-left:3%;min-width:70px;margin-bottom:10px" @click="handleSubmit('预览')" size="large">预览</el-button>
+        <el-button type="primary" style="width: 30%;margin-left:3%;min-width:70px;margin-bottom:10px" @click="handleSubmit('提交')" size="large">提交</el-button>
+      </div>
+      <div class="timeline-wrap">
+        <el-timeline>
+          <el-timeline-item color="#409EFF" v-for="item in operationList" :key="item.Id" placement="top" :timestamp="item.CreateTimeStr">
+            {{item.OpUserName}}{{ item.Remark }}
+          </el-timeline-item>
+        </el-timeline>
+      </div>
+    </div>
+    <!-- 查看报价单弹窗 -->
+    <el-dialog v-dialogDrag v-model="serviceShow" width="60%" class="self-dialog-c" :modal-append-to-body="false" @close="serviceShow=false" :show-close="false">
+      <h2 class="title" style="text-align: center; color: #000">{{ RoleType == 'ficc_seller'? serviceCon.Title : serviceCon.showTitle }}</h2>
+      <img class="img" style="width: 100%; display: block; margin: 20px auto" 
+      :src="RoleType == 'ficc_seller'? serviceCon.Value : serviceCon.showValue" />
+      <div style="text-align: center; margin-bottom: 30px">
+        <el-button type="primary" @click="serviceShow=false" size="large">知道了</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 选择合同弹窗 -->
+    <el-dialog v-dialogDrag :title="`搜索到${formData.trueUseName}下合同`" :append-to-body="true" v-model="showContractList" width="50%" center
+    @close="closeTrueUseNameDia">
+      <el-table :data="contractList" border height="60vh">
+        <el-table-column align="center" width="50">
+          <template #default="scope">
+            <el-radio v-model="formData.RelationContractId" :label="scope.row.ContractId"><span></span></el-radio>
+          </template>
+        </el-table-column>
+        <el-table-column label="合同编号" prop="ContractCode" align="center"></el-table-column>
+        <el-table-column label="销售" prop="SellerName" align="center" width="80"></el-table-column>
+        <el-table-column label="合同类型" prop="ContractType" align="center" width="80"></el-table-column>
+        <el-table-column label="合同有效期" prop="ContractType" align="center" width="180">
+          <template #default="scope">{{ formatTime(scope.row.StartDateStr) }}~{{ formatTime(scope.row.EndDateStr) }}</template>
+        </el-table-column>
+        <el-table-column key="合同金额" align="center" prop="Price" label="合同金额" width="100"></el-table-column>
+        <el-table-column key="合同状态" align="center" prop="Status" label="合同状态" width="80"></el-table-column>
+        <el-table-column key="签回时间" align="center" prop="CheckBackFileTimeStr" label="签回时间" width="160">
+          <template #default="scope">{{ formatTime(scope.row.CheckBackFileTimeStr) }}</template>
+        </el-table-column>
+      </el-table>
+      <div style="text-align: center; margin: 30px 0">
+        <el-button type="primary" @click="handleRelation" size="large">确定关联</el-button>
+        <el-button type="primary" plain @click="showContractList = false" size="large">取消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style>
+.contractdetail-dc .self-dialog-c .el-dialog__header {
+  background-color: #fff;
+  padding: 0;
+}
+.contractdetail-dc .self-dialog-c {
+  width: auto;
+  max-width: 60%;
+}
+.contractdetail-dc .el-timeline-item__tail {
+  border-left: 2px solid #409eff;
+}
+</style>
+<style lang="scss" scoped>
+.contractdetail-dc{
+    min-height: calc(100vh - 250px);
+    position: relative;
+    font-size: 16px;
+    color: #000;
+}
+.flex {
+  display: flex;
+}
+.right-wrap {
+  margin-left: 20px;
+  flex: 1;
+  .timeline-wrap {
+    margin-top: 20px;
+    background-color: #fff;
+    border: 1px solid #aab4cc;
+    padding: 20px;
+    border-radius: 4px;
+    text-align: left;
+    height: calc(100% - 120px);
+    max-height: 1130px;
+    overflow-y: auto;
+  }
+}
+.section {
+  background-color: #fff;
+  border: 1px solid #aab4cc;
+  border-radius: 4px;
+  padding: 20px 30px;
+  margin-bottom: 20px;
+  .section-title {
+    margin-bottom: 30px;
+  }
+  .border-top {
+    padding-top: 30px;
+    border-top: 1px solid #dcdfe6;
+  }
+}
+</style>

+ 379 - 0
src/views/dataReport_manage/voicePlayList.vue

@@ -0,0 +1,379 @@
+<script setup>
+import { reactive } from 'vue'
+import { Search } from "@element-plus/icons-vue";
+import { dataMainInterface, interactiveInterface, customInterence } from '@/api/api.js'
+
+function formatSource(e) {
+  if (e == 1) {
+    return '小程序'
+  } else if (e == 2) {
+    //实际为小程序pc
+    return 'PC端'
+  } else if (e == 3) {
+    //实际为web pc
+    return 'PC端'
+  } else {
+    return '--'
+  }
+}
+
+
+const pageState = reactive({
+  sectionOpts: [],//板块选项
+  selectSec: '',//选择的板块
+  varietyOpts: [],//品种选项
+  selectVari: '',//选择的品种
+  userOpts: [],//
+  selectUser: '',
+  keyword: '',
+  ClickSort: '',
+
+  tableData: [],
+  page_no: 1,
+  pageSize: 15,
+  totals: 0,
+
+  showDia: false,
+  clickTableData: [],
+  page_no_click: 1,
+  pageSizeClick: 15,
+  totalsClick: 0,
+  curTitle: '',
+  curNum: '',
+  curId: 0,
+})
+
+
+async function geTableList() {
+  const res = await dataMainInterface.voicePlayStatistic({
+    CurrentIndex: pageState.page_no,
+    PageSize: pageState.pageSize,
+    SectionId: pageState.selectSec,
+    VarietyId: pageState.selectVari,
+    AdminId: pageState.selectUser,
+    Title: pageState.keyword,
+    ClickSort: pageState.ClickSort
+  })
+  if (res.Ret != 200) return
+  const { List, Paging } = res.Data;
+
+  pageState.tableData = List || [];
+  pageState.totals = Paging.Totals;
+}
+
+function handleCurrentChange(e) {
+  pageState.page_no = e
+  geTableList()
+}
+
+function handleFilter() {
+  pageState.page_no = 1
+  geTableList()
+}
+
+function sortChangeHandle(item) {
+  if (item.order == "descending") {
+    pageState.ClickSort = 2
+  } else if (item.order == "ascending") {
+    pageState.ClickSort = 1
+  } else {
+    pageState.ClickSort = ''
+  }
+  pageState.page_no = 1
+  geTableList()
+}
+
+
+function handleShowDetail(item) {
+  pageState.curTitle = item.BroadcastName
+  pageState.curNum = item.VisitCount
+  pageState.curId = item.BroadcastId
+  pageState.clickTableData = []
+  pageState.page_no_click = 1
+  pageState.totalsClick = 0
+  getDetail()
+  pageState.showDia = true
+}
+
+//获取某个语音播报详情数据
+async function getDetail() {
+  const res = await dataMainInterface.voicePlayDetailSta({
+    CurrentIndex: pageState.page_no_click,
+    PageSize: pageState.pageSizeClick,
+    BroadcastId: Number(pageState.curId)
+  })
+  if (res.Ret != 200) return
+  const { List, Paging } = res.Data;
+
+  pageState.clickTableData = List || [];
+  pageState.totalsClick = Paging.Totals;
+}
+
+function handleClickCurrentChange(e) {
+  pageState.page_no_click = e
+  getDetail()
+}
+
+//获取语音管理员筛选项
+async function getUserOpts() {
+  const res = await interactiveInterface.messageManageList({
+    RoleTypeCode: 'yb_voice_admin',
+    PageSize: 10000,
+    CurrentIndex: 1
+  })
+  if (res.Ret === 200) {
+    pageState.userOpts = res.Data.List || []
+  }
+}
+
+//获取品种数据
+async function getClassifyList() {
+  const res = await customInterence.getvariety({ CompanyType: "ficc" })
+  if (res.Ret !== 200) return
+  pageState.varietyOpts = res.Data.List || []
+}
+
+//获取板块数据
+async function getSectionList() {
+  const res = await interactiveInterface.voiceList({
+    CurrentIndex: pageState.page_no,
+    PageSize: 100000
+  })
+  if (res.Ret !== 200) return
+  const { List, Paging } = res.Data;
+
+  pageState.sectionOpts = List || [];
+}
+
+
+
+
+
+function initPage() {
+  geTableList()
+  getUserOpts()
+  getClassifyList()
+  getSectionList()
+}
+initPage()
+
+</script>
+
+<template>
+  <div class="voice-play-list-page">
+    <div class="top-filter-wrap">
+      <el-select
+        v-model="pageState.selectSec"
+        placeholder="请选择板块名称"
+        clearable
+        @change="handleFilter"
+      >
+        <el-option
+          v-for="item in pageState.sectionOpts"
+          :key="item.SectionId"
+          :label="item.SectionName"
+          :value="item.SectionId"
+        >
+        </el-option>
+      </el-select>
+      <el-cascader
+        v-model="pageState.selectVari"
+        placeholder="请选择品种"
+        :options="pageState.varietyOpts"
+        :props="{
+          value: 'ChartPermissionId',
+          label: 'ClassifyName',
+          children: 'Items',
+          emitPath: false,
+        }"
+        :show-all-levels="false"
+        ref="cascader"
+        clearable
+        @change="handleFilter"
+        style="margin: 0 20px"
+      ></el-cascader>
+      <el-select
+        v-model="pageState.selectUser"
+        filterable
+        placeholder="请选择语音管理员"
+        clearable
+        @change="handleFilter"
+      >
+        <el-option
+          v-for="item in pageState.userOpts"
+          :key="item.AdminId"
+          :label="item.RealName"
+          :value="item.AdminId"
+        >
+        </el-option>
+      </el-select>
+      <el-input
+        placeholder="请输入"
+        v-model="pageState.keyword"
+        style="max-width: 400px; margin-bottom: 8px; float: right"
+        @input="handleFilter"
+        clearable
+        :prefix-icon="Search"
+      >
+      </el-input>
+    </div>
+    <div>
+      <el-table
+        :data="pageState.tableData"
+        border
+        style="width: 100%"
+        @sort-change="sortChangeHandle"
+      >
+        <el-table-column prop="BroadcastName" label="语音标题" align="center" />
+        <el-table-column prop="SectionName" label="板块名称" align="center" />
+        <el-table-column
+          prop="VarietyName"
+          label="品种"
+          align="center"
+          width="200"
+        />
+        <el-table-column
+          prop="PublishTime"
+          label="发布时间"
+          align="center"
+          width="200"
+        />
+        <el-table-column
+          prop="Author"
+          label="语音管理员"
+          align="center"
+          width="200"
+        />
+        <el-table-column
+          prop="VisitCount"
+          label="点击量"
+          width="200"
+          align="center"
+          sortable="custom"
+        >
+          <template #default="scope">
+            <span
+              style="color: #409eff; cursor: pointer"
+              @click="handleShowDetail(scope.row)"
+              >{{ scope.row.VisitCount }}</span
+            >
+          </template>
+        </el-table-column>
+        <template #empty>
+          <div style="margin: 50px 0">
+            <img
+              style="width: 150px"
+              src="~@/assets/img/cus_m/nodata.png"
+              alt=""
+            />
+            <div>暂无数据~</div>
+          </div>
+        </template>
+      </el-table>
+      <el-pagination
+        layout="total,prev,pager,next,jumper"
+        background
+        :current-page="pageState.page_no"
+        @current-change="handleCurrentChange"
+        :page-size="pageState.pageSize"
+        :total="pageState.totals"
+        style="float: right; margin-top: 20px"
+      >
+      </el-pagination>
+    </div>
+
+    <!-- 点击量弹窗 -->
+    <el-dialog
+      v-model="pageState.showDia"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      class="dialog"
+      center
+      width="900px"
+      draggable
+      title="点击量详情"
+    >
+      <div class="click-detail-dia-warp">
+        <div class="title">{{ pageState.curTitle }}</div>
+        <div class="num">点击量:{{ pageState.curNum }}</div>
+        <el-table :data="pageState.clickTableData" border style="width: 100%">
+          <el-table-column prop="RealName" label="用户姓名" align="center" />
+          <el-table-column prop="CompanyName" label="公司名称" align="center" />
+          <el-table-column
+            prop="CompanyStatus"
+            label="客户状态"
+            align="center"
+            width="100"
+          />
+          <el-table-column
+            prop="VisitCount"
+            label="点击次数"
+            align="center"
+            width="100"
+          />
+          <el-table-column
+            prop="Source"
+            label="点击来源"
+            align="center"
+            width="100"
+          >
+            <template #default="scope">
+              <span>{{ formatSource(scope.row.Source) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="CreateTime"
+            label="最近一次点击时间"
+            align="center"
+            width="180"
+          />
+          <template #empty>
+            <div style="margin: 50px 0">
+              <img
+                style="width: 100px"
+                src="~@/assets/img/cus_m/nodata.png"
+                alt=""
+              />
+              <div>暂无数据~</div>
+            </div>
+          </template>
+        </el-table>
+        <el-pagination
+          layout="total,prev,pager,next,jumper"
+          background
+          :current-page="pageState.page_no_click"
+          @current-change="handleClickCurrentChange"
+          :page-size="pageState.pageSizeClick"
+          :total="pageState.totalsClick"
+          style="float: right; margin-top: 20px"
+        >
+        </el-pagination>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.voice-play-list-page {
+  background: #fff;
+  border: 1px solid #ececec;
+  border-radius: 4px;
+  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+  padding: 30px 30px 100px 30px;
+  min-height: 80vh;
+}
+.top-filter-wrap {
+  margin-bottom: 30px;
+}
+
+.click-detail-dia-warp {
+  padding-bottom: 100px;
+  .title {
+    font-size: 14px;
+    margin-bottom: 10px;
+  }
+  .num {
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 142 - 0
src/views/interaction_manage/bannerStatistics.vue

@@ -0,0 +1,142 @@
+<script setup>
+import { departInterence } from "@/api/api.js";
+import { ref } from 'vue'
+
+const tableData = ref([])
+async function getDataList() {
+  const res = await departInterence.getBannerStatistic();
+  if (res.Ret === 200) {
+    tableData.value = res.Data || [];
+  }
+}
+getDataList()
+
+
+</script>
+
+<template>
+  <div class="container-banner-statistics">
+    <div
+      class="table-body-wrapper"
+      style="max-height: calc(100vh - 180px); overflow: auto"
+    >
+      <table border>
+        <thead>
+          <td class="thead-rs">名称</td>
+          <td class="thead-rs">日期</td>
+          <td class="thead-rs">点击量合计</td>
+          <td class="thead-rs">点击量</td>
+          <td class="thead-rs">点击来源</td>
+          <td class="thead-rs">最近一次点击时间</td>
+        </thead>
+        <tbody v-for="item in tableData" :key="item.ViewHistoryID">
+          <tr
+            v-for="(_item, index) in item.SourceList"
+            :key="_item.LastUpdatedTime"
+          >
+            <!-- <td class="thead-rs" :rowspan="item.SourceList.length" v-if="index == 0"> <img :src="item.BannerUrl" class="table-img" alt="" /></td> -->
+            <td
+              class="thead-rs"
+              :rowspan="item.SourceList.length"
+              v-if="index == 0"
+            >
+              {{ item.Remark }}
+            </td>
+            <td
+              class="thead-rs"
+              :rowspan="item.SourceList.length"
+              v-if="index == 0"
+            >
+              {{ item.StartDate }} -- {{ item.EndDate }}
+            </td>
+            <td
+              class="thead-rs"
+              :rowspan="item.SourceList.length"
+              v-if="index == 0"
+            >
+              {{ item.Pv }}
+            </td>
+            <td class="thead-rs">{{ _item.Pv }}</td>
+            <td class="thead-rs">
+              {{
+                _item.FirstSource == 1
+                  ? "小程序移动端"
+                  : _item.FirstSource == 2
+                  ? "小程序PC端"
+                  : _item.FirstSource == 3
+                  ? "研报官网"
+                  : ""
+              }}
+              {{
+                _item.SecondSource == 1
+                  ? "/ 首页"
+                  : _item.SecondSource == 2
+                  ? "/ 研报详情页"
+                  : ""
+              }}
+            </td>
+            <td class="thead-rs">{{ _item.LastUpdatedTime }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.container-banner-statistics {
+  .table-body-wrapper {
+    max-height: calc(100vh - 100px);
+    overflow-y: scroll;
+    overflow-x: auto;
+    border-bottom: 1px solid #dcdfe6;
+    border-top: 1px solid #dcdfe6;
+    .table-data-empty {
+      color: #666666;
+      height: 530px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border-left: 1px solid #dcdfe6;
+      border-right: 1px solid #dcdfe6;
+    }
+  }
+  table {
+    width: 100%;
+    font-size: 14px;
+    color: #666;
+    thead {
+      position: sticky;
+      top: 0;
+      z-index: 1;
+      left: 0;
+      border-left: 1px solid #dcdfe6;
+      border-right: 1px solid #dcdfe6;
+      td {
+        border: none;
+        outline-color: #dcdfe6;
+        outline-style: solid;
+        outline-width: 0.5px;
+        background-color: #ebeef5 !important ;
+        color: #333333;
+        font-weight: 500;
+      }
+    }
+    td,
+    th {
+      min-width: 35px;
+      // word-break: break-all;
+      border: 1px solid #dcdfe6;
+      height: 45px;
+      text-align: center;
+      background-color: #fff;
+    }
+  }
+  .table-img {
+    padding: 20px 0;
+    width: 368px;
+    height: 107px;
+    box-sizing: border-box;
+  }
+}
+</style>

+ 990 - 0
src/views/interaction_manage/questionManage.vue

@@ -0,0 +1,990 @@
+<script setup>
+import { reactive, ref, onMounted } from 'vue'
+import { departInterence, InteractionInterence } from "@/api/api.js";
+import { ElMessage } from 'element-plus';
+import _ from 'lodash'
+
+const pageState = reactive({
+  statusType: "", //提问状态筛选框
+  answers: [], //回答者筛选框
+  ReplierIds: "",
+  countFree: 0,
+  countWait: 0,
+  statusOptions: [],
+  answerOptions: [],//不包含被禁用的研究员
+  answerOptionsAll: [],//所有研究员
+  answerOptionsProp: {
+    value: "admin_id",
+    label: "admin_name",
+    children: "children",
+  }, //回答者选择配置
+  tableData: [],
+  tableLoading: false,
+  page_no: 1,
+  pageSize: 10,
+  total: 0,
+  tableColumns: [
+    {
+      label: "提问内容",
+      key: "QuestionContent",
+      minWidth: 170,
+    },
+    {
+      label: "提问人",
+      key: "RealName",
+    },
+    {
+      label: "提问时间",
+      key: "CreateTime",
+    },
+    {
+      label: "提问状态",
+      key: "ReplyStatus",
+    },
+    {
+      label: "回答者",
+      key: "answer",
+      minWidth: 150,
+    },
+    {
+      label: "回答时间",
+      key: "ReplyTime",
+    },
+    {
+      label: "点击量",
+      key: "ClickNum",
+    },
+  ],
+  questionStatusMap: {
+    // 1: "待分配",
+    2: "待回答",
+    3: "已回答",
+    4: "已终止",
+  },
+  deletePopVisible: false,
+  isEditShow: false,
+  currentEdit: {
+    text: "",
+    id: 0,
+  },
+  reflesh: 0,
+  userInfoDialog: false,
+  clickNumDialog: false,
+  clickLogsData: [],
+  clickNum: 0,
+  clickQuestionContent: '',
+  clickLogsTotal: 0,
+  clickLogsCurrentIndex: 1,
+  clickLogsPageSize: 10,
+  questionId: 0,
+  userDetail: {},
+  SortParam: '',//点击量排序
+  SortType: '',
+
+  maxNum: 28,//字数限制
+
+  questionInfoDialog: false,
+  questionInfo: null,
+
+  stopReasonDialog: false,
+  stopReason: '',
+
+  moveAnswerDialog: false,
+  moveQuestionId: 0,
+  moveAnswerOpt: [],
+  moveAnswerId: [],
+})
+
+
+//删除问题
+function handleDeleteAuth(item) {
+  InteractionInterence.deleteQuestion({
+    QuestionId: item.CommunityQuestionId
+  }).then(res => {
+    if (res.Ret === 200) {
+      ElMessage.success('删除成功');
+      getTableData();
+    }
+  })
+}
+function handleSelect(type) {
+  pageState.page_no = 1;
+  if (type === "status") {
+    getTableData();
+  } else {
+    let temp = [];
+    pageState.answers.forEach((item) => {
+      temp.push(item[item.length - 1]);
+    });
+    pageState.ReplierIds = _.uniq(temp).join(",");
+    getTableData();
+  }
+}
+/**
+ * 获取级联选择器回显
+ * @param {options}  options 树形数组格式(answerOptions)
+ * @param {answerId} answerId 回答者的id,字段为admin_id
+ * @param {keyWord} keyWord 返回项的key
+ * @return [0,1,2] (keyWord=admin_id) 或 ['宏观组','利率债','xx'] (keyWord=admin_name)
+ */
+function getAnswer(options, answerId, keyWord = "admin_id") {
+  const getAnswerOption = (list, value) => {
+    for (let i in list) {
+      if (list[i].admin_id == value) {
+        return [list[i]];
+      }
+      if (list[i].children) {
+        let node = getAnswerOption(list[i].children, value);
+        if (node !== undefined) {
+          return node.concat(list[i]);
+        }
+      }
+    }
+  };
+  const answer = getAnswerOption(options, answerId);
+  return answer.map((i) => i[keyWord]).reverse();
+}
+async function getTableData() {
+  pageState.tableLoading = true;
+  const res = await InteractionInterence.getQuestionList({
+    ReplyStatus: pageState.statusType,
+    ReplierIds: pageState.ReplierIds,
+    CurrentIndex: pageState.page_no,
+    PageSize: pageState.pageSize,
+    SortParam: pageState.SortParam,
+    SortType: pageState.SortType
+  });
+  if (res.Ret === 200) {
+    const { List, Paging, Count } = res.Data;
+    pageState.tableData = List;
+    pageState.tableData && processTableData();
+    pageState.page_no = Paging.CurrentIndex;
+    pageState.total = Paging.Totals;
+    if (Count) {
+      pageState.countFree = Count.free;
+      pageState.countWait = Count.wait;
+    }
+  } else if (res.Ret === 403) {
+    //无权查看
+    pageState.tableData = []
+  }
+  pageState.tableLoading = false;
+}
+//处理table数据,方便answer的回显
+function processTableData() {
+  pageState.tableData.forEach((item) => {
+    if (item.MsgSendStatus === 0) {
+      item.isSent = false;
+    } else {
+      item.isSent = true;
+    }
+    //未回答且没发送通知的问题:回答者一栏显示级联选择器
+    if (item.ReplierAdminId && !item.isSent || item.NeedRedistribute) {
+      //item.answer = getAnswer(pageState.answerOptions, item.ReplierAdminId);
+      item.answer = [item.ResearchGroupFirstId, item.ResearchGroupSecondId, item.ReplierAdminId]
+    }
+    //未分配的提问,回答者一栏显示级联选择器
+    if (!item.ReplierAdminId) {
+      item.answer = []
+    }
+    //已发送通知或已回答的问题:回答者一栏显示文字
+    if (item.isSent && !item.NeedRedistribute || item.ReplyStatus === 3) {
+      /* item.answerText = this.getAnswer(
+        this.answerOptions,
+        item.ReplierAdminId,
+        "admin_name"
+      ).join("/"); */
+      item.answerText = item.ResearchGroupFirstName + '/' + item.ResearchGroupSecondName + '/' + item.ReplierRealName
+    }
+  });
+}
+//分配回答者
+async function handleAnswerChange(item) {
+  console.log("item", item);
+  if (item.QuestionContent.length > pageState.maxNum) {
+    ElMessage.warning("字数超限,请修改问题后重试");
+    pageState.tableData.map((i) => {
+      if (item.CommunityQuestionId === i.CommunityQuestionId) {
+        i.answer = [];
+      }
+    });
+    pageState.reflesh++;
+    return;
+  }
+  const AdminId = item.answer[item.answer.length - 1];
+  const ResearchGroupFirstId = item.answer[0];
+  const ResearchGroupSecondId = item.answer[1];
+  const res = await InteractionInterence.distributeAnswer({
+    QuestionId: item.CommunityQuestionId,
+    AdminId,
+    ResearchGroupFirstId,
+    ResearchGroupSecondId,
+  });
+  if (res.Ret === 200) {
+    if (res.Msg === '操作成功') {
+      //回答者已关注公众号的情况
+      ElMessage.success("分配成功!");
+    } else {
+      //回答者未关注公众号的情况
+      ElMessage.error("该研究员未关注公众号,无法收到消息通知")
+    }
+  }
+  getTableData();
+}
+//发送通知
+function sentMsg(item) {
+  if (!item.answer) return;
+  InteractionInterence.sendMsg({
+    QuestionId: item.CommunityQuestionId,
+  }).then((res) => {
+    if (res.Ret === 200) {
+      ElMessage.success("发送通知成功");
+      getTableData();
+    }
+  });
+}
+//获取answerOptions
+async function getResearchGroupList(include = 0) {
+  /* const res = await departInterence.getResearchGroupList({
+    Include: include
+  }); */
+  const res = await departInterence.getTagTree({
+    IncludeDisableMember: include,
+    HasResearcher: 1
+  });
+  if (res.Ret === 200) {
+    //每一级的value:admin_id,label:admin_name固定,方便级联选择器回显;
+    //第二级的researcher_list复制一份为children
+    const { Data } = res;
+    const temp = _.cloneDeep(Data);
+    temp.forEach((item) => {
+      item.admin_id = item.tag_id;
+      item.admin_name = item.tag_name;
+      if (item.tags) {
+        item.children = _.cloneDeep(item.tags)
+        item.children.map((i) => {
+          i.admin_id = i.tag_id;
+          i.admin_name = i.tag_name;
+          i.children = _.cloneDeep(i.researcher_list);
+        });
+      }
+    });
+    if (include === 1) {
+      pageState.answerOptionsAll = temp;
+    } else {
+      pageState.answerOptions = temp;
+    }
+
+  }
+}
+//获取statusOptions
+function getStatusOptions() {
+  for (let i in pageState.questionStatusMap) {
+    pageState.statusOptions.push({ value: i, label: pageState.questionStatusMap[i] });
+  }
+}
+//显示编辑问题描述弹窗
+function showEdit(item) {
+  pageState.isEditShow = true;
+  pageState.currentEdit = {
+    id: item.CommunityQuestionId,
+    text: item.QuestionContent,
+  };
+}
+//提交编辑问题
+function handleEditQuestion() {
+  const { id, text } = pageState.currentEdit;
+  if (text.length > pageState.maxNum) {
+    ElMessage.warning("字数超出最长限制");
+    return;
+  }
+  InteractionInterence.editQuestion({
+    QuestionId: id,
+    QuestionContent: text,
+  }).then((res) => {
+    if (res.Ret === 200) {
+      ElMessage.success("编辑成功");
+      pageState.isEditShow = false;
+      getTableData();
+    }
+  });
+}
+//页码改变
+function handleCurrentChange(page) {
+  pageState.page_no = page;
+  getTableData();
+}
+//提问者详情
+function userInfo(row) {
+  InteractionInterence.questionUser({
+    QuestionId: row.CommunityQuestionId,
+  }).then(res => {
+    if (res.Ret === 200) {
+      pageState.userDetail = res.Data;
+      pageState.userInfoDialog = true;
+    } else if (res.Ret === 403) {
+      //无权查看
+      pageState.userDetail = {}
+      pageState.userInfoDialog = false;
+    }
+  });
+}
+function clicksEvent(row) {
+  pageState.questionId = row.CommunityQuestionId
+  pageState.clickLogsData = [];
+  pageState.clickNum = row.ClickNum;
+  pageState.clickQuestionContent = row.QuestionContent;
+  pageState.clickNumDialog = true;
+  getClickLogsData()
+}
+
+function getClickLogsData() {
+  InteractionInterence.questionClickLogs({
+    QuestionId: pageState.questionId,
+    CurrentIndex: pageState.clickLogsCurrentIndex,
+    PageSize: pageState.clickLogsPageSize,
+  }).then(res => {
+    if (res.Ret === 200) {
+      pageState.clickLogsData = res.Data.List;
+      pageState.clickLogsCurrentIndex = res.Data.Paging.CurrentIndex;
+      pageState.clickLogsTotal = res.Data.Paging.Totals;
+    } else if (res.Ret === 403) {
+      //无权查看
+      pageState.clickLogsData = []
+    }
+  });
+}
+
+function clickLogsPageChange(currentIndex) {
+  pageState.clickLogsCurrentIndex = currentIndex;
+  getClickLogsData();
+}
+//table排序变化
+function sortChangeHandle({ prop, order }) {
+  console.log(prop, order)
+  //this.page_no = 1;
+  pageState.SortType = order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : ''
+  if (pageState.SortType.length) {
+    pageState.SortParam = prop
+  } else {
+    pageState.SortParam = ''
+  }
+  getTableData()
+}
+
+async function showQuestionDetail(e) {
+  const res = await InteractionInterence.questionDetail({ QuestionId: e.CommunityQuestionId })
+  if (res.Ret === 200) {
+    pageState.questionInfo = res.Data
+    pageState.questionInfoDialog = true
+  }
+}
+
+async function handleShowmoveAnswerDialog(e) {
+  const res = await departInterence.getTagTree({
+    IncludeDisableMember: 0,
+    HasResearcher: 1,
+    CommunityQuestionId: e.CommunityQuestionId
+  });
+  if (res.Ret === 200) {
+    const arr = res.Data || []
+    pageState.moveAnswerOpt = arr.map(item => {
+      let obj = {
+        ...item,
+        label: item.tag_name,
+        value: item.tag_id,
+        children: []
+      }
+      const arr1 = item.tags || []
+      obj.children = arr1.map(item2 => {
+        let obj2 = {
+          ...item2,
+          label: item2.tag_name,
+          value: item2.tag_id,
+          children: []
+        }
+        const arr2 = item2.researcher_list || []
+        obj2.children = arr2.map(item3 => {
+          return {
+            ...item3,
+            label: item3.admin_name,
+            value: item3.admin_id,
+            disabled: !item3.allow_select
+          }
+        })
+        return obj2
+      })
+      return obj
+    })
+    pageState.moveQuestionId = e.CommunityQuestionId
+    pageState.moveAnswerDialog = true
+  }
+}
+// 确认转移
+async function handleConfirmTrans() {
+  const res = await InteractionInterence.questionTransfer({
+    QuestionId: pageState.moveQuestionId,
+    AdminId: pageState.moveAnswerId[2],
+    ResearchGroupFirstId: pageState.moveAnswerId[0],
+    ResearchGroupSecondId: pageState.moveAnswerId[1]
+  })
+  if (res.Ret == 200) {
+    ElMessage.success('转移成功')
+    pageState.moveAnswerId = []
+    pageState.moveAnswerDialog = false
+    getTableData();
+  }
+}
+
+
+
+onMounted(async () => {
+  await getResearchGroupList(1);
+  // await getResearchGroupList();
+  getStatusOptions();
+  getTableData();
+})
+
+
+
+</script>
+
+<template>
+  <div class="question-wrap">
+    <div class="question-top">
+      <div class="question-info">
+        <span>待分配问题数:{{ pageState.countFree }}</span>
+        <span>待回答提问数:{{ pageState.countWait }}</span>
+      </div>
+      <div class="question-select">
+        <el-select
+          v-model="pageState.statusType"
+          clearable
+          placeholder="提问状态"
+          style="width: 260px;margin-right:10px"
+          @change="handleSelect('status')"
+        >
+          <el-option
+            v-for="item in pageState.statusOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          >
+          </el-option>
+        </el-select>
+        <el-cascader
+          placeholder="回答者"
+          v-model="pageState.answers"
+          collapse-tags
+          clearable
+          style="width: 260px"
+          :options="pageState.answerOptionsAll"
+          :props="{ ...pageState.answerOptionsProp, multiple: true }"
+          @change="handleSelect('answers')"
+        ></el-cascader>
+      </div>
+    </div>
+    <div class="question-table">
+      <el-table
+        :data="pageState.tableData"
+        border
+        v-loading="pageState.tableLoading"
+        :key="pageState.reflesh"
+        @sort-change="sortChangeHandle"
+      >
+        <el-table-column
+          v-for="item in pageState.tableColumns"
+          :key="item.label"
+          :label="item.label"
+          :prop="item.key"
+          :min-width="item.minWidth"
+          align="center"
+          :sortable="item.key === 'ClickNum' ? 'custom' : false"
+        >
+          <template #default="{ row }">
+            <!-- <div v-if="item.key === 'answer' && (row.ReplyStatus < 3 && !row.isSent || row.NeedRedistribute)">
+              <el-cascader v-model="row.answer" :options="answerOptions" :props="answerOptionsProp"
+                @change="handleAnswerChange(row)"></el-cascader>
+            </div> -->
+            <span v-if="item.key === 'answer'">
+              {{ row.answerText }}
+            </span>
+            <span
+              v-else-if="item.key === 'RealName' && row.UserHasDetail"
+              @click="userInfo(row)"
+              style="cursor: pointer; color: rgb(64, 153, 239)"
+            >
+              {{ row[item.key] }}
+              {{ row.UserQaCount ? `(${row.UserQaCount})` : "" }}
+            </span>
+            <span v-else-if="item.key === 'RealName' && !row.UserHasDetail">
+              {{ row[item.key] }}
+              {{ row.UserQaCount ? `(${row.UserQaCount})` : "" }}
+            </span>
+            <span v-else-if="item.key === 'ReplyStatus'">
+              {{ pageState.questionStatusMap[row[item.key]] }}
+            </span>
+            <span v-else-if="item.key === 'ReplyTime'">
+              {{ row.ReplyStatus === 3 ? row.ReplyTime : "" }}
+            </span>
+            <span v-else-if="item.key === 'ClickNum'">
+              <span
+                v-if="row.ClickNum > 0"
+                @click="clicksEvent(row)"
+                style="cursor: pointer; color: rgb(64, 153, 239)"
+              >
+                {{ row.ClickNum }}</span
+              >
+              <span v-else>{{ "--" }}</span>
+            </span>
+            <div
+              style="
+                display: flex;
+                justify-content: center;
+                cursor: pointer;
+                color: rgb(64, 153, 239);
+              "
+              v-else-if="item.key === 'QuestionContent'"
+              @click="showQuestionDetail(row)"
+            >
+              {{ row[item.key] }}
+              <span
+                style="
+                  margin-left: 5px;
+                  min-width: 16px;
+                  max-width: 16px;
+                  height: 16px;
+                  align-self: center;
+                "
+                @click.stop="showEdit(row)"
+                v-if="row.MsgSendStatus === 0 && row.ReplyStatus < 3"
+              >
+                <img
+                  style="width: 100%"
+                  :src="require('@/assets/img/icons/editquestion.png')"
+                  alt=""
+                />
+              </span>
+            </div>
+            <span v-else>{{ row[item.key] }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center">
+          <template #default="{ row }">
+            <span
+              v-if="row.ReplyStatus === 4"
+              style="color: #409eff; display: inline-block; cursor: pointer"
+              @click="
+                (pageState.stopReasonDialog = true),
+                  (pageState.stopReason = row.StopReason)
+              "
+              >终止原因</span
+            >
+            <!-- <span v-if="row.ReplyStatus===2" style="color:#409EFF;display:inline-block;cursor: pointer" @click="handleShowmoveAnswerDialog(row)">转移并通知</span> -->
+            <!-- <span v-if="row.ReplyStatus < 3 && !row.isSent" @click="sentMsg(row)" style="cursor: pointer" :style="{
+              color: row.answer ? 'rgb(64, 153, 239)' : 'gray',
+              cursor: row.answer ? 'pointer' : 'not-allowed',
+            }">发送通知</span> -->
+            <el-popover
+              placement="top"
+              width="160"
+              v-model="row.deletePopVisible"
+              :ref="`pop_${row.CommunityQuestionId}`"
+            >
+              <p>该操作不可撤销,确定删除吗?</p>
+              <div style="text-align: right; margin: 0">
+                <el-button
+                  size="mini"
+                  type="text"
+                  @click="row.deletePopVisible = false"
+                  >取消</el-button
+                >
+                <el-button
+                  type="primary"
+                  size="mini"
+                  @click="handleDeleteAuth(row)"
+                  >确定</el-button
+                >
+              </div>
+              <template #reference>
+                <span style="cursor: pointer; color: #d1433a">删除</span>
+              </template>
+            </el-popover>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <div style="line-height: 44px; margin: 60px 0; color: #999">
+            <img
+              src="~@/assets/img/cus_m/nodata.png"
+              alt=""
+              style="display: block; width: 160px; height: 128px; margin: auto"
+            />
+            <span>暂无数据</span>
+          </div>
+        </template>
+      </el-table>
+      <el-pagination
+        layout="total,prev,pager,next,jumper"
+        background
+        :current-page="pageState.page_no"
+        @current-change="handleCurrentChange"
+        :page-size="pageState.pageSize"
+        :total="pageState.total"
+        style="text-align: end; margin-top: 20px"
+      >
+      </el-pagination>
+    </div>
+    <el-dialog
+      v-if="pageState.isEditShow"
+      v-model="pageState.isEditShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="pageState.isEditShow = false"
+      width="889px"
+      draggable
+      center
+    >
+      <template #header>
+        <div style="display: flex; align-items: center">
+          <span style="fontsize: 16px">问题编辑</span>
+        </div>
+      </template>
+
+      <div class="dialog-container">
+        <div class="input-item">
+          <el-input
+            type="textarea"
+            :rows="12"
+            placeholder="请输入提问内容"
+            v-model="pageState.currentEdit.text"
+            required
+            resize="none"
+          >
+          </el-input>
+        </div>
+        <div class="hint">
+          <span>注:请把问题描述控制在{{ pageState.maxNum }}个字以内</span>
+          <span
+            >超出字数:<span style="color: #c94040ff">{{
+              pageState.currentEdit.text.length - pageState.maxNum > 0
+                ? pageState.currentEdit.text.length - pageState.maxNum
+                : 0
+            }}</span
+            >字</span
+          >
+        </div>
+      </div>
+      <template #footer>
+        <div class="foot-container">
+          <el-button type="primary" @click="handleEditQuestion"
+            >确 定</el-button
+          >
+          <el-button @click="pageState.isEditShow = false">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog
+      draggable
+      :append-to-body="true"
+      :model-value="pageState.userInfoDialog"
+      width="500px"
+      title="提问者详情"
+      center
+    >
+      <template #header>
+        <div style="display: flex; align-items: center">
+          <span style="font-size: 16px">提问者详情</span>
+        </div>
+      </template>
+
+      <div>
+        <div class="detail-wrap">
+          <div class="main">
+            <table class="table-wrap" style="border: none !important">
+              <tbody>
+                <tr>
+                  <td class="table-item" style="width: 25%; text-align: right">
+                    提问者内容:
+                  </td>
+                  <td class="table-item" style="width: 75%; text-align: left">
+                    {{ pageState.userDetail.QuestionContent }}
+                  </td>
+                </tr>
+                <tr>
+                  <td class="table-item" style="width: 25%; text-align: right">
+                    提问者姓名:
+                  </td>
+                  <td class="table-item" style="width: 75%; text-align: left">
+                    {{ pageState.userDetail.RealName }}
+                  </td>
+                </tr>
+                <tr>
+                  <td class="table-item" style="width: 25%; text-align: right">
+                    公司名称:
+                  </td>
+                  <td class="table-item" style="width: 75%; text-align: left">
+                    {{ pageState.userDetail.CompanyName }}
+                  </td>
+                </tr>
+                <tr>
+                  <td class="table-item" style="width: 25%; text-align: right">
+                    客户状态:
+                  </td>
+                  <td class="table-item" style="width: 75%; text-align: left">
+                    {{ pageState.userDetail.CompanyStatus }}
+                  </td>
+                </tr>
+                <tr>
+                  <td class="table-item" style="width: 25%; text-align: right">
+                    所属销售:
+                  </td>
+                  <td class="table-item" style="width: 75%; text-align: left">
+                    {{ pageState.userDetail.SellerName }}
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="foot-container">
+          <el-button type="primary" @click="userInfoDialog = false"
+            >知道了</el-button
+          >
+        </div>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      draggable
+      :append-to-body="true"
+      :model-value="pageState.clickNumDialog"
+      width="840px"
+      title="点击量详情"
+      center
+    >
+      <template #header>
+        <div style="display: flex; align-items: center">
+          <span style="font-size: 16px">点击量详情</span>
+        </div>
+      </template>
+
+      <div>
+        <div>点击量:{{ pageState.clickNum }}</div>
+        <div>提问内容:{{ pageState.clickQuestionContent }}</div>
+        <div>
+          <el-table
+            :data="pageState.clickLogsData"
+            style="width: 100%; margin: 20px 0 30px"
+            border
+          >
+            <el-table-column prop="RealName" label="用户姓名" align="center">
+            </el-table-column>
+            <el-table-column prop="CompanyName" label="公司名称" align="center">
+            </el-table-column>
+            <el-table-column
+              prop="CompanyStatus"
+              label="客户状态"
+              align="center"
+            >
+            </el-table-column>
+            <el-table-column prop="ClickNum" label="点击次数" align="center">
+            </el-table-column>
+            <el-table-column prop="SourceAgent" label="点击来源" align="center">
+              <template #default="scope">
+                <span v-if="scope.row.SourceAgent == 1">小程序</span>
+                <span v-else>pc</span>
+              </template>
+            </el-table-column>
+            <el-table-column
+              prop="LastCreateTime"
+              label="最后一次点击时间"
+              align="center"
+              min-width="140"
+            >
+            </el-table-column>
+          </el-table>
+        </div>
+        <!-- 分页 -->
+        <div style="text-align: end; margin-bottom: 24px">
+          <el-pagination
+            small
+            layout="prev,pager,next,jumper"
+            :current-page="pageState.clickLogsCurrentIndex"
+            @current-change="clickLogsPageChange"
+            :page-size="pageState.clickLogsPageSize"
+            :total="pageState.clickLogsTotal"
+            class="pagination-wrap"
+          >
+          </el-pagination>
+        </div>
+      </div>
+    </el-dialog>
+    <!-- 问答详情弹窗 -->
+    <el-dialog
+      draggable
+      :append-to-body="true"
+      :model-value="pageState.questionInfoDialog"
+      width="800px"
+      title="提问详情"
+      center
+    >
+      <div class="question-detail-wrap" v-if="pageState.questionInfo">
+        <div class="left">
+          <div class="item">
+            <span class="lable">提问人</span>
+            <span>{{ pageState.questionInfo.RealName }}</span>
+          </div>
+          <div class="item">
+            <span class="lable">提问时间</span>
+            <span>{{ pageState.questionInfo.CreateTime }}</span>
+          </div>
+          <div class="item">
+            <span class="lable">提问内容</span>
+            <div style="padding: 10px; background: #ededed">
+              {{ pageState.questionInfo.QuestionContent }}
+            </div>
+          </div>
+        </div>
+        <div class="right">
+          <div style="margin-bottom: 20px">问答流程</div>
+          <el-timeline>
+            <el-timeline-item
+              :timestamp="item.CreateTime"
+              placement="bottom"
+              color="#409EFF"
+              v-for="item in pageState.questionInfo.ProcessList"
+              :key="item.CreateTime"
+            >
+              <p>{{ item.Remark }}</p>
+            </el-timeline-item>
+          </el-timeline>
+        </div>
+      </div>
+    </el-dialog>
+    <!-- 终止原因 -->
+    <el-dialog
+      draggable
+      :append-to-body="true"
+      :model-value="pageState.stopReasonDialog"
+      width="400px"
+      title="终止原因"
+      center
+    >
+      <div style="padding-bottom: 30px">
+        <p style="margin-bottom: 20px">{{ pageState.stopReason }}</p>
+        <div style="text-align: center">
+          <el-button type="primary" @click="pageState.stopReasonDialog = false"
+            >知道了</el-button
+          >
+        </div>
+      </div>
+    </el-dialog>
+    <!-- 转移回答者弹窗 -->
+    <el-dialog
+      draggable
+      :append-to-body="true"
+      :model-value="pageState.moveAnswerDialog"
+      width="550px"
+      title="问题转移并通知"
+      center
+    >
+      <div>
+        <div>
+          <span>选择研究员</span>
+          <el-cascader
+            style="width: 400px"
+            v-model="pageState.moveAnswerId"
+            :options="pageState.moveAnswerOpt"
+          ></el-cascader>
+        </div>
+        <div style="text-align: center; margin: 30px 0">
+          <el-button type="primary" @click="handleConfirmTrans">确定</el-button>
+          <el-button type="primary" plain @click="pageState.moveAnswerDialog = false"
+            >取消</el-button
+          >
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+<style scoped lang="scss">
+.question-wrap {
+  /*  height: calc(100vh - 120px); */
+  background-color: #fff;
+  box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+  border-radius: 4px;
+  padding: 30px 20px;
+  box-sizing: border-box;
+
+  .question-top {
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .question-table {
+    margin-top: 20px;
+  }
+
+  .dialog-container {
+    .hint {
+      display: flex;
+      justify-content: space-between;
+
+      span {
+        color: #999999ff;
+      }
+    }
+  }
+}
+
+.detail-wrap {
+  //max-height: 70vh;
+  //overflow-y: scroll;
+  display: flex;
+  justify-content: space-between;
+
+  .main {
+    width: 100%;
+
+    .table-wrap {
+      border: 0;
+      width: 100%;
+      // text-align: center;
+      border-top: 1px solid #dcdfe6;
+      border-left: 1px solid #dcdfe6;
+
+      .table-item {
+        border: none !important;
+        padding: 14px 10px;
+        border-right: 1px solid #dcdfe6;
+        border-bottom: 1px solid #dcdfe6;
+        position: relative;
+      }
+    }
+  }
+}
+
+.question-detail-wrap {
+  display: flex;
+  padding-bottom: 30px;
+  .left {
+    flex: 1;
+    .item {
+      display: flex;
+      margin-bottom: 20px;
+      .lable {
+        width: 80px;
+      }
+    }
+  }
+  .right {
+    flex: 1;
+    border-left: 1px solid #ededed;
+    height: 400px;
+    overflow-y: auto;
+    padding-left: 20px;
+  }
+}
+</style>

+ 1 - 1
src/views/interaction_manage/videoManage.vue

@@ -188,7 +188,7 @@ async function getPermissionList() {
       let obj = {}
       obj.value = item.ClassifyName
       obj.label = item.ClassifyName
-      obj.children = item.List.map(_item => {
+      obj.children = item.List?.map(_item => {
         return { value: _item.ChartPermissionID, label: _item.ChartPermissionName }
       })
       return obj

+ 1 - 1
src/views/interaction_manage/videoManageENAdd.vue

@@ -408,7 +408,7 @@ async function getClassify() {
             :ref="`overviewEditor`"
             :tag="'textarea'"
             :config="pageState.overviewConfig"
-            v-model="pageState.formData.overview"
+            v-model:value="pageState.formData.overview"
           >
           </froala>
         </div>

+ 215 - 0
src/views/interactive_manage/bulletChatList.vue

@@ -0,0 +1,215 @@
+<script setup>
+import { reactive, ref } from 'vue'
+import { videoInterface } from '@/api/api.js'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Search } from "@element-plus/icons-vue";
+
+function formatSeconds(e) {
+  let tem = parseInt(e)
+  let min = parseInt(tem / 60)
+  let sen = parseInt(tem % 60)
+  if (min > 1) {
+    return `${min}分${sen}秒`
+  } else {
+    return `${sen}秒`
+  }
+}
+
+const pageState = reactive({
+  source: '',
+  sourceOpt: [{ label: '视频社区', val: 1 }, { label: '线上路演', val: 2 }],
+  keyWord: '',
+
+  showDataLoading: false,
+  tableData: [],
+  page: 1,
+  pageSize: 15,
+  total: 0,
+  tableColums: [
+    {
+      label: '弹幕内容',
+      key: 'Content',
+      minwidthsty: '200px',
+      toolTip: true
+    },
+    {
+      label: '视频标题',
+      key: 'Title',
+      minwidthsty: '150px'
+    },
+    {
+      label: '所属模块',
+      key: 'Source',
+    },
+    {
+      label: '时间点',
+      key: 'Seconds',
+    },
+    {
+      label: '发送时间',
+      key: 'CreateTime',
+      minwidthsty: '120px'
+    },
+    {
+      label: '发送人',
+      key: 'UserName',
+    },
+    {
+      label: '弹幕来源',
+      key: 'SourceAgent',
+    },
+    {
+      label: '操作',
+      key: 'opt',
+    },
+  ]
+
+
+})
+
+async function getList() {
+  pageState.showDataLoading = true
+  const res = await videoInterface.bulletChatList({
+    PageSize: pageState.pageSize,
+    CurrentIndex: pageState.page,
+    Source: pageState.source,
+    Keywords: pageState.keyWord
+  })
+  pageState.showDataLoading = false
+  if (res.Ret === 200) {
+    pageState.total = res.Data.Paging.Totals
+    pageState.tableData = res.Data.List || []
+  }
+}
+getList()
+
+function search() {
+  pageState.page = 1
+  pageState.source = ''
+  getList()
+}
+
+function handlePageChange(e) {
+  pageState.page = e
+  getList()
+}
+
+function typeChange() {
+  pageState.keyWord = ''
+  pageState.page = 1
+  getList()
+}
+
+// 删除弹幕
+function handleDel(item) {
+  ElMessageBox.confirm('是否确认删除该条弹幕,删除后不可恢复!', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(() => {
+    videoInterface.bulletChatDel({ Id: item.Id }).then(res => {
+      if (res.Ret === 200) {
+        ElMessage.success('删除成功')
+        pageState.page = 1
+        pageState.source = ''
+        getList()
+      }
+    })
+  }).catch(() => {
+
+  });
+}
+
+</script>
+
+<template>
+  <div class="bullet-chat-page">
+    <div class="top-box">
+      <el-select
+        v-model="pageState.source"
+        clearable
+        placeholder="请选择所属模块"
+        @change="typeChange"
+      >
+        <el-option
+          v-for="item in pageState.sourceOpt"
+          :key="item.val"
+          :label="item.label"
+          :value="item.val"
+        >
+        </el-option>
+      </el-select>
+      <el-input
+        v-model="pageState.keyWord"
+        placeholder="请输入弹幕内容或视频标题"
+        @input="search"
+        clearable
+        style="width: 400px; float: right"
+        :prefix-icon="Search"
+      ></el-input>
+    </div>
+    <div class="main">
+      <el-table
+        ref="table"
+        border
+        :data="pageState.tableData"
+        v-loading="pageState.showDataLoading"
+        element-loading-text="加载中..."
+        style="margin-top: 20px"
+      >
+        <el-table-column
+          v-for="item in pageState.tableColums"
+          :key="item.label"
+          :label="item.label"
+          :width="item.widthsty"
+          :min-width="item.minwidthsty"
+          :show-overflow-tooltip="item.toolTip"
+          align="center"
+          :prop="item.key"
+        >
+          <template #default="{ row }">
+            <span v-if="item.key == 'Source'">{{
+              row.Source === 1 ? "视频社区" : "线上路演"
+            }}</span>
+            <span v-else-if="item.key === 'Seconds'">{{
+              formatSeconds(row.Seconds)
+            }}</span>
+            <span v-else-if="item.key === 'SourceAgent'">{{
+              row.SourceAgent == "1" ? "小程序" : "pc端"
+            }}</span>
+            <span
+              v-else-if="item.key === 'opt'"
+              style="color: #ff0000; cursor: pointer"
+              @click="handleDel(row)"
+              >删除</span
+            >
+            <span v-else>{{ row[item.key] || "——" }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div>
+        <el-pagination
+          layout="total,prev,pager,next"
+          background
+          :current-page="pageState.page"
+          @current-change="handlePageChange"
+          :page-size="pageState.pageSize"
+          :total="pageState.total"
+          style="float: right; margin-top: 20px"
+        >
+        </el-pagination>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.bullet-chat-page {
+  min-height: 70vh;
+  padding: 20px 30px;
+  background: #fff;
+  border: 1px solid #ececec;
+  border-radius: 4px;
+  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+}
+</style>

+ 556 - 0
src/views/interactive_manage/likelist.vue

@@ -0,0 +1,556 @@
+<script setup>
+import { reactive, ref } from 'vue'
+import { interactiveInterface } from '@/api/api.js';
+import mPage from '@/components/mPage.vue'
+
+const pageState = reactive({
+  page_no: 1,
+  totals: 0,
+  tableData: [],
+  tableColums: [
+    {
+      label: '报告标题',
+      key: 'Title',
+      minwidthsty: 150
+    },
+    {
+      label: '发布时间',
+      key: 'PublishTime',
+    },
+    {
+      label: '点赞数量',
+      key: 'LikeNum',
+      sortable: 'custom'
+    },
+    {
+      label: '最近一次点赞时间',
+      key: 'ModifyTime'
+    },
+  ],
+  sort_obj: {},
+
+  openDetailDia: false,
+  likeprops: {
+    title: '点赞详情',
+    like_num: 0
+  },
+  likeList: [],
+  like_page_no: 1,
+  like_totals: 0,
+  likeTableColums: [
+    {
+      label: '用户姓名',
+      key: 'RealName',
+    },
+    {
+      label: '公司名称',
+      key: 'CompanyName',
+    },
+    {
+      label: '客户状态',
+      key: 'CompanyStatus',
+    },
+    {
+      label: '点赞来源',
+      key: 'SourceAgent',
+    },
+    {
+      label: '点赞时间',
+      key: 'ModifyTime',
+      minwidthsty: 200
+    },
+  ],
+
+  /*  */
+  showDataLoading: false,
+  default_datatype: 1,
+  tabsOptions: [
+    { label: '报告点赞', key: 1 },
+    { label: '问答点赞', key: 2 },
+    { label: '视频点赞', key: 4 },
+    { label: '问答吐槽', key: 3 },
+    { label: '视频吐槽', key: 5 },
+  ],
+  subTabsOptions: [
+    { label: '线上路演', key: 3 },
+    { label: '视频社区', key: 2 }
+  ],
+  default_subtype: 3,//3 路演视频 2视频社区
+})
+
+/* 列表 */
+async function getTableList() {
+  pageState.showDataLoading = true;
+  const { type, param } = pageState.sort_obj;
+
+  let params = {
+    SortParam: param || '',
+    SortType: type || '',
+    PageSize: 10,
+    CurrentIndex: pageState.page_no
+  }
+
+  if ([2, 4].includes(pageState.default_datatype)) {
+    params.DataType = 'like'
+  }
+  if ([3, 5].includes(pageState.default_datatype)) {
+    params.DataType = 'tease'
+  }
+  if ([2, 3].includes(pageState.default_datatype)) {
+    params.Source = 1
+  }
+  if ([4, 5].includes(pageState.default_datatype)) {
+    params.Source = pageState.default_subtype
+  }
+
+
+  const { Ret, Data } = pageState.default_datatype === 1
+    ? await interactiveInterface.likelist(params)
+    : await interactiveInterface.questionLikeList(params)
+
+  if (Ret !== 200) return
+
+  const { List, Paging } = Data;
+  pageState.tableData = List || [];
+  pageState.totals = Paging.Totals;
+
+  pageState.showDataLoading = false;
+}
+
+function likePageChange(page) {
+  pageState.page_no = page;
+  getTableList();
+}
+
+function likeDetailPageChange(page) {
+  pageState.like_page_no = page;
+  getDetailList()
+}
+
+/* 获取点赞/吐槽详情列表 */
+async function getDetailList() {
+
+  const { ReportId, ReportChapterId, CommunityQuestionId } = pageState.detailParams;
+
+  const { Ret, Data } = pageState.default_datatype === 1
+    ? await interactiveInterface.likeDetail({
+      PageSize: 10,
+      CurrentIndex: pageState.like_page_no,
+      ReportId: ReportId || '',
+      ReportChapterId: ReportChapterId || ''
+    })
+    : await interactiveInterface.questionLikeDetail({
+      PageSize: 10,
+      CurrentIndex: pageState.like_page_no,
+      CommunityQuestionId: CommunityQuestionId || '',
+      DataType: [2, 4].includes(pageState.default_datatype) ? 'like' : 'tease',
+      Source: [2, 3].includes(pageState.default_datatype) ? 1 : pageState.default_subtype
+    })
+
+  if (Ret !== 200) return
+
+  const { List, Paging } = Data;
+
+  pageState.likeList = List || [];
+  pageState.like_totals = Paging.Totals;
+}
+getTableList()
+
+/* 查看详情 */
+function toDetail({ ReportId, ReportChapterId, LikeNum, Title, CommunityQuestionId, Num, QuestionContent }) {
+  pageState.detailParams = { ReportId, ReportChapterId, CommunityQuestionId };
+  getDetailList()
+
+  pageState.likeprops = {
+    title: [1, 2, 4].includes(pageState.default_datatype) ? '点赞详情' : '吐槽详情',
+    like_num: LikeNum || Num,
+    sub_title: Title || QuestionContent
+  }
+  pageState.openDetailDia = true;
+}
+
+function closeDialog() {
+  pageState.openDetailDia = false;
+  pageState.like_page_no = 1;
+}
+
+/* 排序变化时 */
+function sortChangeHandle({ prop, order }) {
+  pageState.page_no = 1;
+
+  pageState.sort_obj = {
+    type: order === 'ascending' ? 'asc' : order === 'descending' ? 'desc' : '',
+    param: prop
+  }
+  getTableList();
+}
+
+/* 改变表格类型  */
+const table = ref(null)
+function changeTabHandle({ key }) {
+  pageState.default_datatype = key;
+  pageState.default_subtype = pageState.subTabsOptions[0].key
+  pageState.page_no = 1;
+  pageState.sort_obj = {};
+  table.value.clearSort();
+  setColumns();
+  getTableList();
+}
+
+// 二级类型切换
+function changeSubTabHandle(item) {
+  pageState.default_subtype = item.key
+  pageState.page_no = 1;
+  pageState.sort_obj = {};
+  table.value.clearSort();
+  setColumns();
+  getTableList();
+}
+
+function setColumns() {
+  let tag = [1, 2, 4].includes(pageState.default_datatype) ? '点赞' : '吐槽';
+  if (pageState.default_datatype === 1) {
+    pageState.tableColums = [
+      {
+        label: '报告标题',
+        key: 'Title',
+        minwidthsty: 150
+      },
+      {
+        label: '发布时间',
+        key: 'PublishTime',
+      },
+      {
+        label: `${tag}数量`,
+        key: 'LikeNum',
+        sortable: 'custom'
+      },
+      {
+        label: `最近一次${tag}时间`,
+        key: 'ModifyTime'
+      },
+    ]
+
+    pageState.likeTableColums = [
+      {
+        label: '用户姓名',
+        key: 'RealName',
+      },
+      {
+        label: '公司名称',
+        key: 'CompanyName',
+      },
+      {
+        label: '客户状态',
+        key: 'CompanyStatus',
+      },
+      {
+        label: '点赞来源',
+        key: 'SourceAgent',
+      },
+      {
+        label: '点赞时间',
+        key: 'ModifyTime',
+        minwidthsty: 200
+      }
+    ]
+  } else if ([2, 3].includes(pageState.default_datatype)) {//问答
+    pageState.tableColums = [
+      {
+        label: '提问内容',
+        key: 'QuestionContent',
+        minwidthsty: 150
+      },
+      {
+        label: '提问时间',
+        key: 'CreateTime',
+      },
+      {
+        label: `${tag}数量`,
+        key: 'Num',
+        sortable: 'custom'
+      },
+      {
+        label: `${tag}时间`,
+        key: 'ModifyTime'
+      },
+    ]
+
+    pageState.likeTableColums = [
+      {
+        label: '用户姓名',
+        key: 'RealName',
+      },
+      {
+        label: '公司名称',
+        key: 'CompanyName',
+      },
+      {
+        label: '客户状态',
+        key: 'CompanyFiccStatus',
+      },
+      {
+        label: `${tag}来源`,
+        key: 'SourceAgent',
+      },
+      {
+        label: `${tag}时间`,
+        key: 'ModifyTime',
+        minwidthsty: 200
+      },
+    ]
+  } else if ([4, 5].includes(pageState.default_datatype)) {//视频
+    pageState.tableColums = [
+      {
+        label: '视频标题',
+        key: 'QuestionContent',
+        minwidthsty: 150
+      },
+      {
+        label: `所属${pageState.default_subtype == 2 ? '标签' : '品种'}`,
+        key: 'VarietyClassifyTag',
+      },
+      {
+        label: '发布时间',
+        key: 'CreateTime',
+      },
+      {
+        label: `${tag}数量`,
+        key: 'Num',
+        sortable: 'custom'
+      },
+      {
+        label: `${tag}时间`,
+        key: 'ModifyTime'
+      },
+    ]
+
+    pageState.likeTableColums = [
+      {
+        label: '用户姓名',
+        key: 'RealName',
+      },
+      {
+        label: '公司名称',
+        key: 'CompanyName',
+      },
+      {
+        label: '客户状态',
+        key: 'CompanyFiccStatus',
+      },
+      {
+        label: `${tag}来源`,
+        key: 'SourceAgent',
+      },
+      {
+        label: `${tag}时间`,
+        key: 'ModifyTime',
+        minwidthsty: 200
+      },
+    ]
+  }
+}
+
+
+
+
+</script>
+
+<template>
+  <div class="like-container">
+    <div class="main">
+      <ul class="tabs-cont">
+        <li
+          :class="['tab-item', { act: pageState.default_datatype === tab.key }]"
+          v-for="tab in pageState.tabsOptions"
+          :key="tab.key"
+          @click="changeTabHandle(tab)"
+        >
+          {{ tab.label }}
+        </li>
+      </ul>
+      <div v-if="[4, 5].includes(pageState.default_datatype)" class="sub-tabs">
+        <span
+          :class="[
+            'sub-tab-item',
+            pageState.default_subtype == item.key && 'active',
+          ]"
+          v-for="item in pageState.subTabsOptions"
+          :key="item.key"
+          @click="changeSubTabHandle(item)"
+          >{{ item.label }}</span
+        >
+      </div>
+      <el-table
+        ref="table"
+        border
+        :data="pageState.tableData"
+        v-loading="pageState.showDataLoading"
+        element-loading-text="加载中..."
+        style="margin-top: 20px"
+        @sort-change="sortChangeHandle"
+      >
+        <el-table-column
+          v-for="item in pageState.tableColums"
+          :key="item.label"
+          :label="item.label"
+          :width="item.widthsty"
+          :min-width="item.minwidthsty"
+          :sortable="item.sortable"
+          align="center"
+          :prop="item.key"
+        >
+          <template #default="{ row }">
+            <span
+              v-if="['LikeNum', 'Num'].includes(item.key)"
+              class="editsty"
+              @click="toDetail(row)"
+              >{{ row[item.key] || "——" }}</span
+            >
+
+            <span v-else-if="item.key === 'Title'">
+              {{ row.ClassifyNameFirst ? `${row.ClassifyNameFirst}/` : "" }}
+              {{ row[item.key] }}
+            </span>
+
+            <span v-else>{{ row[item.key] || "——" }}</span>
+          </template>
+        </el-table-column>
+        <template #empty>
+          <div style="padding: 100px 0">
+            <img
+              src="~@/assets/img/cus_m/nodata.png"
+              alt=""
+              style="
+                display: block;
+                width: 128px;
+                height: 128px;
+                margin: 0 auto;
+              "
+            />
+            <span>暂无数据</span>
+          </div>
+        </template>
+      </el-table>
+      <div style="height: 40px; margin-top: 10px; padding: 0 20px">
+        <m-page
+          :page_no="pageState.page_no"
+          :pageSize="10"
+          :total="pageState.totals"
+          @handleCurrentChange="likePageChange"
+        />
+      </div>
+    </div>
+
+    <!-- 详情弹窗 -->
+    <el-dialog
+      :model-value="pageState.openDetailDia"
+      width="700px"
+      :title="pageState.likeprops.title"
+      @close="closeDialog"
+    >
+      <div class="dialog-cont">
+        <h4 style="margin-bottom: 10px">{{ pageState.likeprops.sub_title }}</h4>
+        <span>点赞数量:{{ pageState.likeprops.like_num }} </span>
+        <el-table
+          border
+          :data="pageState.likeList"
+          style="margin-top: 20px"
+          max-height="350"
+        >
+          <el-table-column
+            v-for="item in pageState.likeTableColums"
+            :key="item.label"
+            :label="item.label"
+            :width="item.widthsty"
+            :min-width="item.minwidthsty"
+            align="center"
+            :prop="item.key"
+          >
+            <template #default="{ row }">
+              <span v-if="item.key === 'SourceAgent'">{{
+                row[item.key] === 1 ? "小程序" : "PC"
+              }}</span>
+              <span v-else>{{ row[item.key] || "——" }}</span>
+            </template>
+          </el-table-column>
+          <template #empty>
+            <div style="padding: 20px 0">
+              <img
+                src="~@/assets/img/cus_m/nodata.png"
+                alt=""
+                style="
+                  display: block;
+                  width: 128px;
+                  height: 128px;
+                  margin: 0 auto;
+                "
+              />
+              <span>暂无数据</span>
+            </div>
+          </template>
+        </el-table>
+        <div style="height: 40px; margin-top: 10px; padding: 0 20px">
+          <m-page
+            :page_no="pageState.like_page_no"
+            :pageSize="10"
+            :total="pageState.like_totals"
+            @handleCurrentChange="likeDetailPageChange"
+          />
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang='scss' scoped>
+* {
+  box-sizing: border-box;
+}
+.like-container {
+  .main {
+    min-height: calc(100vh - 120px);
+    padding: 20px 30px;
+    background: #fff;
+    border: 1px solid #ececec;
+    border-radius: 4px;
+    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
+
+    .tabs-cont {
+      display: flex;
+      align-items: center;
+      font-size: 15px;
+      .tab-item {
+        margin-right: 30px;
+        // border-bottom: 3px solid transparent;
+        min-width: 100px;
+        text-align: center;
+        padding: 10px 0;
+        cursor: pointer;
+        color: #606266;
+        border: 1px solid #dcdfe6;
+        border-radius: 4px;
+        &.act {
+          background: #409eff;
+          color: #fff;
+        }
+      }
+    }
+    .sub-tabs {
+      margin-top: 30px;
+      .sub-tab-item {
+        font-size: 16px;
+        color: #999;
+        display: inline-block;
+        margin-right: 30px;
+        padding-bottom: 5px;
+        cursor: pointer;
+        &.active {
+          border-bottom: 4px solid #58aaff;
+          color: #333;
+        }
+      }
+    }
+  }
+}
+</style>

+ 420 - 0
src/views/interactive_manage/messageManage.vue

@@ -0,0 +1,420 @@
+<script setup>
+import { interactiveInterface } from '@/api/api.js';
+import mPage from '@/components/mPage.vue'
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { nextTick, reactive, ref } from 'vue';
+import { Search } from "@element-plus/icons-vue";
+
+const pageState = reactive({
+  searchParams: {
+    CreateDate: '',
+    ProductType: '',
+    KeyWord: '',
+    CurrentIndex: 1,
+    PageSize: 10
+  },
+  total: 0,
+  dataList: [],
+  boardArray: [],
+  // 弹窗标题
+  dialogTitle: '',
+  //---------------留言详情弹窗
+  // 是否显示
+  isDetailShow: false,
+  detailForm: {},
+  //  ----------------留言回复弹窗
+  // 是否显示
+  isReplayShow: false,
+  replayForm: {
+    CommentId: '',
+    content: ''
+  },
+  expandRow: {}
+})
+
+
+// 切换页码
+function pageChange(page) {
+  pageState.searchParams.CurrentIndex = page
+  getMessageList()
+}
+// 搜索
+function search() {
+  pageState.searchParams.CurrentIndex = 1
+  getMessageList()
+}
+// 获取留言列表
+const messageTable = ref(null)
+function getMessageList(option = '') {
+  // option 操作 expand展开对应行
+  interactiveInterface.getAllBoardMessage(pageState.searchParams).then(res => {
+    pageState.dataList = res.Data.List || []
+    pageState.total = res.Data.Paging.Totals || 0
+    if (option == 'expand') {
+      nextTick(() => {
+        messageTable.value?.toggleRowExpansion(pageState.expandRow, true)
+      })
+    }
+  })
+}
+//获取板块列表
+function getProductionList() {
+  interactiveInterface.getProductList().then(res => {
+    pageState.boardArray = [{ ProductName: '全部', ProductType: '' }, ...res.Data]
+  })
+}
+// // 刷新列表
+// refreshList(){
+//   this.getMessageList()
+// },
+// 置顶/取消置顶
+function topOption(row) {
+  interactiveInterface.messageTop({ CommentId: row.CommentId }).then(res => {
+    row.IsTop = res.Data.IsTop
+    ElMessage.success('操作成功')
+  })
+}
+// 留言详情
+const replayForm = ref(null)
+function messageDetail(row) {
+  // console.log(row);
+  pageState.dialogTitle = '留言人详情'
+  pageState.detailForm = row
+  pageState.isDetailShow = true
+}
+// 回复
+function replayMessage(row) {
+  pageState.dialogTitle = '留言回复'
+  pageState.replayForm = {
+    CommentId: row.CommentId,
+    content: ''
+  }
+  pageState.expandRow = row;
+  replayForm.value?.clearValidate()
+  pageState.isReplayShow = true
+}
+// 提交
+function submit() {
+  replayForm.value?.validate((valid) => {
+    if (valid) {
+      interactiveInterface.messageReplay(pageState.replayForm).then(res => {
+        if (res.Ret == 200) {
+          ElMessage.success('回复成功')
+          pageState.isReplayShow = false
+          getMessageList('expand')
+        }
+      })
+    }
+  })
+}
+// 删除操作
+function deleteOption(row) {
+  // console.log(row);
+  let text = row.ReplyCommentId ? '回复' : '留言'
+  let productType = row.ProductType ? row.ProductType
+    : row.ReplyCommentId ? 1 : ''
+  ElMessageBox.confirm(text + '删除后不可恢复,确定删除吗?', {
+    confirmText: '确定',
+    cancelText: '取消',
+    customClass: 'warning-outline-icon',
+    iconClass: 'el-icon-warning-outline'
+  }).then(res => {
+    interactiveInterface.messageDelete({ CommentId: row.CommentId, ProductType: productType }).then(res => {
+      if (res.Ret == 200) {
+        ElMessage.success('删除成功')
+        getMessageList()
+      }
+    })
+  }).catch(() => { })
+}
+// 合并单元格
+function spanMethod({ row, rowIndex, column, columnIndex }) {
+  if ((!row.Title) && (!row.ProductType)) {
+    if (columnIndex == 3) {
+      return {
+        rowspan: 1,
+        colspan: 2
+      }
+    } else if (columnIndex == 4) {
+      return {
+        rowspan: 1,
+        colspan: 0
+      }
+    }
+  }
+}
+
+
+
+
+function initPage() {
+  getMessageList()
+  getProductionList()
+}
+initPage()
+
+</script>
+
+<template>
+  <div id="messageManage-container">
+    <div class="search-zone">
+      <div class="search-zone-left">
+        <el-date-picker
+          v-model="pageState.searchParams.CreateDate"
+          placeholder="留言时间"
+          @change="search"
+          value-format="yyyy-MM-dd"
+          style="width: 248px; margin-right: 48px"
+        ></el-date-picker>
+        <el-select
+          v-model="pageState.searchParams.ProductType"
+          placeholder="板块"
+          @change="search"
+        >
+          <el-option
+            :label="item.ProductName"
+            :value="item.ProductType"
+            v-for="item in pageState.boardArray"
+            :key="item.value"
+          ></el-option>
+        </el-select>
+      </div>
+      <div class="search-zone-right">
+        <el-input
+          v-model="pageState.searchParams.KeyWord"
+          placeholder="留言内容"
+          @input="search"
+          style="width: 230px"
+          :prefix-icon="Search"
+        ></el-input>
+      </div>
+    </div>
+    <el-table
+      :data="pageState.dataList"
+      border
+      row-key="CommentId"
+      :tree-props="{ children: 'ReplyList' }"
+      :span-method="spanMethod"
+      ref="messageTable"
+    >
+      <el-table-column label="留言内容" prop="Content">
+        <template #default="{ row }">
+          {{ row.Content }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="时间"
+        prop="CreateTime"
+        show-overflow-tooltip
+        align="center"
+      ></el-table-column>
+      <el-table-column label="留言人" prop="RealName" align="center">
+        <template #default="{ row }">
+          <span
+            style="color: #409eff; cursor: pointer"
+            @click="messageDetail(row)"
+            v-if="row.RealName"
+            >{{ row.RealName }}</span
+          >
+          <span v-else>{{ row.AdminName || "--" }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="来源"
+        prop="Title"
+        show-overflow-tooltip
+        align="center"
+      >
+        <template #default="{ row }">
+          {{ row.Title }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="板块"
+        prop="ProductType"
+        show-overflow-tooltip
+        align="center"
+      >
+        <template #default="{ row }">
+          <span>{{
+            pageState.boardArray[row.ProductType]
+              ? pageState.boardArray[row.ProductType].ProductName
+              : ""
+          }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center">
+        <template #default="{ row }">
+          <div class="table-option">
+            <template v-if="row.ProductType == 1">
+              <span @click="topOption(row)" v-if="row.IsTop == 0">置顶</span>
+              <span @click="topOption(row)" v-else>取消置顶</span>
+            </template>
+            <span @click="replayMessage(row)" v-if="row.ProductType == 1"
+              >回复</span
+            >
+            <span @click="deleteOption(row)" style="color: #b72e18">删除</span>
+          </div>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <div style="padding: 100px 0">
+          <img
+            src="~@/assets/img/cus_m/nodata.png"
+            style="display: block; width: 128px; height: 128px; margin: 0 auto"
+          />
+          <span>暂无数据</span>
+        </div>
+      </template>
+    </el-table>
+    <div style="height: 40px; margin-top: 10px; padding: 0 20px">
+      <m-page
+        :page_no="pageState.searchParams.CurrentIndex"
+        :pageSize="10"
+        :total="pageState.total"
+        @handleCurrentChange="pageChange"
+      />
+    </div>
+    <!-- 留言详情弹窗 -->
+    <el-dialog
+      v-model="pageState.isDetailShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="pageState.isDetailShow = false"
+      width="568px"
+      draggable
+      center
+      :title="pageState.dialogTitle"
+    >
+      <el-form :model="pageState.detailForm" label-width="105px">
+        <el-form-item label="留言内容:" id="content-form-item">
+          <span>{{ pageState.detailForm.Content }}</span>
+        </el-form-item>
+        <el-form-item label="留言人姓名:">
+          {{ pageState.detailForm.RealName }}
+        </el-form-item>
+        <el-form-item label="公司名称:">
+          {{ pageState.detailForm.CompanyName || "--" }}
+        </el-form-item>
+        <el-form-item label="客户状态:">
+          {{ pageState.detailForm.CompanyStatus || "--" }}
+        </el-form-item>
+        <el-form-item label="所属销售:">
+          {{ pageState.detailForm.SellerName || "--" }}
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div>
+          <el-button
+            @click="pageState.isDetailShow = false"
+            type="primary"
+            style="height: 40px; width: 224px"
+            >知道了</el-button
+          >
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 留言回复弹窗 -->
+    <el-dialog
+      v-model="pageState.isReplayShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="pageState.isReplayShow = false"
+      width="568px"
+      draggable
+      center
+      :title="pageState.dialogTitle"
+    >
+      <el-form :model="pageState.replayForm" ref="replayForm">
+        <el-form-item
+          :rules="[
+            { required: true, message: '回复内容不能为空', trigger: 'blur' },
+          ]"
+          prop="content"
+        >
+          <el-input
+            v-model="pageState.replayForm.content"
+            placeholder="请输入回复"
+            type="textarea"
+            resize="none"
+            :show-word-limit="true"
+            maxlength="150"
+            rows="10"
+          ></el-input>
+        </el-form-item>
+        <span
+          style="
+            font-size: 16px;
+            font-family: PingFang SC-Regular, PingFang SC;
+            font-weight: 400;
+            color: #999999;
+          "
+        >
+          注:至多输入150字,超出部分不予显示
+        </span>
+      </el-form>
+      <template #footer>
+        <div style="margin-top: ;20px">
+          <el-button
+            @click="submit"
+            type="primary"
+            style="width: 132px; height: 40px"
+            >保存</el-button
+          >
+          <el-button
+            @click="pageState.isReplayShow = false"
+            style="width: 132px; height: 40px"
+            >取消</el-button
+          >
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+#messageManage-container {
+  background-color: white;
+  padding: 18px 20px;
+  box-sizing: border-box;
+  min-height: calc(100vh - 110px);
+  border: 1px solid #ececec;
+  .search-zone {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 21px;
+  }
+  .table-option {
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+    span {
+      cursor: pointer;
+      color: #409eff;
+    }
+  }
+}
+</style>
+<style lang="scss">
+#content-form-item {
+  .el-form-item__label,
+  .el-form-item__content {
+    line-height: normal;
+  }
+}
+#messageManage-container {
+  .el-table__indent {
+    padding-left: 0 !important;
+  }
+  .el-table__placeholder {
+    width: 0;
+  }
+}
+
+.warning-outline-icon {
+  .el-icon-warning-outline {
+    color: #e6a23c;
+  }
+}
+</style>

+ 583 - 0
src/views/report_manage/tagLib.vue

@@ -0,0 +1,583 @@
+<script setup>
+import taglibApi from "@/api/modules/taglibApi.js";
+import { departInterence, customInterence } from '@/api/api.js';
+import { reactive, ref } from 'vue'
+import { ElMessage } from "element-plus";
+import { Search } from "@element-plus/icons-vue";
+
+const pageState = reactive({
+  tableColumn: [],//表格列,值为tagTableColumn或classifyTableColumn
+  tableData: [],//表格数据
+  tableLoading: false,//表格的loading
+  permissionOptions: [],//品种筛选框数据
+  tagForm: {},//新增/编辑 标签,标签分类数据
+  tableName: '',//当前展示的表格名
+  page_no: 1,//分页相关
+  pageSize: 10,
+  total: 2,
+  searchInput: '',//标签名称筛选
+  tagTreeOptions: [],//标签分类筛选
+  selectTagTree: [],
+  searchClassify: 0,//标签分类筛选的值
+  tagDialogShow: false,//弹窗显示/隐藏相关
+  classifyDialogShow: false,
+  tagTableColumn: [
+    {
+      label: "标签名称",
+      key: "tag_name",
+    },
+    {
+      label: "标签分类",
+      key: "variety_classify_name",
+    },
+    {
+      label: "关联品种",
+      key: "RelatVariety",
+    },
+    {
+      label: "更新时间",
+      key: "modify_time",
+    },
+    {
+      label: "状态",
+      key: "state",
+    }
+  ],
+  classifyTableColumn: [
+    {
+      label: "标签分类名称",
+      key: "classify_name",
+    },
+    {
+      label: "更新时间",
+      key: "modify_time",
+    },
+    {
+      label: "状态",
+      key: "state",
+    }
+  ]
+})
+
+
+//新增 标签/标签分类
+function addTag() {
+  pageState.tagForm = {}
+  //打开对应弹窗
+  handleDialog('open')
+}
+//编辑 标签/标签分类
+function editTag(row) {
+  pageState.tagForm = row
+  handleDialog('open')
+}
+//处理 新增/编辑标签,标签分类
+async function handleChangeTag() {
+  const { status, hintText } = checkTagForm()
+  if (!status) {
+    ElMessage.warning(hintText)
+    return
+  }
+  //调接口
+  let res = null
+  if (pageState.tableName === 'tag') {
+    res = await changeTag()
+  } else {
+    res = await changeClassify()
+  }
+  if (res.Ret !== 200) {
+    //ElMessage.error(res.ErrMsg)
+    return
+  } else {
+    let isEdit = pageState.tableName === 'tag' && pageState.tagForm.variety_tag_id || pageState.tableName === 'classify' && pageState.tagForm.variety_classify_id
+    ElMessage.success(`${isEdit ? '编辑' : '新增'}成功`)
+  }
+  //then
+  pageState.tagForm = {}
+  handleDialog('close')
+  //如果更新的是标签分类,则需要重新获取一次标签树
+  getTagTree()
+  getAllClassifyList()
+  pageState.page_no = 1
+  getTableList()
+}
+function changeTag() {
+  const { variety_tag_id, variety_classify_id, tag_name, chart_permission_id } = pageState.tagForm
+  const v_c_id = Array.isArray(variety_classify_id) ? variety_classify_id[0] : variety_classify_id
+  const c_p_id = Array.isArray(chart_permission_id) ? chart_permission_id[chart_permission_id.length - 1] : chart_permission_id
+  return taglibApi.modifyTag({
+    variety_tag_id: variety_tag_id ? variety_tag_id : 0,
+    variety_classify_id: v_c_id,
+    tag_name,
+    chart_permission_id: c_p_id
+  })
+}
+function changeClassify() {
+  const { variety_classify_id, classify_name } = pageState.tagForm
+  return taglibApi.modifyClassify({
+    variety_classify_id: variety_classify_id ? variety_classify_id : 0,
+    classify_name
+  })
+}
+//处理 启用/禁用标签,标签分类
+async function handleChangeTagStatus(row) {
+  //调接口
+  let res = null
+  if (pageState.tableName === 'tag') {
+    res = await changeTagStatus(row)
+  } else {
+    res = await changeClassifyStatus(row)
+  }
+  if (res.Ret !== 200) {
+    //ElMessage.error(res.ErrMsg)
+    return
+  } else {
+    ElMessage.success('操作成功')
+  }
+  //then
+  getTableList()
+}
+function changeTagStatus({ variety_tag_id, state }) {
+  return taglibApi.setTagStatus({
+    variety_tag_id,
+    state: state === 0 ? 1 : 0
+  })
+}
+function changeClassifyStatus({ variety_classify_id, state }) {
+  return taglibApi.setClassifyState({
+    variety_classify_id,
+    state: state === 0 ? 1 : 0
+  })
+}
+//切换表格
+function changTable(tableName) {
+  pageState.tableName = tableName
+  pageState.tableColumn = pageState.tableName === 'tag' ? pageState.tagTableColumn : pageState.classifyTableColumn
+  //重置分页
+  pageState.page_no = 1
+  getTableList()
+}
+function handleSearchChange(type) {
+  if (type === 'input') {
+    pageState.searchClassify = 0
+  } else {
+    pageState.searchInput = ''
+  }
+  pageState.page_no = 1
+  getTagList()
+}
+//获取表格数据
+function getTableList() {
+  pageState.tableLoading = true
+  if (pageState.tableName === 'tag') {
+    getTagList()
+  } else {
+    getClassifyList()
+  }
+}
+async function getTagList() {
+  let searchObj = {}
+  if (pageState.searchClassify !== 0) {
+    searchObj.ClassifyId = pageState.searchClassify[0]
+  }
+  const res = await taglibApi.getTagList({
+    ...{
+      PageSize: pageState.pageSize,
+      CurrentIndex: pageState.page_no,
+      Keywords: pageState.searchInput
+    }, ...searchObj
+  })
+  if (res.Ret === 200) {
+    const { Paging, List } = res.Data
+    pageState.page_no = Paging.CurrentIndex
+    pageState.total = Paging.Totals
+    pageState.tableData = List
+    pageState.tableLoading = false
+  }
+}
+async function getClassifyList() {
+  const res = await taglibApi.getClassifyList({
+    PageSize: pageState.pageSize,
+    CurrentIndex: pageState.page_no,
+  })
+  if (res.Ret === 200) {
+    const { Paging, List } = res.Data
+    pageState.page_no = Paging.CurrentIndex
+    pageState.total = Paging.Totals
+    pageState.tableData = List
+    pageState.tableLoading = false
+  }
+}
+//获取品种数据
+async function getPermissionList() {
+  const res = await customInterence.authList({ CompanyType: 'ficc' })
+  if (res.Ret === 200) {
+    let arr = res.Data.List || []
+    pageState.permissionOptions = arr.map(item => {
+      let obj = {}
+      obj.value = item.ClassifyName
+      obj.label = item.ClassifyName
+      obj.children = item.Items.map(_item => {
+        return { value: _item.ChartPermissionId, label: _item.PermissionName }
+      })
+      return obj
+    }).filter(item => item.value != '市场策略');
+  }
+}
+//获取标签树数据
+async function getTagTree() {
+  const res = await departInterence.getTagTree()
+  if (res.Ret === 200) {
+    let arr = res.Data || []
+    pageState.tagTreeOptions = arr.map(item => {
+      let obj = {}
+      obj.value = item.classify_id
+      obj.label = item.classify_name
+      return obj
+    })
+  }
+}
+//获取标签列表数据,PageSize:100,作为标签分类筛选框的数据
+async function getAllClassifyList() {
+  const res = await taglibApi.getClassifyList({
+    PageSize: 100,
+    CurrentIndex: 1,
+  })
+  if (res.Ret === 200) {
+    const { List } = res.Data
+    pageState.selectTagTree = List.map(item => {
+      let obj = {}
+      obj.value = item.variety_classify_id
+      obj.label = item.classify_name
+      return obj
+    })
+  }
+}
+//分页
+function handleCurrentChange(page) {
+  pageState.page_no = page
+  getTableList()
+}
+//控制弹窗
+function handleDialog(type) {
+  if (pageState.tableName === 'tag') {
+    pageState.tagDialogShow = type === 'open' ? true : false
+  } else {
+    pageState.classifyDialogShow = type === 'open' ? true : false
+  }
+  if (type === 'close') {
+    pageState.tagForm = {}
+  }
+}
+//校验 标签,标签分类表单
+function checkTagForm() {
+  const tagCheck = [
+    { key: 'tag_name', name: '标签名称', handle: '输入' },
+    { key: 'variety_classify_id', name: '标签分类', handle: '选择' },
+    { key: 'chart_permission_id', name: '关联品种', handle: '选择' }
+  ]
+  const classifyCheck = [{ key: 'classify_name', name: '标签分类', handle: '输入' }]
+  const checkArr = pageState.tableName === 'tag' ? tagCheck : classifyCheck
+  for (let i of checkArr) {
+    if (!pageState.tagForm[i.key]) {
+      return { status: false, hintText: '请' + i.handle + i.name }
+    }
+  }
+  return { status: true, hintText: '' }
+}
+
+function initPage() {
+  //获取标签树
+  getTagTree()
+  getAllClassifyList()
+  //获取品种列表
+  getPermissionList()
+  //默认显示标签列表
+  changTable('tag')
+}
+initPage()
+
+
+</script>
+
+<template>
+  <div class="taglib-page-wrap">
+    <div class="top-wrap">
+      <div class="switch-tab">
+        <div
+          :class="['tab-item',pageState.tableName === 'tag'?'active':'']"
+          @click="changTable('tag')"
+        >
+          标签列表
+        </div>
+        <div
+          class="tab-item"
+          :class="[pageState.tableName === 'classify'?'active':'' ]"
+          @click="changTable('classify')"
+        >
+          标签分类列表
+        </div>
+      </div>
+      <div class="tool-wrap">
+        <template v-if="pageState.tableName === 'classify'">
+          <el-button type="primary" size="medium" @click="addTag"
+            >添加标签分类</el-button
+          >
+        </template>
+        <template v-else>
+          <el-button type="primary" size="medium" @click="addTag"
+            >添加标签</el-button
+          >
+          <el-cascader
+            style="width: 200px; margin: 0 10px"
+            placeholder="请选择标签分类"
+            :options="pageState.selectTagTree"
+            v-model="pageState.searchClassify"
+            @change="handleSearchChange('select')"
+            clearable
+          ></el-cascader>
+          <el-input
+            style="width: 210px"
+            placeholder="标签名称"
+            :prefix-icon="Search"
+            clearable
+            v-model="pageState.searchInput"
+            @input="handleSearchChange('input')"
+          ></el-input>
+        </template>
+      </div>
+    </div>
+    <div class="tag-table">
+      <el-table
+        :data="pageState.tableData"
+        border
+        v-loading="pageState.tableLoading"
+      >
+        <el-table-column
+          v-for="item in pageState.tableColumn"
+          :key="item.key"
+          :prop="item.key"
+          :label="item.label"
+          align="center"
+        >
+          <template #default="{ row }">
+            <span
+              v-if="item.key === 'state'"
+              :style="{
+                color: row.state === 1 ? '#606266' : 'rgb(209, 67, 58)',
+              }"
+              >{{ row.state === 1 ? "启用" : "禁用" }}</span
+            >
+            <span v-else-if="item.key === 'RelatVariety'">
+              {{ row.chart_permission_classify_name }}/{{
+                row.chart_permission_name
+              }}
+            </span>
+            <span v-else>{{ row[item.key] }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center">
+          <template #header>
+            <span>操作</span>
+            <el-tooltip
+              effect="dark"
+              placement="top-start"
+              :content="`禁用后将关闭该标签${
+                pageState.tableName === 'tag' ? '' : '分类'
+              },不清除历史数据`"
+            >
+              <i class="el-icon-question" />
+            </el-tooltip>
+          </template>
+          <template #default="{ row }">
+            <span
+              @click="editTag(row)"
+              style="
+                color: rgb(64, 153, 239);
+                cursor: pointer;
+                margin-right: 10px;
+              "
+              >编辑</span
+            >
+            <span
+              @click="handleChangeTagStatus(row)"
+              style="cursor: pointer"
+              :style="{
+                color:
+                  row.state === 1 ? 'rgb(209, 67, 58)' : 'rgb(64, 153, 239)',
+              }"
+              >{{ row.state === 1 ? "禁用" : "启用" }}</span
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        layout="total,prev,pager,next,jumper"
+        background
+        :current-page="pageState.page_no"
+        @current-change="handleCurrentChange"
+        :page-size="pageState.pageSize"
+        :total="pageState.total"
+        style="text-align: end; margin-top: 20px"
+      >
+      </el-pagination>
+    </div>
+    <!-- 添加/编辑标签弹窗 -->
+    <el-dialog
+      v-if="pageState.tagDialogShow"
+      :model-value="pageState.tagDialogShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="handleDialog('close')"
+      width="689px"
+      draggable
+      center
+    >
+      <template #header>
+        <div style="display: flex; align-items: center">
+          <span style="font-size: 16px"
+            >{{ pageState.tagForm.tag_name ? "编辑" : "添加" }}标签</span
+          >
+        </div>
+      </template>
+
+      <div class="dialog-container">
+        <div class="input-item">
+          标签名称:<el-input
+            placeholder="请输入标签名称"
+            v-model="pageState.tagForm.tag_name"
+            required
+          ></el-input>
+        </div>
+        <div class="input-item">
+          标签分类:<el-cascader
+            placeholder="请选择标签分类"
+            v-model="pageState.tagForm.variety_classify_id"
+            :options="pageState.tagTreeOptions"
+          ></el-cascader>
+        </div>
+        <div class="input-item">
+          关联品种:<el-cascader
+            placeholder="请选择关联品种"
+            v-model="pageState.tagForm.chart_permission_id"
+            :show-all-levels="false"
+            :props="{ emitPath: false }"
+            :options="pageState.permissionOptions"
+          ></el-cascader>
+        </div>
+      </div>
+      <template #footer>
+        <div class="foot-container">
+          <el-button type="primary" @click="handleChangeTag">确 定</el-button>
+          <el-button @click="handleDialog('close')">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 添加/编辑标签分类弹窗 -->
+    <el-dialog
+      v-if="pageState.classifyDialogShow"
+      :model-value="pageState.classifyDialogShow"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      @close="handleDialog('close')"
+      width="689px"
+      draggable
+      center
+    >
+      <template #header>
+        <div style="display: flex; align-items: center">
+          <span style="font-size: 16px"
+            >{{
+              pageState.tagForm.classify_name ? "编辑" : "添加"
+            }}标签分类</span
+          >
+        </div>
+      </template>
+
+      <div class="dialog-container">
+        <div class="input-item">
+          标签分类名称:<el-input
+            placeholder="请输入标签分类名称"
+            v-model="pageState.tagForm.classify_name"
+            required
+          ></el-input>
+        </div>
+      </div>
+      <template #footer>
+        <div class="foot-container">
+          <el-button type="primary" @click="handleChangeTag">确 定</el-button>
+          <el-button @click="handleDialog('close')">取 消</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+
+<style lang="scss">
+.taglib-page-wrap {
+  .dialog-container {
+    .input-item {
+      .el-cascader {
+        .el-input {
+          width: 100%;
+        }
+      }
+    }
+  }
+}
+</style>
+<style scoped lang="scss">
+.taglib-page-wrap {
+  background-color: #fff;
+  box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+  border-radius: 4px;
+  padding: 30px 20px;
+  min-height: calc(100vh - 120px);
+  box-sizing: border-box;
+  .top-wrap {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-bottom: 20px;
+    .switch-tab {
+      /* width:200px; */
+      display: flex;
+      .tab-item {
+        font-size: 16px;
+        margin-right: 30px;
+        height: 24px;
+        line-height: 24px;
+        padding-bottom: 2px;
+        cursor: pointer;
+        &.active {
+          color: #409eff;
+          border-bottom: 2px solid #409eff;
+        }
+      }
+    }
+    .tool-wrap {
+      display: flex;
+      /* width:600px; */
+      height: 40px;
+      /* justify-content: space-between; */
+    }
+  }
+  .dialog-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    .input-item {
+      width: 350px;
+      margin-bottom: 20px;
+      display: flex;
+      align-items: center;
+      .el-input,
+      .el-cascader {
+        flex: 1;
+      }
+    }
+  }
+}
+</style>