Răsfoiți Sursa

外部协议

@dongkboy 1 an în urmă
părinte
comite
cc787e16ec

+ 35 - 2
src/api/modules/agreement.ts

@@ -16,7 +16,7 @@ const agreementApi = (axiosInstance: ApiInstance) => ({
   //协议授权保存
   authSave: (data: Types.authSaveRequest) => axiosInstance.post('agreement/authSave', data),
   //根据机构获取后线人员列表
-  getUserByDeptId: (deptId?: string,searchValue?:string) => axiosInstance.get(`agreement/getUserByDeptId?deptId=${deptId}&searchValue=${searchValue}`),
+  getUserByDeptId: (deptId?: string, searchValue?: string) => axiosInstance.get(`agreement/getUserByDeptId?deptId=${deptId}&searchValue=${searchValue}`),
   //协议授权机构查询
   agreementdeptTree: (id: string, deptName: string) => axiosInstance.get(`agreement/deptTree?id=${id}&deptName=${deptName}`),
   //启用禁用
@@ -25,6 +25,8 @@ const agreementApi = (axiosInstance: ApiInstance) => ({
   agreementDelete: (ids: any) => axiosInstance.delete(`/agreement/agreementDelete?ids=${ids}`),
   //获取保险公司数结构
   findTree: (data: any) => axiosInstance.post('esmInsCompany/findTree', data),
+  //复制协议
+  agreementCopy: (data: any) => axiosInstance.post('/agreement/agreementCopy', data),
   /**
     * @param 承保规则
     */
@@ -57,11 +59,42 @@ const agreementApi = (axiosInstance: ApiInstance) => ({
   //协议费用批量删除
   AgreementFeedeleteByIds: (data: any) => axiosInstance.post('ptlAgreementCost/deleteByIds', data),
   //协议费用批量启用/禁用
-  AgreementFeeEnableByIds: (isEnabled:boolean,ids:any) => axiosInstance.post('ptlAgreementCost/enableDisableByIds?isEnabled='+isEnabled, ids),
+  AgreementFeeEnableByIds: (isEnabled: boolean, ids: any) => axiosInstance.post('ptlAgreementCost/enableDisableByIds?isEnabled=' + isEnabled, ids),
   //协议费用管理详情查询
   AgreementFeeById: (id: any) => axiosInstance.get(`ptlAgreementCost/queryById?id=${id}`),
   //协议上传文件
   agreementEditFile: (data: any) => axiosInstance.post('agreement/agreementEditFile', data),
 
+
+
+  /**
+   * 外部协议
+   */
+  //外部协议添加
+  contactAdd: (data: any) => axiosInstance.post('/agreement/contactAdd', data),
+  //外部协议修改
+  contactEdit: (data: any) => axiosInstance.post('/agreement/contactEdit', data),
+  //费用列表崔查询
+  costExternalList: (data: any) => axiosInstance.post('/ptlAgreementCostExternal/costExternalList', data),
+  //补录协议费用新增
+  ptlAgreementCostExternalsave: (data: any) => axiosInstance.post('/ptlAgreementCostExternal/save', data),
+  //补录协议费用修改
+  ptlAgreementCostExternalupdate: (data: any) => axiosInstance.post('ptlAgreementCostExternal/updateList', data),
+  //查询协议下对接人所有费用
+  selectByAgreementIdAll: (data: any) => axiosInstance.post('ptlAgreementCostExternal/selectByAgreementIdAll', data),
+  //删除对接人
+  deleteByPtlAgreementIdAndMobile: (data: any) => axiosInstance.post('ptlAgreementCostExternal/deleteByPtlAgreementIdAndMobile', data),
+  //费用管理分页列表
+  CostExternalFeePageList: (data: any) => axiosInstance.post('ptlAgreementCostExternal/pageList', data),
+  //单个费用修改
+  FeeupdateById: (data: any) => axiosInstance.post('/ptlAgreementCostExternal/updateById', data),
+  //单个费用查询
+  FeegetById: (id: any) => axiosInstance.get('/ptlAgreementCostExternal/getById?id='+id),
+  //协议费用管理批量禁用/启用
+  enableDisableByIds: (isEnabled: boolean, ids: any) => axiosInstance.post('ptlAgreementCostExternal/enableDisableByIds?isEnabled=' + isEnabled, ids),
+  //批量删除
+  deleteById: (data: any) => axiosInstance.post('ptlAgreementCostExternal/deleteById', data),
+  //批量修改审核方式
+  approvalMethodAll: (auditMethod: string, ids: any) => axiosInstance.post('ptlAgreementCostExternal/approvalMethodAll?auditMethod=' + auditMethod, ids),
 })
 export default agreementApi

+ 8 - 8
src/api/modules/role.ts

@@ -4,31 +4,31 @@ interface roleData {
   desc: string;
   system: string,
   permsId: number[],
-  perms: string[]
+  perms: string[],
 }
 const roleApi = (axiosInstance: ApiInstance) => ({
   // 新增角色
-  roleAdd: (data: roleData) => axiosInstance.post('userRole/roleAdd', data, {}),
+  roleAdd: (data: any) => axiosInstance.post('userRole/roleAdd', data, {}),
 
   //角色列表
   roleList:(data: roleData) => axiosInstance.post('userRole/roleList', data, {}),
 
   //查询角色信息
-  roleInfo:(params: roleData) => axiosInstance.get('userRole/roleGet', {params}),
+  roleInfo:(params: any) => axiosInstance.get('userRole/roleGet', {params}),
 
   //编辑角色信息
-  roleUpdate:(data: roleData) => axiosInstance.post('userRole/roleUpdate', data,{}),
+  roleUpdate:(data: any) => axiosInstance.post('userRole/roleUpdate', data,{}),
 
   //角色删除检测
-  roleDeleteValid:(data: roleData) => axiosInstance.post('userRole/roleDeleteValid', data,{}),
+  roleDeleteValid:(data: any) => axiosInstance.post('userRole/roleDeleteValid', data),
 
   //角色删除检测
-  roleDelete:(data: roleData) => axiosInstance.post('userRole/roleDelete', data,{}),
+  roleDelete:(data: any) => axiosInstance.post('userRole/roleDelete', data,{}),
 
   //配置路由
   updateConfig:(data: any) => axiosInstance.post('system/updateConfig', data,{})
-  
- 
+
+
 })
 
 export default roleApi

+ 24 - 6
src/components/complexTable/index.vue

@@ -7,7 +7,8 @@
       <el-table ref="multipleTableRef"
         :header-cell-style="{ 'background-color': '#F7F7F9', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
         :data="props.tableData" :height="tableHeight" :max-height="maxHeight" table-layout="fixed" style="width: 100%;"
-        @selection-change="selectionChange" @select-all="selectAllChange">
+        @selection-change="selectionChange" @select-all="selectAllChange" :highlight-current-row="highlightCurrentRow"
+        @current-change="currentChange" v-loading="loading">
         <!-- 空内容自定义 -->
         <template #empty>
           <div class="flex f-c a-c ">
@@ -72,7 +73,7 @@
                 v-if="typeof getDisplayText(scope.row, column) === 'string' && getDisplayText(scope.row, column).includes('\n')"
                 class="flex f-c ">
                 <span v-for="(line, index) in getDisplayText(scope.row, column).split('\n')" :key="index">{{ line
-                  }}</span>
+                }}</span>
               </div>
               <span v-else>
                 {{ getDisplayText(scope.row, column) }}
@@ -80,7 +81,7 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column label="操作" fixed="right" :width="props.columnwidth" align="center">
+        <el-table-column label="操作" fixed="right" :width="props.columnwidth" v-if="props.showOperation" align="center">
           <template #default="scope">
             <div class="flex a-c j-c">
               <slot name="operation" :operationData="scope.row"></slot>
@@ -104,11 +105,14 @@ import type { TableInstance } from "element-plus";
 interface TableRow {
   [key: string]: any;
 }
-const emit = defineEmits(['getList', 'selectAllChange', 'selectionChange']);
-const props = defineProps<{
+const emit = defineEmits(['getList', 'selectAllChange', 'selectionChange', 'currentChange']);
+interface Props{
   tableData: Array<TableRow>;
   columnwidth: number,//操作栏宽度
   secondaryTable?: boolean,//是否展示二级表格
+  highlightCurrentRow?: boolean,//是否可点击
+  showOperation?: boolean,//操作显示/隐藏
+  loading?:boolean,
   columns: {
     prop: string;
     label: string;
@@ -140,17 +144,27 @@ const props = defineProps<{
   tableheight?: (number | string),
   maxHeight?: (number | string),
   uniqueIdName?: string,
-}>();
+};
 interface Row {
   [key: string]: any; // 假设Row是一个具有任意属性的对象
 }
 interface RouterLink {
   name: string;
 }
+const props = withDefaults(defineProps<Props>(), {
+  showOperation: true, // 默认值为 true
+});
+const loading=ref(false)
 const tableHeight = ref(props.tableheight); // 初始化表格高度
 const newSelectionList = ref<string[]>([]);
 const multipleTableRef = ref<TableInstance>();
 const TableData = ref<TableRow[]>();
+watch(props, () => {
+  if(props.loading!=undefined){
+    loading.value=props.loading;
+
+  }
+})
 onMounted(() => {
   let counter = 1;
   TableData.value = props.tableData.map((row) => ({
@@ -192,6 +206,10 @@ const selectAllChange = (value: any[]) => {
   emit('selectAllChange', newSelectionList.value)
 
 }
+//行点击事件
+const currentChange = (currentRow: any, _oldCurrentRow: any) => {
+  emit('currentChange', currentRow)
+}
 const selectionChange = (value: any[]) => {
   newSelectionList.value = [];
   if (value.length > 0) {

+ 12 - 2
src/router/modules/externalAgreement.ts

@@ -26,10 +26,20 @@ const routes: RouteRecordRaw = {
           meta: {
             title: '协议添加',
             menu:false,
-            activeMenu: '/personnel/member',
+            activeMenu: '/externalAgreement/agreement/agreementIndex',
+          },
+        },
+
+        {
+          path: 'agreementDetails',
+          name: 'ExternalAgreementDetails',
+          component: () => import('@/views/externalAgreement/agreement/agreementDetails.vue'),
+          meta: {
+            title: '协议详情',
+            menu:false,
+            activeMenu: '/externalAgreement/agreement/agreementIndex',
           },
         },
-       
       ]
     },
     {

+ 11 - 11
src/router/modules/quoteConfig.ts

@@ -39,19 +39,19 @@ const routes: RouteRecordRaw = {
             activeMenu: '/quoteConfig/agreement/agreementIndex',
           },
         },
-        // {
-        //   path: 'agreementDetails',
-        //   name: 'AgreementDetails',
-        //   component: () => import('@/views/quoteConfig/agreement/agreementDetails.vue'),
-        //   meta: {
-        //     title: '协议详情',
-        //     menu:false,
-        //     activeMenu: '/quoteConfig/agreement/agreementIndex',
-        //   },
-        // },
+        {
+          path: 'agreementDetails',
+          name: 'AgreementDetails',
+          component: () => import('@/views/quoteConfig/agreement/agreementDetails.vue'),
+          meta: {
+            title: '协议详情',
+            menu:false,
+            activeMenu: '/quoteConfig/agreement/agreementIndex',
+          },
+        },
       ]
     },
-    
+
     {
       path: 'costManagement',
       name: 'CostManagement',

+ 3 - 0
src/types/components.d.ts

@@ -8,6 +8,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     Auth: typeof import('./../components/Auth/index.vue')['default']
+    ComplexTable: typeof import('./../components/complexTable/index.vue')['default']
     DialogSetDeptLeader: typeof import('./../components/DialogSetDeptLeader/index.vue')['default']
     Empty: typeof import('./../components/empty/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
@@ -41,9 +42,11 @@ declare module 'vue' {
     RuleCondition: typeof import('./../components/ruleCondition/index.vue')['default']
     RuleConditionContainer: typeof import('./../components/ruleConditionContainer/index.vue')['default']
     SearchBar: typeof import('./../components/SearchBar/index.vue')['default']
+    SearchForm: typeof import('./../components/SearchForm/index.vue')['default']
     SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
     SystemInfo: typeof import('./../components/SystemInfo/index.vue')['default']
     Table: typeof import('./../components/Table/index.vue')['default']
+    TableTree: typeof import('./../components/TableTree/index.vue')['default']
     Trend: typeof import('./../components/Trend/index.vue')['default']
   }
 }

+ 429 - 0
src/views/externalAgreement/agreement/agreementAdd.vue

@@ -0,0 +1,429 @@
+<template>
+  <div>
+    <PageMain style="padding: 20px;margin-bottom: 100px;">
+      <ElRow :gutter="60">
+        <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+          <span class="strong">协议信息</span>
+        </ElCol>
+        <el-form :model="agreementFrom" label-width="120px" :rules="agreementFromRules" label-position="top"
+          ref="agreementFromRef" style="max-width: 100%;" class="flex a-c f-wrap" :size="fromSize">
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议名称" prop="agreementTitle">
+              <el-input v-model="agreementFrom.agreementTitle" placeholder="输入协议名称" maxlength="50"
+                show-word-limit></el-input>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议简称" prop="agreementAbbreviation">
+              <el-input v-model="agreementFrom.agreementAbbreviation" placeholder="输入简称" maxlength="50"
+                show-word-limit />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议生效时间" prop="startDate">
+              <el-date-picker v-model="agreementFrom.startDate" style="width: 100%;" type="datetime"
+                placeholder="选择生效时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                time-format="HH:mm:ss" @change="handleStartDateChange" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议失效时间" prop="endDate">
+              <el-date-picker v-model="agreementFrom.endDate" style="width: 100%;" type="datetime" placeholder="选择失效时间"
+                format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" time-format="HH:mm:ss"
+                :disabled-date="disabledEndDate" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="是否含增值税结算" prop="withTax">
+              <el-radio-group v-model="agreementFrom.withTax">
+                <el-radio-button :label="item.label" :value="item.value" v-for="(item, index) in getDict('with_tax')"
+                  :key="index" />
+              </el-radio-group>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="结算周期" prop="moneyTime">
+              <span style="margin: 0  20px;font-size: 18px;">T</span>
+              <el-input-number v-model="agreementFrom.moneyTime" :min="1" :max="999" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12"></ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="协议描述" prop="agreementName">
+              <el-input v-model="agreementFrom.agreementName" placeholder="请输入" type="textarea" maxlength="500"
+                show-word-limit />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+            <ElDivider></ElDivider>
+            <span class="strong">其他信息</span>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议归属" prop="deptId">
+              <el-cascader v-model="agreementFrom.deptId" style="width: 100%;" filterable clearable
+                :options="deptOptions" :props="parentIdprops" :show-all-levels="false" @change="deptIdChange" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="保险公司" prop="companyId">
+              <el-cascader v-model="agreementFrom.companyId" style="width: 100%;" filterable clearable
+                :options="companyOptions" :props="companyIdprops" :show-all-levels="false" @change="companyIdChange" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="协议类型" prop="agreementType">
+              <el-select v-model="agreementFrom.agreementType" placeholder="选择协议类型">
+                <el-option :label="item.label" :value="item.value" v-for="(item, index) in getDict('agreement_type')"
+                  :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="管理人" prop="custodianId">
+              <el-input v-model="managerName" @click="managerModalVisible = true">{{ agreementFrom.custodianId
+              }}</el-input>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+
+    </PageMain>
+    <FixedActionBar>
+      <ElButton type="primary" @click="submitForm(agreementFromRef)">
+        保存
+      </ElButton>
+      <ElButton @click="router.back()">
+        取消
+      </ElButton>
+    </FixedActionBar>
+    <!-- 选择管理人 -->
+    <managerModal :managerModal="managerModalVisible" :deptOptions="deptOptions" :managerlist="managerlist"
+      @save="managerModalsave" @NodeClick="managerModalNodeClick" @query="managerModalQuery" @cancel="managerModalVisible = false" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import { FormRules, FormInstance, ComponentSize, ElMessage } from 'element-plus';
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import { useRoute, useRouter } from 'vue-router';
+import managerModal from "./components/managerModal.vue";
+import api from '@/api';
+const parentIdprops = {
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'name',
+  checkStrictly: true,
+}
+const companyIdprops = {
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'nameSimple',
+  checkStrictly: true,
+}
+interface AgreementForm {
+  id?: string; // 协议 ID
+  agreementTitle: string; // 协议名称
+  agreementAbbreviation: string; // 协议简称
+  startDate: string; // 生效时间,通常为 ISO 格式的日期字符串
+  endDate: string; // 失效时间,通常为 ISO 格式的日期字符串
+  withTax: string; // 是否含增值税结算
+  moneyTime: any; // 结算周期
+  agreementName: string; // 协议描述
+  deptId: string; // 协议归属
+  companyId: string; // 保险公司 ID
+  agreementType: string; // 协议类型
+  custodianId: string; // 管理人 ID
+
+}
+const route = useRoute();
+const router = useRouter();
+const id = ref(route.query.id as string);//id参数
+const deptOptions = ref([])//机构数据
+const companyOptions = ref()//保险公司数据
+const agreementFromRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')
+const agreementFrom: AgreementForm = reactive({
+  id: "",
+  agreementTitle: "",//协议名称
+  agreementAbbreviation: "",//协议简称
+  startDate: "",//生效时间
+  endDate: "",//失效时间
+  withTax: "",//是否含增值税结算
+  moneyTime: 0,//结算周期
+  agreementName: "",//协议描述
+  deptId: "",//协议归属
+  companyId: "",//保险公司id
+  agreementType: "",//协议类型
+  custodianId: "",//管理人id
+
+})
+const validatepassword = (_rule: any, value: any, callback: any) => {
+  // 定义正则表达式,例如:只允许字母、数字,长度在4到10个字符之间
+  const codeRegex = /^(?:(?:\+|00)86)?1\d{10}$/;
+  // 判断是否为空
+  if (!value) {
+    return callback(new Error('请输入手机号'));
+  }
+
+  // 验证是否符合正则表达式
+  if (!codeRegex.test(value)) {
+    return callback(new Error('请输入正确的手机号'));
+  }
+  // 验证通过
+  callback();
+};
+
+const agreementFromRules = ref<FormRules>({
+  agreementTitle: [
+    { required: true, trigger: 'blur', message: '请输入协议名称' },
+  ],
+  agreementAbbreviation: [
+    { required: true, trigger: 'blur', message: '请输入协议简称' },
+  ],
+  startDate: [
+    { required: true, trigger: 'change', message: '选择生效时间' },
+  ],
+  endDate: [
+    { required: true, trigger: 'change', message: '选择失效时间' },
+  ],
+  withTax: [
+    { required: true, trigger: 'change', message: '请选择是否含增值税结算' },
+  ],
+  moneyTime: [
+    { required: true, trigger: 'blur', message: '请输入结算周期' },
+  ],
+  agreementName: [
+    { required: true, trigger: 'blur', message: '请输入协议描述' },
+  ],
+  deptId: [
+    { required: true, trigger: 'change', message: '请选择协议归属' },
+  ],
+  companyId: [
+    { required: true, trigger: 'change', message: '请选择保险公司' },
+  ],
+  agreementType: [
+    { required: true, trigger: 'blur', message: '请选择协议类型' },
+  ],
+  custodianId: [
+    { required: true, trigger: 'blur', message: '请选择管理人' },
+  ],
+  dockingPerson: [
+    { required: true, trigger: 'blur', message: '请输入对接人' },
+  ],
+  dockingPhone: [
+    { required: true, trigger: 'blur', message: '请输入对接人手机号' },
+    { validator: validatepassword, trigger: 'blur' }
+  ],
+  attributionId: [
+    { required: true, trigger: 'change', message: '请选择出单账号' },
+  ],
+
+});
+onBeforeMount(async () => {
+  await loadDicts(['agreement_type', 'with_tax']);//获取多个字典
+  getDeptTree();//归属机构
+  company();//获取保险公司列表
+  if (id.value) {
+    agreementFrom.id = id.value;
+    detailsInfo();//详情
+  }
+
+});
+//协议详情
+const detailsInfo = () => {
+  api.agreementApi.agreementGet(id.value).then((res: any) => {
+    if (res.code == '200') {
+      // 更新 agreementFrom 的属性
+      Object.keys(agreementFrom).forEach((key) => {
+        if (res.data.agreement.hasOwnProperty(key)) {
+          // 使用类型断言,确保类型匹配
+          agreementFrom[key as keyof AgreementForm] = res.data.agreement[key] as AgreementForm[keyof AgreementForm];
+        }
+      });
+    } else {
+    }
+  });
+}
+const companyIdChange = (value: any) => {
+  if (value) {
+    agreementFrom.companyId = value[value.length - 1]
+  }
+}
+const deptIdChange = (value: any) => {
+  if (value) {
+    agreementFrom.deptId = value[value.length - 1]
+  }
+}
+// 禁用失效时间选择
+const disabledEndDate = (date: any) => {
+  const startDate = new Date(agreementFrom.startDate);
+  return startDate && date < startDate; // 禁用小于生效时间的日期
+};
+
+// 处理生效时间变化
+const handleStartDateChange = (value: any) => {
+  // 如果生效时间改变,检查失效时间是否需要更新
+  if (agreementFrom.endDate && new Date(agreementFrom.endDate) < new Date(value)) {
+    agreementFrom.endDate = ''; // 清空失效时间
+  }
+};
+
+//嫌憎编辑提交事件
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (id.value) {
+        api.agreementApi.contactEdit({ ...agreementFrom }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back()
+          } else {
+          }
+        });
+      } else {
+        api.agreementApi.contactAdd({ ...agreementFrom }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back()
+          } else {
+          }
+        });
+      }
+    } else {
+
+    }
+  })
+}
+const getDeptTree = () => {
+  api.institutionApi.institutionList({}).then((res: any) => {
+    if (res.code == '200') {
+      deptOptions.value = res.data;
+      agreementFrom.deptId = res.data[0].id;
+    } else {
+    }
+  });
+}
+const company = () => {
+  api.agreementApi.findTree({ name: "" }).then((res: any) => {
+    if (res.code == '200') {
+      companyOptions.value = res.data;
+    } else {
+      ElMessage({
+        type: 'error',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//选择管理人
+interface managerlist {
+  id: string,
+  name: string,
+  deptName: string,
+}
+const managerModalVisible = ref(false)
+const managerlist = ref<managerlist[]>([]);//机构向下人员列表
+const managerName = ref();//管理员姓名  回显使用
+//选中机构获取人员
+const managerModalNodeClick = (id: string) => {
+  console.log(id)
+  api.agreementApi.getUserByDeptId(id,'').then((res: any) => {
+    if (res.code == '200') {
+      managerlist.value = res.data;
+    }
+  });
+}
+const managerModalQuery=(searchValue:string)=>{
+  api.agreementApi.getUserByDeptId('',searchValue).then((res: any) => {
+    if (res.code == '200') {
+      managerlist.value = res.data;
+    }
+  });
+}
+//选择管理人保存事件
+const managerModalsave = (id: string, name: string, deptName: string) => {
+  agreementFrom.custodianId = id;
+  managerName.value = name + ' - ' + deptName;
+  managerModalVisible.value = false;
+
+}
+
+</script>
+<style lang="scss" scoped>
+.disabled :deep(.el-upload--picture-card) {
+  display: none;
+}
+
+.agreementNotice {
+  width: max-content;
+  padding: 18px 24px;
+  border: 1px solid #eee;
+  border-radius: 4px;
+
+  &:hover {
+    background-color: #fafafa;
+  }
+
+  img {
+    width: 40px;
+    height: 40px;
+    margin-right: 20px;
+  }
+
+  span:first-child {
+    margin-bottom: 2px;
+    font-size: 14px;
+    color: #333;
+  }
+
+  span:last-child {
+    font-size: 14px;
+    color: #666;
+  }
+
+  .fileBtn {
+    margin-left: 40px;
+
+    .btn-icon {
+      margin: 0 10px;
+      font-size: 20px;
+      color: #999;
+      cursor: pointer;
+
+      &:hover {
+        color: #00a0f4;
+      }
+    }
+  }
+}
+
+.image-view {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 9999;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  background: rgb(0 0 0 / 60%);
+
+  img {
+    width: 800px;
+    height: 800px;
+    border-radius: 5px;
+  }
+}
+</style>

+ 727 - 0
src/views/externalAgreement/agreement/agreementDetails.vue

@@ -0,0 +1,727 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <PageMain>
+      <div class="headers">
+        <div class="flex f-c a-start">
+          <div class="flex j-s a-c" style="width: 100%;">
+            <span class="title">{{ agreementInfo.agreementTitle }}</span>
+            <div>
+              <el-button type="default" @click="agreementAuth">协议授权</el-button>
+              <el-button type="default" plain @click="agreementEdit">编辑</el-button>
+              <el-dropdown trigger="click" @command="dropdownCommand" style="margin-left: 10px;">
+                <el-button plain>
+                  更多 <el-icon class="el-icon--right">
+                    <CaretBottom />
+                  </el-icon></el-button>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item command="启用" v-if="agreementInfo.isEnable == '1'">启用</el-dropdown-item>
+                    <el-dropdown-item command="禁用" v-else>禁用</el-dropdown-item>
+                    <el-dropdown-item command="删除">删除</el-dropdown-item>
+                    <el-dropdown-item command="分配管理人">复制协议</el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </div>
+          </div>
+          <div style="margin-top: 10px;" class="flex">
+            <el-tag type="primary" style="margin-right: 20px;">{{
+              getDict('is_enable').find(val => val.value == agreementInfo.isEnable)?.label}}</el-tag>
+            <el-tag type="success">{{getDict('valid_status').find(val => val.value == agreementInfo.validStatus)?.label
+              }}</el-tag>
+          </div>
+        </div>
+        <el-descriptions :column="3" style="margin-top: 10px;">
+          <el-descriptions-item label="协议号:">{{ agreementInfo.agreementCode }}</el-descriptions-item>
+          <el-descriptions-item label="协议名称:">{{ agreementInfo.agreementTitle }}</el-descriptions-item>
+          <el-descriptions-item label="归属机构:">{{ agreementInfo.deptName }}</el-descriptions-item>
+          <el-descriptions-item label="授权机构:">{{ authDeptlength }}</el-descriptions-item>
+          <el-descriptions-item label="添加人:">
+            {{ agreementInfo.createBy }}
+          </el-descriptions-item>
+          <el-descriptions-item label="添加时间:">
+            {{ agreementInfo.createTime }}
+          </el-descriptions-item>
+        </el-descriptions>
+      </div>
+    </PageMain>
+    <PageMain class="agreement ">
+      <el-tabs v-model="activeName" class="demo-tabs">
+        <el-tab-pane label="协议信息" name="agreementInfo">
+          <el-descriptions title="基本信息" class="m-top-20" :column="3">
+            <el-descriptions-item label="协议名称:">{{ agreementInfo.agreementTitle
+            }}</el-descriptions-item>
+            <el-descriptions-item label="协议简称:">{{ agreementInfo.agreementAbbreviation
+            }}</el-descriptions-item>
+            <el-descriptions-item label="协议生效时间:">{{ agreementInfo.startDate }}</el-descriptions-item>
+            <el-descriptions-item label="协议失效时间:">{{ agreementInfo.endDate }}</el-descriptions-item>
+            <el-descriptions-item label="是否含增值税结算:">
+              {{ agreementInfo.withTax }}
+            </el-descriptions-item>
+            <el-descriptions-item label="结算周期:">T{{ agreementInfo.moneyTime }} </el-descriptions-item>
+            <el-descriptions-item label="协议描述:">{{ agreementInfo.agreementName }}</el-descriptions-item>
+          </el-descriptions>
+          <el-descriptions title="其他信息" class="m-top-20" :column="3">
+            <el-descriptions-item label="协议归属:">{{ agreementInfo.deptName }} </el-descriptions-item>
+            <el-descriptions-item label="保险公司:"> {{ agreementInfo.companyId }}</el-descriptions-item>
+            <el-descriptions-item label="协议类型:">{{ agreementInfo.agreementType }} </el-descriptions-item>
+            <el-descriptions-item label="管理人:"> {{ agreementInfo.custodianId }}</el-descriptions-item>
+            <el-descriptions-item label="对接人:"> {{ agreementInfo.dockingPerson }} </el-descriptions-item>
+            <el-descriptions-item label="对接人手机号:"> {{ agreementInfo.dockingPhone }} </el-descriptions-item>
+          </el-descriptions>
+        </el-tab-pane>
+        <el-tab-pane label="费用管理" name="costManagement">
+          <span class="strong m-right-20">对接人</span>
+          <el-button type="primary" @click="addUnderwritingRule" plain>
+            添加对接人
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+          <div class="flex f-c  RuleContainer" v-if="contactPersonList">
+            <div class="list-item" v-for="(item, index) in contactPersonList" :key="index">
+              <div class="header-title flex f-c ">
+                <div class="flex a-c j-s">
+                  <div class="flex a-c">
+                    <img src="../../../assets/images/fee.png" alt="">
+                    <span class="strong m-left-10">{{ item.contactPerson
+                    }}</span>
+                    <span class="strong m-left-10">{{ item.contactMobile }}</span>
+                  </div>
+                  <div class="flex">
+                    <el-button link type="primary" :icon="Delete"
+                      @click="contactPersonDel(item.contactMobile)">删除</el-button>
+                    <el-button link type="primary" :icon="Edit" @click="contactPersonEdit(item)">编辑</el-button>
+                    <el-button v-if="!item.contactPersonShow" link type="default"
+                      @click="item.contactPersonShow = !item.contactPersonShow">展开<el-icon class="m-left-10">
+                        <ArrowDownBold />
+                      </el-icon> </el-button>
+                    <el-button v-else link type="default"
+                      @click="item.contactPersonShow = !item.contactPersonShow">收起<el-icon class="m-left-10">
+                        <ArrowUpBold />
+                      </el-icon></el-button>
+                  </div>
+                </div>
+              </div>
+              <div class="header-content" v-if="item.contactPersonShow">
+                <ComplexTable :tableData="item.costExternalListVo" :columns="columns" :maxHeight="400"
+                  :showIndex="false" :columnwidth="200" :showSelect="false">
+                  <template v-slot:operation="scope">
+                    <el-button type="primary" link @click="itemEdit(scope.operationData.id)"> 编辑
+                    </el-button>
+                    <el-button type="primary" link @click="itemDel(scope.operationData.id)"> 删除
+                    </el-button>
+                  </template>
+                </ComplexTable>
+              </div>
+            </div>
+          </div>
+          <Empty v-else />
+        </el-tab-pane>
+      </el-tabs>
+    </PageMain>
+    <!-- 协议授权弹窗 -->
+    <auth-Drawer :Drawer="agreementAuthDrawer" @cancel="agreementAuthDrawer = false" @save="DrawerSave"
+      @query="Drawerquery" :deptlist="deptauthList" :checkedKeys="checkedKeys" :checkedIds="checkedIds"></auth-Drawer>
+    <!-- 规则费用抽屉组件 -->
+    <contactPersonFeeDialog v-show="addcontactPersonShow" :ruleFeeVisible="addcontactPersonShow"
+      :title='contactPersontitle' :agreementInfo="agreementInfo"  @save="contactPersonSave"
+      :contactPersonEditInfo="contactPersonEditInfo" addMethod="details" @cancel="addcontactPersonShow = false"  />
+    <!-- 规则费用编辑组件 -->
+    <contactPersonFeeDialogEdit :ruleFeeVisible="ruleFeeEditVisible" :headerTitle="ruleFeeHeaderTitle"
+      :agreementInfo="agreementInfo"  :costitemEditInfo="costitemEditInfo" @save="ruleFeeEditSave"
+      @cancel="ruleFeeEditVisible = false" />
+  </div>
+</template>
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import api from '@/api';
+import { CaretBottom } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox, } from 'element-plus'
+import { ref, reactive } from 'vue'
+import { useRoute, useRouter } from 'vue-router';
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import authDrawer from "./components/agreementAuth.vue";
+import contactPersonFeeDialog from "./components/contactPersonFeeDialog.vue";
+import contactPersonFeeDialogEdit from "./components/contactPersonFeeDialogEdit.vue";
+import { Delete, Edit } from '@element-plus/icons-vue'
+interface Agreement {
+  [key: string]: string | number; // 协议名称
+}
+const agreementInfo = ref<Agreement>({}); // 初始值为 null
+const route = useRoute();
+const router = useRouter();
+const id = ref(route.query.id as string);//id参数
+const activeName = ref('costManagement')//默认标签页
+const authDeptlength = ref<number>(0);
+onBeforeMount(async () => {
+  await loadDicts(['agreement_type', 'valid_status', 'is_enable', 'with_tax', 'audit_status', 'insurance_type']);//获取多个字典
+
+  if (id.value) {
+    detailsInfo();//协议信息
+    costExternalList();//对接人列表
+  }
+})
+//协议详情
+const detailsInfo = () => {
+  api.agreementApi.agreementGet(id.value).then((res: any) => {
+    if (res.code == '200') {
+      agreementInfo.value = res.data.agreement;
+
+      authDeptlength.value = res.data.dept.length;
+      imageUrl.value = res.data.agreement.fileUrl;
+    } else {
+    }
+  });
+}
+
+//编辑跳转
+const agreementEdit = () => {
+  router.push({
+    name: 'ExternalagreementAdd',
+    query: {
+      id: id.value
+    }
+  })
+}
+//协议授权
+//检索事件
+const imageUrl = ref('');//协议文件
+const agreementAuthDrawer = ref(false)//授权弹窗
+const deptauthList = ref([]);//授权机构列表
+const checkedKeys = ref<string[]>([])//授权机构回显数据
+const checkedIds = ref<string[]>([])//授权机构回显id
+const Drawerquery = (name: any) => {
+  agreementAuth(name)
+}
+//授权保存
+const DrawerSave = (list: any) => {
+  api.agreementApi.authSave({ deptIds: list, id: id.value }).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      agreementAuthDrawer.value = false;
+    } else {
+    }
+  });
+}
+//协议授权机构获取
+const agreementAuth = (deptName: any) => {
+  let name = typeof deptName === 'string' ? deptName : '';
+  api.agreementApi.agreementdeptTree(id.value, name).then((res: any) => {
+    if (res.code == '200') {
+      agreementAuthDrawer.value = true;
+      deptauthList.value = res.data;
+      checkedKeys.value = getUncheckedIds(res.data);
+      checkedIds.value = getUncheckedIds(res.data);
+    } else {
+    }
+  });
+}
+// 递归回显数据
+function getUncheckedIds(data: any[]): string[] {
+  let ids: string[] = [];
+
+  data.forEach((item) => {
+    if (item.isCheck === 1) {
+      ids.push(item.id); // 如果 isCheck 为 0,添加 id
+    }
+    if (item.children && item.children.length > 0) {
+      // 如果有子项,递归调用
+      ids = ids.concat(getUncheckedIds(item.children));
+    }
+  });
+
+  return ids;
+}
+
+//更多操作集合
+const dropdownCommand = (e: any) => {
+  switch (e) {
+    case '启用':
+      api.agreementApi.sysUpAndDown({ ids: [id.value], opType: '0' }).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.push({
+            name: 'reload',
+          })
+        } else {
+        }
+      });
+      break;
+    case '禁用':
+      ElMessageBox.confirm(
+        '确定要禁用该协议吗,禁用后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否禁用?',
+        '禁用协议',
+        {
+          confirmButtonText: '确定禁用',
+          type: 'error',
+          center: true,
+          confirmButtonClass: 'el-button--danger',
+        }
+      )
+        .then(() => {
+          api.agreementApi.sysUpAndDown({ ids: [id.value], opType: '1' }).then((res: any) => {
+            if (res.code == '200') {
+              ElMessage({
+                type: 'success',
+                message: res.msg,
+                plain: true,
+              })
+              router.push({
+                name: 'reload',
+              })
+            }
+          });
+        })
+
+      break;
+    case '删除':
+      ElMessageBox.confirm(
+        '确定要删除该协议吗,删除后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否删除?',
+        '删除协议',
+        {
+          confirmButtonText: '确定删除',
+          type: 'error',
+          center: true,
+          confirmButtonClass: 'el-button--danger',
+        }
+      )
+        .then(() => {
+          api.agreementApi.agreementDelete({ ids: [id.value] }).then((res: any) => {
+            if (res.code == '200') {
+              ElMessage({
+                type: 'success',
+                message: res.msg,
+                plain: true,
+              })
+              router.back()
+            }
+          });
+        })
+      break;
+    case '复制协议':
+      break;
+    default:
+      break;
+  }
+
+}
+
+//  添加承保规则
+const addcontactPersonShow = ref(false);//添加对接人
+const contactPersonList = ref();//协议下对接人费用
+const contactPersontitle = ref();//弹窗title
+const addUnderwritingRule = () => {//添加对接人
+  addcontactPersonShow.value = true;
+  contactPersontitle.value = '添加';
+}
+
+//对接人列表
+const costExternalList = () => {
+  api.agreementApi.costExternalList({ ptlAgreementId: id.value }).then((res: any) => {
+    if (res.code == '200') {
+      res.data.map((val: any) => {
+        val.contactPersonShow = false;
+        return val;
+      })
+      contactPersonList.value = res.data;
+    } else {
+    }
+  });
+}
+//修改对接人所有费用
+interface contactPersonEditInfo{
+  ptlAgreementId: string,
+  contactPerson: string,
+  contactMobile: string,
+  ptlAgreementCostExternalAddList: any[],
+}
+const contactPersonEditInfo = reactive<contactPersonEditInfo>({
+  ptlAgreementId: "",//协议id
+  contactPerson: "",//对接人
+  contactMobile: "",//手机号
+  ptlAgreementCostExternalAddList: [],//费用集合
+})
+const contactPersonEdit = (item: any) => {
+  api.agreementApi.selectByAgreementIdAll({ ptlAgreementId: id.value, contactPerson: item.contactPerson, contactMobile: item.contactMobile }).then((res: any) => {
+    if (res.code == '200') {
+      const dict = getDict('insurance_type');//获取险种字典
+      const difference = getObjectArrayDifference(dict, res.data, "label", 'productName');//获取编辑数据和字典数组差集
+      const differenceArray:any = dataCompletion(difference);//差集对象赋值
+      contactPersonEditInfo.ptlAgreementId = id.value;
+      contactPersonEditInfo.contactPerson = item.contactPerson;
+      contactPersonEditInfo.contactMobile = item.contactMobile;
+      contactPersonEditInfo.ptlAgreementCostExternalAddList =[...res.data,...differenceArray];
+      contactPersontitle.value = '编辑';
+      addcontactPersonShow.value = true;
+    } else {
+    }
+  });
+}
+//匹配编辑和新增数组差集
+const getObjectArrayDifference = (arr1: any, arr2: any, key: string, key1: string) => {
+  console.log(arr1, arr2)
+  return arr1.filter((item1: any) =>
+    !arr2.some((item2: any) => item2[key1] === item1[key])
+  );
+}
+/**
+ * @param dataCompletion 编辑数据补全
+ * @param list 差集数组
+ */
+const dataCompletion = (list: any) => {
+  let array = list.map((val: any) => {
+    const filteredList: any = filterCostTypeList(val.label);//根据险种匹配列表
+    let list = JSON.parse(JSON.stringify(filteredList)); // 深拷贝
+    return {
+      productName: val.label,
+      productId: val.value,
+      effectiveTime: "",//生效时间
+      failureTime: "",//失效时间
+      auditMethod: "",//审核方式
+      isInquiry: "0",//是否线询
+      costDescribe: "",//费用描述
+      costExternalTypeAdds: list,
+    }
+  })
+  return array;
+}
+
+// 过滤方法
+const filterCostTypeList = (name: string) => {
+  const ptlAgreementCostTypeList = [
+    {
+      costType: "交强险",
+      inlet: 0,
+      export: 0,
+      totalCost: 0,
+      other: 0,
+      level1: 0,
+      level5: 0,
+    },
+    {
+      costType: "商业险",
+      inlet: 0,
+      export: 0,
+      totalCost: 0,
+      other: 0,
+      level1: 0,
+      level5: 0,
+    },
+    {
+      costType: "驾意险",
+      inlet: 0,
+      export: 0,
+      totalCost: 0,
+      other: 0,
+      level1: 0,
+      level5: 0,
+    }
+  ];
+  return ptlAgreementCostTypeList.filter(item => {
+    if (name === "单交" && item.costType === "商业险") {
+      return false; // 当 name 为 "单交" 且 costType 为 "商业险" 时,返回 false,不包含该项
+    }
+    if (name === "单三" && item.costType === "商业险") {
+      return false; // 当 name 为 "单交" 且 costType 为 "商业险" 时,返回 false,不包含该项
+    }
+    if (name === "单商" && item.costType === "交强险") {
+      return false; // 当 name 为 "商业" 且 costType 为 "交强险" 时,返回 false,不包含该项
+    }
+    return true; // 其他情况返回 true,保留该项
+  });
+};
+//删除规则
+const contactPersonDel = (contactMobile: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该对接人吗,删除后,该对接人的费用也将同步删除,请确认是否删除?',
+    '删除对接人',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.deleteByPtlAgreementIdAndMobile({ ptlAgreementId: id.value, contactMobile: contactMobile }).then((res: any) => {
+        if (res.code == '200') {
+          costExternalList();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        }
+      });
+    })
+}
+//添加费用
+const ruleFeeEditVisible = ref(false);//编辑弹窗
+const ruleFeeHeaderTitle = ref();//编辑弹窗title
+const costitemEditInfo = ref();
+const columns = [
+  { prop: "productName", label: "险种", width: '200' },
+  {
+    prop: "isInquiry", label: "是否现询", width: '200', formatter: (row: any) => {
+      if (row.isInquiry == '0') {
+        return "是"
+      } else {
+        return "否"
+      }
+    }
+  },
+  {
+    prop: "costExternalTypeAdds", label: "交强险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '交强险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1.toFixed(2)}% 五级:${info.level5.toFixed(2)}%`
+      }else {
+        return '-'
+      }
+    }
+  },
+  {
+    prop: "costExternalTypeAdds", label: "商业险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '商业险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1.toFixed(2)}% 五级:${info.level5.toFixed(2)}%`
+      }else {
+        return '-'
+      }
+    }
+  },
+  {
+    prop: "costExternalTypeAdds", label: "驾意险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '驾意险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1.toFixed(2)}% 五级:${info.level5.toFixed(2)}%`
+      }else {
+        return '-'
+      }
+    }
+  },
+
+  {
+    prop: "effectiveTime", label: "有效期", width: '200',
+    formatter: (row: any) => {
+      return `起${row.effectiveTime}\n止${row.failureTime}`
+    }
+  },
+  {
+    prop: "isEnabled", label: "费用状态", width: '200', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('is_enable').find(val => val.value == row.isEnabled).label
+      if (name) {
+        return { type: 'danger', text: name };
+      } else {
+        return { type: 'primary', text: name };
+      }
+    }
+  },
+  {
+    prop: "effective", label: "有效状态", width: '200',
+    component: "Tag",
+    formatter: (row: any) => {
+      if (row.effective) {
+        return { type: 'danger', text: '有效' };
+      } else {
+        return { type: 'danger', text: '无效' };
+      }
+    }
+  },
+];
+
+//费用提交
+const contactPersonSave = (data: object) => {
+  if (contactPersontitle.value == '编辑') {
+    api.agreementApi.ptlAgreementCostExternalupdate(data).then((res: any) => {
+      if (res.code == '200') {
+        addcontactPersonShow.value = false;
+        costExternalList();
+      } else {
+      }
+    });
+  } else {
+    api.agreementApi.ptlAgreementCostExternalsave(data).then((res: any) => {
+      if (res.code == '200') {
+        addcontactPersonShow.value = false;
+        costExternalList();
+      } else {
+      }
+    });
+  }
+
+}
+//费用编辑
+const ruleFeeEditSave = (data: object) => {
+  api.agreementApi.FeeupdateById(data).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      ruleFeeEditVisible.value=false;
+      costExternalList();
+    } else {
+    }
+  });
+}
+//编辑删除费用
+const itemEdit = (id: string) => {
+  api.agreementApi.FeegetById(id).then((res: any) => {
+    if (res.code == '200') {
+      ruleFeeHeaderTitle.value = res.data.productName;
+      costitemEditInfo.value = res.data;
+      ruleFeeEditVisible.value = true;
+    } else {
+    }
+  });
+}
+
+//删除费用
+const itemDel = (id: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该费用,请确认是否删除?',
+    '删除费用',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.deleteById([id]).then((res: any) => {
+        if (res.code == '200') {
+          costExternalList();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        } else {
+        }
+      });
+    })
+}
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+:deep(.demo-tabs .el-tabs__item) {
+  height: 60px;
+  font-size: 20px;
+  font-weight: bold !important;
+}
+
+:deep(.ruleFee .el-tabs__item) {
+  height: 40px;
+  font-size: 14px;
+  font-weight: 500 !important;
+}
+
+:deep(.agreement .p-5) {
+  padding-top: 0;
+}
+
+:deep(.el-descriptions__cell) {
+  width: 33.33%;
+}
+
+:deep(.my-label) {
+  font-weight: bold !important;
+}
+
+.headers {
+  .title {
+    font-size: 20px;
+    font-weight: bold;
+    color: #333;
+  }
+}
+
+.RuleContainer {
+  margin-top: 20px;
+
+  .list-item {
+    padding: 18px 16px;
+    margin-bottom: 20px;
+    border: 1px solid #e5e5e5;
+    border-radius: 10px;
+
+    .empty {
+      font-size: 14px;
+      color: #666;
+      text-align: center;
+    }
+  }
+}
+
+.agreementNotice {
+  width: max-content;
+  padding: 18px 24px;
+  border: 1px solid #eee;
+  border-radius: 4px;
+
+  &:hover {
+    background-color: #fafafa;
+  }
+
+  img {
+    width: 40px;
+    height: 40px;
+    margin-right: 20px;
+  }
+
+  span:first-child {
+    margin-bottom: 2px;
+    font-size: 14px;
+    color: #333;
+  }
+
+  span:last-child {
+    font-size: 14px;
+    color: #666;
+  }
+
+  .fileBtn {
+    margin-left: 40px;
+
+    .btn-icon {
+      margin: 0 10px;
+      font-size: 20px;
+      color: #999;
+      cursor: pointer;
+
+      &:hover {
+        color: #00a0f4;
+      }
+    }
+  }
+}
+
+.title {
+  display: flex;
+
+  &::before {
+    display: inline-block;
+    width: 4px;
+    height: 18px;
+    margin-right: 20px;
+    content: "";
+    background-color: #0083ff;
+  }
+}
+</style>

+ 473 - 11
src/views/externalAgreement/agreement/agreementIndex.vue

@@ -1,17 +1,479 @@
-<!-- 模板区域 -->
 <template>
-<div>
-<PageMain>
-</PageMain>
-</div>
+  <div>
+    <PageMain class="vh100">
+      <SearchForm :fields="fields" :formInline="formInline" :onSearch="agreementQuery" :onReset="resetForm" />
+      <ComplexTable :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectionChange="selectionChange">
+        <template v-slot:table-title-btn>
+          <RouterLink :to="{ name: 'ExternalagreementAdd' }">
+            <el-button type="primary">
+              添加协议
+              <template #icon>
+                <el-icon>
+                  <Plus />
+                </el-icon>
+              </template>
+            </el-button>
+          </RouterLink>
+          <el-dropdown placement="bottom-start">
+            <el-button > 批量管理<el-icon>
+                <arrow-down />
+              </el-icon>
+            </el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchDel">删除</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchdisable">禁用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchenable">启用</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button type="primary" link @click="agreemenUpdate(scope.operationData)"> 详情
+          </el-button>
+          <el-dropdown placement="bottom-start" trigger="click">
+            <span class="el-dropdown-link">
+              更 多
+            </span>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item @click="enable(scope.operationData.id)">启用</el-dropdown-item>
+                <el-dropdown-item @click="disable(scope.operationData.id)">禁用</el-dropdown-item>
+                <el-dropdown-item @click="del(scope.operationData.id)">删除</el-dropdown-item>
+                <el-dropdown-item @click="agreemenCopy(scope.operationData.id)">复制协议</el-dropdown-item>
+                <el-dropdown-item @click="batchAuthorization(scope.operationData.id)">协议授权</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:search="scope">
+          {{ scope.slotData.label }}
+        </template>
+      </ComplexTable>
+    </PageMain>
+    <auth-Drawer :Drawer="agreementAuthDrawer" @cancel=" agreementAuthDrawer = false" @save="DrawerSave"
+      @query="Drawerquery" :deptlist="deptauthList" :checkedKeys="checkedKeys" :checkedIds="checkedIds"></auth-Drawer>
+  </div>
 </template>
 
-<!-- 行为区域 -->
-<script lang='ts' setup>
-import { ref,reactive } from 'vue'
-import api from '@/api'
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import { RouterLink, useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import authDrawer from "./components/agreementAuth.vue";
+import api from '@/api';
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+onBeforeMount(async () => {
+  await loadDicts(['agreement_type', 'valid_status', 'is_enable', 'with_tax']);//获取多个字典
+  findpage();//列表数据
+  getDeptTree();
+  agreement_type.value = getDict('agreement_type');//协议类型
+  valid_status.value = getDict('valid_status');//生效状态
+  is_enable.value = getDict('is_enable');//协议状态
+  with_tax.value = getDict('with_tax')//是否含税
+});
+
+const parentIdprops = {
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'name',
+  checkStrictly: true,
+}
+const tableData = ref([
+]);
+const sysUpAndDownList = ref<string[]>([])//选中id
+const provinceoptions = ref<string[]>([])//省市区回显数据
+const deptOptions = ref()//机构数据
+const agreement_type = ref();
+const valid_status = ref();
+const is_enable = ref();
+const with_tax = ref();
+const router = useRouter();
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 100,
+});
+const formInline = reactive({
+  searchValue: '',//协议信息
+  deptId: "",//归属
+  companyId: '',//保险公司id
+  agreementType: '',//协议类型
+  validStatus: "",//有效状态
+  isEnable: "",//协议状态
+  withTax: "",
+  dockingPerson: "",
+  attributionName: "",
+  takeStartDate: "",
+  takeEndDate: "",
+  loseStartDate: "",
+  loseEndDate: "",
+  isExternal:'1',
+  value1:[],
+  value2:[],
+})
+
+//表格字段数据
+const fields = ref([
+  { label: "协议信息", model: "searchValue", type: "input", placeholder: "输入代码,名称,简称查找" },
+  {
+    label: "协议归属", model: "deptId", type: "cascader", options: deptOptions, props: parentIdprops, onChange: (value: any) => {
+      formInline.deptId = value[value.length - 1];
+    }
+  },
+  {
+    label: "保险公司", model: "companyId", type: "select", placeholder: "选择保险公司",
+  },
+  {
+    label: "协议类型", model: "agreementType", type: "select", placeholder: "选择保险公司", options: agreement_type,
+  },
+  {
+    label: "生效状态", model: "validStatus", type: "select", placeholder: "选择生效状态", options: valid_status,
+  },
+  {
+    label: "协议状态", model: "isEnable", type: "select", placeholder: "选择协议状态", options: is_enable,
+  },
+  {
+    label: "是否含税结算", model: "withTax", type: "select", placeholder: "请选择", options: with_tax,
+  },
+  {
+    label: "生效时间", model: "value1", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      formInline.takeStartDate = value[0];
+      formInline.takeEndDate = value[1];
+    }
+  },
+  {
+    label: "失效时间", model: "value2", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      formInline.loseStartDate = value[0];
+      formInline.loseEndDate = value[1];
+    }
+  },
+]);
+
+//数据查询
+function findpage() {
+  api.agreementApi.agreementList({ pages: pageInfo.value.pageNum, size: pageInfo.value.pageSize, ...formInline }).then((res: any) => {
+    if (res.code == '200') {
+      tableData.value = res.data.records;
+      pageInfo.value.pageNum = res.data.current;
+      pageInfo.value.pageSize = res.data.size;
+      pageInfo.value.total = res.data.total;
+    } else {
+    }
+  });
+}
+const columns = [
+  { prop: "agreementCode", label: "协议编号", width: '200', },
+  {
+    prop: "agreementTitle", label: "协议名称", width: '200',
+  },
+  { prop: "agreementAbbreviation", label: "协议简称", width: '120', },
+  {
+    prop: "deptName", label: "归属", width: '120',
+  },
+  {
+    prop: "agreementType", label: "协议类型", width: '200', formatter: (row: any) => {
+      let name = getDict('agreement_type').find(val => val.value == row.agreementType).label
+      return name
+    }
+  },
+  {
+    prop: "isEnable", label: "协议状态", width: '100',
+    component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('is_enable').find(val => val.value == row.isEnable).label
+      if (name) {
+        return { type: 'primary', text: name };
+      } else {
+        return { type: 'primary', text: name };
+      }
+    }
+
+  },
+  {
+    prop: "validStatus", label: "生效状态", width: '120', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('valid_status').find(val => val.value == row.validStatus).label
+      if (name) {
+        return { type: 'danger', text: name };
+      } else {
+        return { type: 'danger', text: name };
+      }
+    }
+  },
+  {
+    prop: "withTax", label: "是否含税结算", width: '180',
+    formatter: (row: any) => {
+      let name = getDict('with_tax').find(val => val.value == row.withTax).label
+      return name
+    }
+  },
+  {
+    prop: "moneyTime", label: "结算周期", width: '120',
+    formatter: (row: any) => {
+      return 'T' + row.moneyTime
+    }
+  },
+  { prop: "company", label: "保险公司", width: '200' },
+  { prop: "custodianId", label: "管理人", width: '200' },
+  { prop: "startDate", label: "生效时间", width: '200' },
+  { prop: "endDate", label: "失效时间", width: '200' },
+  { prop: "createTime", label: "添加时间", width: '200' },
+  { prop: "createBy", label: "添加人", width: '200' },
+];
+//分页事件
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+
+};
+//机构授权
+const agreementAuthDrawer = ref(false)//授权弹窗
+const deptauthList = ref([]);//授权机构列表
+const checkedKeys = ref<string[]>([])//授权机构回显数据
+const checkedIds = ref<string[]>([])//授权机构回显id
+//授权机构检索
+const Drawerquery = (name: any) => {
+  agreementAuth(name)
+}
+//授权保存
+const DrawerSave = (list: any) => {
+  api.agreementApi.authSave({ deptIds: list, id: agreementId.value }).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      agreementAuthDrawer.value = false;
+    } else {
+    }
+  });
+}
+//协议授权
+const agreementAuth = (deptName: any) => {
+  let name = typeof deptName === 'string' ? deptName : '';
+  api.agreementApi.agreementdeptTree(agreementId.value, name).then((res: any) => {
+    if (res.code == '200') {
+      agreementAuthDrawer.value = true;
+      deptauthList.value = res.data;
+      checkedKeys.value = getUncheckedIds(res.data);
+      checkedIds.value = getUncheckedIds(res.data);
+    } else {
+    }
+  });
+}
+// 定义递归函数
+function getUncheckedIds(data: any[]): string[] {
+  let ids: string[] = [];
+
+  data.forEach((item) => {
+    if (item.isCheck === 1) {
+      ids.push(item.id); // 如果 isCheck 为 0,添加 id
+    }
+    if (item.children && item.children.length > 0) {
+      // 如果有子项,递归调用
+      ids = ids.concat(getUncheckedIds(item.children));
+    }
+  });
+
+  return ids;
+}
+
+function agreemenUpdate(item: any) {
+  router.push({
+    name: 'ExternalAgreementDetails',
+    query: {
+      id: item.id
+    }
+  })
+}
+
+//多选事件
+const selectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+//协议授权
+const agreementId = ref()
+const batchAuthorization = (value: string) => {
+  agreementId.value = value;
+  api.agreementApi.agreementdeptTree(value, '').then((res: any) => {
+    if (res.code == '200') {
+      agreementAuthDrawer.value = true;
+      deptauthList.value = res.data;
+      checkedKeys.value = getUncheckedIds(res.data);
+      checkedIds.value = getUncheckedIds(res.data);
+    } else {
+    }
+  });
+
+}
+//复制协议
+const agreemenCopy = (id:string) => {
+  router.push({
+    name: 'AgreementCopy',
+    query: {
+      id: id
+    }
+  })
+}
+//禁用
+const disable = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要禁用该协议吗,禁用后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否禁用?',
+    '禁用协议',
+    {
+      confirmButtonText: '确定禁用',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      sysUpAndDown('1', [value]);
+    })
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown('0', [value]);
+}
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该协议吗,删除后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否删除?',
+    '删除协议',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.agreementDelete([value]).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.push({
+            name: 'reload',
+          })
+        }
+      });
+    })
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '确定要删除该协议吗,删除后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否删除?',
+    '删除协议',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.agreementDelete(sysUpAndDownList.value).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.push({
+            name: 'reload',
+          })
+        }
+      });
+    })
+
+}
+//批量禁用
+const batchdisable = () => {
+  ElMessageBox.confirm(
+    '确定要禁用该协议吗,禁用后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否禁用?',
+    '禁用协议',
+    {
+      confirmButtonText: '确定禁用',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(async () => {
+      await sysUpAndDown('1', undefined);
+    })
+    .catch(() => {
+    })
+
+
+}
+//批量启用
+const batchenable = async () => {
+  await sysUpAndDown('0', undefined);
+}
+const agreementQuery = () => {
+  findpage();
+}
+const sysUpAndDown = async (type: string, list?: string[]) => {
+  await api.agreementApi.sysUpAndDown({ ids: list ? list : sysUpAndDownList.value, opType: type }).then((res: any) => {
+    if (res.code == '200') {
+      findpage();
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const resetForm = () => {
+  formInline.searchValue = '';
+  provinceoptions.value = [];
+  findpage();
+}
+const getDeptTree = () => {
+  api.institutionApi.institutionList({}).then((res: any) => {
+    if (res.code == '200') {
+      deptOptions.value = res.data;
+    } else {
+    }
+  });
+}
 </script>
-<!-- 样式区域 -->
-<style lang='scss' scoped>
 
+<style lang="scss" scoped>
+.demo-form-inline .el-input {
+  --el-input-width: 220px;
+}
+
+.demo-form-inline .el-select {
+  --el-select-width: 220px;
+}
+
+:deep(.el-dropdown-link) {
+  display: flex;
+  align-items: center;
+  margin-left: 15px;
+  color: var(--el-color-primary);
+  cursor: pointer;
+}
+
+.el-inputs {
+  width: 100% !important;
+}
 </style>

+ 83 - 0
src/views/externalAgreement/agreement/components/agreementAuth.vue

@@ -0,0 +1,83 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-drawer v-model="props.Drawer" :direction="direction" @open="DrawerOpen" :close-on-click-modal="false">
+      <template #header>
+        <h4>参与机构</h4>
+      </template>
+      <template #default>
+        <div class="flex f-c">
+          <div>
+            <el-input v-model="deptName" class="m-right-20" style="width: 240px;" placeholder="输入名称"
+              :prefix-icon="Search" />
+            <el-button @click="query">查询</el-button>
+          </div>
+          <el-checkbox v-model="checkedALL" label="全选" size="large" />
+          <el-tree :data="props.deptlist" show-checkbox node-key="id" ref="treeRef" :props="defaultProps"
+            :default-expand-all="true" @check="handleCheck" :default-checked-keys="props.checkedKeys"
+            :check-strictly="true" />
+        </div>
+      </template>
+      <template #footer>
+        <div>
+          <el-button @click="cancel">取消</el-button>
+          <el-button type="primary" @click="save">保存</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang="ts" setup>
+import { defineProps, defineEmits } from "vue";
+import { DrawerProps } from 'element-plus';
+import { Search } from '@element-plus/icons-vue'
+const props = defineProps<{
+  Drawer: boolean,
+  deptlist: string[]
+  checkedKeys: string[]
+  checkedIds: string[]
+}>();
+const emits = defineEmits(["cancel", 'save', 'DrawerOpen', 'query']);
+const defaultProps = {
+  children: 'children',
+  label: 'name',
+}
+const direction = ref<DrawerProps['direction']>('rtl')
+const deptName = ref('');
+
+
+const checkedALL = ref(false)
+const ids = ref()//id集合
+const DrawerOpen = () => {
+
+}
+watch(props, () => {
+  if (props.Drawer) {
+    ids.value = props.checkedIds;
+  }
+
+})
+const cancel = () => {
+  emits('cancel')
+}
+const save = () => {
+    emits('save', ids.value)
+}
+const query = () => {
+  emits('query', deptName.value)
+
+}
+const handleCheck = (
+  _checkedNodes: any[],
+  checkedInfo: {
+    checkedKeys: any[];
+    checkedNodes: any[];
+    halfCheckedKeys: any[];
+    halfCheckedNodes: any[];
+  }
+) => {
+  ids.value = checkedInfo.checkedKeys;
+};
+</script>

+ 482 - 0
src/views/externalAgreement/agreement/components/contactPersonFeeDialog.vue

@@ -0,0 +1,482 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-drawer v-model="props.ruleFeeVisible" :direction="direction" :close-on-click-modal="false" size="50%"
+      @open="draweropen">
+      <template #header>
+        <h4>{{ props.title }}费用</h4>
+      </template>
+      <template #default>
+        <ElRow :gutter="20" class="m-top-20">
+          <el-form :model="ruleFeeinfo" label-width="120px" ref="formRef" style="width: 100%;" :rules="feerules"
+            class="flex a-c f-wrap" :size="fromSize">
+            <ElCol :md="24" :lg="18">
+              <el-form-item label="协议" :prop="addMethod == 'list' ? 'ptlAgreementId' : ''">
+                <el-select v-if="addMethod == 'list'" v-model="ruleFeeinfo.ptlAgreementId" placeholder="选择协议"
+                  :disabled="props.title != '添加'">
+                  <el-option v-for="item in agreementselect" :key="item.id" :label="item.agreementTitle"
+                    :value="item.id" />
+                </el-select>
+                <el-select v-else v-model="agreementActiveId" placeholder="选择协议" :disabled="props.title != '添加'">
+                  <el-option v-for="item in agreementselect" :key="item.id" :label="item.agreementTitle"
+                    :value="item.id" />
+                </el-select>
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="18">
+              <el-form-item label="快速配置"
+                v-if="agreementActiveId || ruleFeeinfo.ptlAgreementId || props.agreementInfo?.id">
+                <el-select v-model="contactPersonListid" placeholder="选择对接人" @visible-change="contactvisiblechange"
+                  :disabled="props.title != '添加'" @change="contactPersonChange">
+                  <el-option v-for="item in contactPersonList" :key="item.contactMobile" :label="item.contactPerson"
+                    :value="item.contactMobile" />
+                </el-select>
+                <span style="font-size: 12px;color: #aaa;">选择已存在的对接人,直接复制该对接人的费用信息;也可在下方输入框手动配置</span>
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="对接人姓名" prop="contactPerson">
+                <el-input v-model="ruleFeeinfo.contactPerson" placeholder="请输入对接人姓名" maxlength="50" show-word-limit />
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="对接人手机号" prop="contactMobile">
+                <el-input v-model="ruleFeeinfo.contactMobile" placeholder="请输入手机号" maxlength="11" show-word-limit />
+              </el-form-item>
+            </ElCol>
+          </el-form>
+        </ElRow>
+        <div style="padding-left: 40px;">
+          <el-tabs v-model="activeName" type="card" @tab-click="hledclick" style="position: relative;"
+            class="ruleFeetabs">
+            <el-tab-pane v-for="(item, index) in ruleFeeinfo.ptlAgreementCostExternalAddList" :key="index"
+              :label="item.productName" :name="item.productName">
+              <ElRow :gutter="20" class="m-top-20">
+                <el-form :model="item" label-width="80px" ref="feeFormRef" style="width: 100%;" :rules="feerules"
+                  class="flex a-c f-wrap" :size="fromSize">
+                  <ElCol :md="24" :lg="12">
+                    <el-form-item label="生效时间" prop="effectiveTime">
+                      <el-date-picker v-model="item.effectiveTime" style="width: 100%;" type="datetime"
+                        placeholder="选择生效时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                        time-format="HH:mm:ss" @change="handleStartDateChange($event, index)" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12">
+                    <el-form-item label="失效时间" prop="failureTime">
+                      <el-date-picker v-model="item.failureTime" style="width: 100%;" type="datetime"
+                        placeholder="选择失效时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                        time-format="HH:mm:ss" :disabled-date="(date: any) => disabledEndDate(date, index)" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12">
+                    <el-form-item label="审核方式" prop="auditMethod">
+                      <el-select v-model="item.auditMethod" placeholder="选择审核方式">
+                        <el-option label="自动审核" value="0" />
+                        <el-option label="手动审核" value="1" />
+                      </el-select>
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12">
+                    <el-form-item label="是否现询" prop="isInquiry">
+                      <el-radio-group v-model="item.isInquiry">
+                        <el-radio value="0" size="large">是</el-radio>
+                        <el-radio value="1" size="large">否</el-radio>
+                      </el-radio-group>
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="24">
+                    <el-form-item label="费用描述" prop="costDescribe">
+                      <el-input v-model="item.costDescribe" maxlength="500" placeholder="请输入费用描述" show-word-limit
+                        type="textarea" />
+                    </el-form-item>
+                  </ElCol>
+                </el-form>
+              </ElRow>
+
+              <el-table :data="item.costExternalTypeAdds" style="width: 100%;" border
+                :header-cell-style="{ 'background-color': '#F7F7F9', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }">
+                <template #empty>
+                  <div class="flex f-c a-c ">
+                    <img src="../../assets/images/empty.png" alt="">
+                    <span style="font-size: 14px;color: #909399;">暂无数据</span>
+                  </div>
+                </template>
+                <el-table-column prop="costType" label="类型"></el-table-column>
+                <el-table-column prop="inlet" label="入口比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.inlet" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="export" label="出口比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.export" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="totalCost" label="总费用比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.totalCost" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="other" label="其他费用比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.other" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="level1" label="1级比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.level1" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="level5" label="5级比例" width="180" align="center">
+                  <template #default="scope">
+                    <el-input-number v-model="scope.row.level5" :precision="2" controls-position="right"
+                      style="width: 100%;" :min="0" :max="100">
+                      <template #suffix>%</template>
+                    </el-input-number>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+      </template>
+      <template #footer>
+        <div class="flex a-c j-c">
+          <el-button @click="cancel">取消</el-button>
+          <el-button type="primary" @click="save(feeFormRef)">提交审核</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { ref, } from 'vue'
+import { DrawerProps, ComponentSize, FormInstance, FormRules, ElMessage } from 'element-plus';
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import { defineProps, defineEmits } from "vue";
+import api from '@/api';
+const direction = ref<DrawerProps['direction']>('rtl')
+const fromSize = ref<ComponentSize>('default')
+const activeName = ref('单交')
+const props = defineProps<{
+  ruleFeeVisible: boolean,
+  agreementInfo?: any,
+  addMethod: string,
+  contactPersonEditInfo?: any,
+  title?: string,
+}>();
+interface ruleFeeinfo {
+  ptlAgreementId: string,
+  contactPerson: string,
+  contactMobile: string,
+  ptlAgreementCostExternalAddList: any[],
+}
+
+const emits = defineEmits(["cancel", 'save']);
+const ruleFeeinfo = reactive<ruleFeeinfo>({
+  ptlAgreementId: "",//协议id
+  contactPerson: "",//对接人
+  contactMobile: "",//手机号
+  ptlAgreementCostExternalAddList: [],//费用集合
+});//规则费用集合
+const insuranceTypeIndex = ref(0);//险种区域下标
+const agreementActiveId = ref();//选中协议id
+const agreementselect = ref();
+const feeFormRef = ref<FormInstance>()
+const formRef = ref<FormInstance>();
+const feerules = ref<FormRules>({
+  ptlAgreementId: [
+    { required: true, trigger: 'change', message: '请选择' },
+  ],
+  contactPerson: [
+    { required: true, trigger: 'blur', message: '请输入对接人姓名' },
+  ],
+  contactMobile: [
+    { required: true, trigger: 'blur', message: '请输入手机号' },
+  ],
+  effectiveTime: [
+    { required: true, trigger: 'change', message: '请选择生效时间' },
+  ],
+  failureTime: [
+    { required: true, trigger: 'change', message: '请选择失效时间' },
+  ],
+  auditMethod: [
+    { required: true, trigger: 'change', message: '请选择审核方式' },
+  ],
+  isInquiry: [
+    { required: true, trigger: 'change', message: '请选择是否线询' },
+  ],
+})
+watch(props, () => {
+  if (props.contactPersonEditInfo && props.contactPersonEditInfo.contactPerson) {
+    Object.assign(ruleFeeinfo, props.contactPersonEditInfo);
+    activeName.value = ruleFeeinfo.ptlAgreementCostExternalAddList[0].productName;
+  }
+})
+onMounted(async () => {
+  await loadDicts(['insurance_type'])
+  const dict = getDict('insurance_type');
+  agreementList();
+  ruleFeeinfo.ptlAgreementCostExternalAddList = dict.map((val: any) => {
+    const filteredList: any = filterCostTypeList(val.label);//根据险种匹配列表
+    let list = JSON.parse(JSON.stringify(filteredList)); // 深拷贝
+    return {
+      productName: val.label,
+      productId: val.value,
+      effectiveTime: "",//生效时间
+      failureTime: "",//失效时间
+      auditMethod: "0",//审核方式
+      isInquiry: "0",//是否线询
+      costDescribe: "",//费用描述
+      costExternalTypeAdds: list,
+    }
+  })
+})
+// 协议列表
+const agreementList = () => {
+  api.agreementApi.agreementList({ pages: 1, size: 100, isExternal: '1' }).then((res: any) => {
+    if (res.code == '200') {
+      agreementselect.value = res.data.records;
+    } else {
+    }
+  });
+}
+//对接人列表
+const contactPersonList = ref();
+const contactPersonListid = ref();
+//对接人下拉列表
+const costExternalList = () => {
+  api.agreementApi.costExternalList({ ptlAgreementId: agreementActiveId.value ? agreementActiveId.value : props.agreementInfo?.id }).then((res: any) => {
+    if (res.code == '200') {
+      contactPersonList.value = res.data;
+    } else {
+    }
+  });
+}
+//选择对接人
+const contactPersonChange = (value: string) => {
+  let info = contactPersonList.value.find((val: any) => val.contactMobile == value);
+  ruleFeeinfo.ptlAgreementCostExternalAddList = info.costExternalListVo;
+  activeName.value = ruleFeeinfo.ptlAgreementCostExternalAddList[0].productName;
+
+}
+//下拉框出现事件
+const contactvisiblechange = (visible: boolean) => {
+  if (visible) {
+    costExternalList();
+  }
+}
+// 禁用失效时间选择
+const disabledEndDate = (date: any, index: number) => {
+  let start = ruleFeeinfo.ptlAgreementCostExternalAddList[index]?.effectiveTime;
+  const startDate = new Date(start);
+  return startDate && date < startDate; // 禁用小于生效时间的日期
+};
+
+// 处理生效时间变化
+/**
+ * @param index  险种下标
+ * @param Costindex 费用下标
+ */
+const handleStartDateChange = (value: any, index: number,) => {
+  let endDate = ruleFeeinfo.ptlAgreementCostExternalAddList[index]?.failureTime;
+  // 如果生效时间改变,检查失效时间是否需要更新
+  if (endDate && new Date(endDate) < new Date(value)) {
+    ruleFeeinfo.ptlAgreementCostExternalAddList[index].failureTime = ''; // 清空失效时间
+  }
+};
+const cancel = () => {
+  emits("cancel")
+}
+const validateContactPerson = () => {
+  if (formRef.value) {
+    formRef.value.validateField(['contactPerson', 'contactMobile', 'ptlAgreementId'], (valid) => {
+      if (valid) {
+        console.log('对接人验证通过');
+      } else {
+        ElMessage({
+          type: 'error',
+          message: '对接人信息必填',
+          plain: true,
+        });
+      }
+    });
+  }
+};
+const save = async (formEl: FormInstance | undefined | any) => {
+  validateContactPerson();
+  if (!formEl) return
+  const validationPromises = ruleFeeinfo.ptlAgreementCostExternalAddList.map((formRef: any, index: any) => {
+    // 轮询验证所有表单校验
+    return new Promise((resolve, reject) => {
+      if (formRef) {
+        formEl[index].validate((valid: boolean) => {
+          if (valid) {
+
+            resolve(true);
+          } else {
+            reject(index);
+          }
+        });
+      } else {
+        reject(index);
+      }
+    });
+  });
+
+  try {
+    await Promise.all(validationPromises);
+    // 所有表单验证通过后执行提交逻辑
+    if (props.addMethod == 'details') {
+      ruleFeeinfo.ptlAgreementId = agreementActiveId.value ? agreementActiveId.value : props.agreementInfo.id;
+    }
+    emits("save", ruleFeeinfo)
+  } catch (error) {
+    const invalidIndex: any = error; // 获取未通过校验的表单索引
+    const invalidForm = ruleFeeinfo.ptlAgreementCostExternalAddList[invalidIndex]; // 获取未通过校验的表单对象
+    activeName.value = invalidForm.productName;
+    ElMessage({
+      type: 'error',
+      message: `${ruleFeeinfo.ptlAgreementCostExternalAddList[invalidIndex].productName}费用未填写完整`,
+      plain: true,
+    });
+  }
+};
+// 过滤方法
+const ptlAgreementCostTypeList = [
+  {
+    costType: "交强险",
+    inlet: 0,
+    export: 0,
+    totalCost: 0,
+    other: 0,
+    level1: 0,
+    level5: 0,
+  },
+  {
+    costType: "商业险",
+    inlet: 0,
+    export: 0,
+    totalCost: 0,
+    other: 0,
+    level1: 0,
+    level5: 0,
+  },
+  {
+    costType: "驾意险",
+    inlet: 0,
+    export: 0,
+    totalCost: 0,
+    other: 0,
+    level1: 0,
+    level5: 0,
+  }
+];
+
+const filterCostTypeList = (name: string) => {
+  return ptlAgreementCostTypeList.filter(item => {
+    if (name === "单交" && item.costType === "商业险") {
+      return false; // 当 name 为 "单交" 且 costType 为 "商业险" 时,返回 false,不包含该项
+    }
+    if (name === "单三" && item.costType === "商业险") {
+      return false; // 当 name 为 "单交" 且 costType 为 "商业险" 时,返回 false,不包含该项
+    }
+    if (name === "单商" && item.costType === "交强险") {
+      return false; // 当 name 为 "商业" 且 costType 为 "交强险" 时,返回 false,不包含该项
+    }
+    return true; // 其他情况返回 true,保留该项
+  });
+};
+const hledclick = (pane: any) => {
+  insuranceTypeIndex.value = pane.index;
+  activeName.value = pane.props.name;
+}
+const draweropen = () => {
+  if(formRef.value){
+    formRef.value.resetFields();
+  }
+  contactPersonListid.value='';
+  ruleFeeinfo.ptlAgreementId = "";
+  ruleFeeinfo.contactPerson = "";
+  ruleFeeinfo.contactMobile = "";
+  ruleFeeinfo.ptlAgreementCostExternalAddList.map((val: any) => {
+    val.effectiveTime = "";
+    val.failureTime = "";
+    val.costDescribe = "";
+    val.costExternalTypeAdds.map((item: any) => {
+      for (let key in item) {
+        if(item[key]!='costType'){
+          item[key] = 0;
+        }
+      }
+      return item
+    })
+    return val;
+  })
+}
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+:deep(.el-drawer__header) {
+  padding-top: 0;
+  margin-bottom: 0;
+  border-bottom: 1px solid #f2f2f2;
+}
+
+.agreement {
+  width: 100%;
+  padding: 15px;
+  font-size: 14px;
+  color: #333;
+  border-radius: 4px;
+
+  p {
+    margin: 0;
+    margin-top: 10px;
+  }
+}
+
+.feeItem {
+  width: 100%;
+  border: 1px solid #e5e5e5;
+  border-radius: 8px;
+
+  .header {
+    padding: 12px 14px;
+
+    .title {
+      display: flex;
+
+      &::before {
+        display: inline-block;
+        width: 4px;
+        height: 18px;
+        margin-right: 20px;
+        content: "";
+        background-color: #0083ff;
+      }
+    }
+  }
+}
+
+:deep(.ruleFeetabs>.el-tabs__content) {
+  position: static;
+}
+</style>

+ 254 - 0
src/views/externalAgreement/agreement/components/contactPersonFeeDialogEdit.vue

@@ -0,0 +1,254 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-drawer v-model="props.ruleFeeVisible" :direction="direction" :close-on-click-modal="false" size="50%">
+      <template #header>
+        <h4>{{ props.headerTitle }}费用编辑</h4>
+      </template>
+      <template #default>
+
+        <ElRow :gutter="20" class="m-top-20">
+          <el-form :model="contactPersonEditInfo" label-width="120px" ref="contactPersonFormRef" style="width: 100%;"
+            :rules="feerules" class="flex a-c f-wrap" :size="fromSize">
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="对接人姓名" prop="contactPerson">
+                <el-input v-model="contactPersonEditInfo.contactPerson" placeholder="请输入对接人姓名" />
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="对接人手机号" prop="contactMobile">
+                <el-input v-model="contactPersonEditInfo.contactMobile" placeholder="请输入手机号" />
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="生效时间" prop="effectiveTime">
+                <el-date-picker v-model="contactPersonEditInfo.effectiveTime" style="width: 100%;" type="datetime"
+                  placeholder="选择生效时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                  time-format="HH:mm:ss" @change="handleStartDateChange($event)" />
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="失效时间" prop="failureTime">
+                <el-date-picker v-model="contactPersonEditInfo.failureTime" style="width: 100%;" type="datetime"
+                  placeholder="选择失效时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
+                  time-format="HH:mm:ss" :disabled-date="(date: any) => disabledEndDate(date)" />
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="审核方式" prop="auditMethod">
+                <el-select v-model="contactPersonEditInfo.auditMethod" placeholder="选择审核方式">
+                  <el-option label="自动审核" value="0" />
+                  <el-option label="手动审核" value="1" />
+                </el-select>
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="12">
+              <el-form-item label="是否现询" prop="isInquiry">
+                <el-radio-group v-model="contactPersonEditInfo.isInquiry">
+                  <el-radio value="0" size="large">是</el-radio>
+                  <el-radio value="1" size="large">否</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </ElCol>
+            <ElCol :md="24" :lg="24">
+              <el-form-item label="费用描述" prop="costDescribe">
+                <el-input v-model="contactPersonEditInfo.costDescribe" maxlength="500" placeholder="请输入费用描述"
+                  show-word-limit type="textarea" />
+              </el-form-item>
+            </ElCol>
+          </el-form>
+        </ElRow>
+
+        <el-table :data="contactPersonEditInfo.costExternalTypeAdds" style="width: 100%;" border
+          :header-cell-style="{ 'background-color': '#F7F7F9', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }">
+          <template #empty>
+            <div class="flex f-c a-c ">
+              <img src="../../assets/images/empty.png" alt="">
+              <span style="font-size: 14px;color: #909399;">暂无数据</span>
+            </div>
+          </template>
+          <el-table-column prop="costType" label="类型"></el-table-column>
+          <el-table-column prop="inlet" label="入口比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.inlet" :precision="2" controls-position="right" style="width: 100%;"
+                :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+          <el-table-column prop="export" label="出口比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.export" :precision="2" controls-position="right" style="width: 100%;"
+                :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+          <el-table-column prop="totalCost" label="总费用比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.totalCost" :precision="2" controls-position="right"
+                style="width: 100%;" :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+          <el-table-column prop="other" label="其他费用比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.other" :precision="2" controls-position="right" style="width: 100%;"
+                :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+          <el-table-column prop="level1" label="1级比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.level1" :precision="2" controls-position="right" style="width: 100%;"
+                :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+          <el-table-column prop="level5" label="5级比例" width="180" align="center">
+            <template #default="scope">
+              <el-input-number v-model="scope.row.level5" :precision="2" controls-position="right" style="width: 100%;"
+                :min="0" :max="100">
+                <template #suffix>%</template>
+              </el-input-number>
+            </template>
+          </el-table-column>
+        </el-table>
+      </template>
+      <template #footer>
+        <div class="flex a-c j-c">
+          <el-button @click="cancel">取消</el-button>
+          <el-button type="primary" @click="save(contactPersonFormRef)">提交审核</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { ref, } from 'vue'
+import { DrawerProps, ComponentSize, FormInstance, FormRules, ElMessage } from 'element-plus';
+import { defineProps, defineEmits } from "vue";
+const direction = ref<DrawerProps['direction']>('rtl')
+const fromSize = ref<ComponentSize>('default')
+const props = defineProps<{
+  ruleFeeVisible: boolean,
+  headerTitle?: string,
+  costitemEditInfo?: any,
+}>();
+const emits = defineEmits(["cancel", 'save']);
+watch(props, () => {
+  contactPersonEditInfo.value = props.costitemEditInfo;
+  console.log(contactPersonEditInfo.value)
+})
+const contactPersonEditInfo = ref();//规则费用集合
+const contactPersonFormRef = ref<FormInstance>()
+const feerules = ref<FormRules>({
+  contactPerson: [
+    { required: true, trigger: 'blur', message: '请输入对接人姓名' },
+  ],
+  contactMobile: [
+    { required: true, trigger: 'blur', message: '请输入手机号' },
+  ],
+  effectiveTime: [
+    { required: true, trigger: 'change', message: '请选择生效时间' },
+  ],
+  failureTime: [
+    { required: true, trigger: 'change', message: '请选择失效时间' },
+  ],
+  auditMethod: [
+    { required: true, trigger: 'change', message: '请选择审核方式' },
+  ],
+  isInquiry: [
+    { required: true, trigger: 'change', message: '请选择是否线询' },
+  ],
+})
+// 禁用失效时间选择
+const disabledEndDate = (date: any) => {
+  let start = contactPersonEditInfo.value.effectiveTime;
+  const startDate = new Date(start);
+  return startDate && date < startDate; // 禁用小于生效时间的日期
+};
+
+// 处理生效时间变化
+/**
+ * @param index  险种下标
+ * @param Costindex 费用下标
+ */
+const handleStartDateChange = (value: any) => {
+  let endDate = contactPersonEditInfo.value.failureTime;
+  // 如果生效时间改变,检查失效时间是否需要更新
+  if (endDate && new Date(endDate) < new Date(value)) {
+    contactPersonEditInfo.value.failureTime = ''; // 清空失效时间
+  }
+};
+const cancel = () => {
+  emits("cancel")
+}
+const save = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      emits('save',contactPersonEditInfo.value)
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+:deep(.el-drawer__header) {
+  padding-top: 0;
+  margin-bottom: 0;
+  border-bottom: 1px solid #f2f2f2;
+}
+
+.agreement {
+  width: 100%;
+  padding: 15px;
+  font-size: 14px;
+  color: #333;
+  border-radius: 4px;
+
+  p {
+    margin: 0;
+    margin-top: 10px;
+  }
+}
+
+.feeItem {
+  width: 100%;
+  border: 1px solid #e5e5e5;
+  border-radius: 8px;
+
+  .header {
+    padding: 12px 14px;
+
+    .title {
+      display: flex;
+
+      &::before {
+        display: inline-block;
+        width: 4px;
+        height: 18px;
+        margin-right: 20px;
+        content: "";
+        background-color: #0083ff;
+      }
+    }
+  }
+}
+
+:deep(.ruleFeetabs>.el-tabs__content) {
+  position: static;
+}
+</style>

+ 110 - 0
src/views/externalAgreement/agreement/components/managerModal.vue

@@ -0,0 +1,110 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-dialog v-model="props.managerModal" title="选择管理人" width="40%"  @close="cancel">
+      <div class="flex a-c">
+        <el-input v-model="searchValue" style="width: 240px;margin-right: 10px;" clearable placeholder="输入姓名,手机号,工号查找" />
+        <el-button type="primary" @click='searchValueclick'>查询</el-button>
+      </div>
+      <div class="deptlist flex ">
+        <div class="area" style="border-right: 1px solid #dcdfe6;" v-show="!searchValueShow">
+          <el-tree style="max-width: 600px;" :data="props.deptOptions" :props="parentIdprops" :highlight-current="true"
+            @node-click="handleNodeClick" />
+        </div>
+        <div class="area">
+          <el-radio-group v-model="userId">
+            <div class="flex f-c">
+              <el-radio :value="item.id" v-for="(item,index) in managerlist" @change="radioChange" :key="index" class="m-btn-20">
+                <div>{{ item.name }}</div>
+                <div>{{ item.id }}</div>
+              </el-radio>
+            </div>
+          </el-radio-group>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="cancel">取消</el-button>
+          <el-button type="primary" @click="save">
+            确定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import api from '@/api';
+import { ref, reactive } from 'vue'
+import { defineProps, defineEmits } from "vue";
+
+interface Tree {
+  id: string
+  children?: Tree[]
+}
+interface managerlist{
+  id:string,
+  name:string,
+  deptName:string,
+}
+const props = defineProps<{
+  managerModal: boolean,
+  deptOptions: string[]
+  managerlist: managerlist[],
+}>();
+const parentIdprops = {
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'name',
+  checkStrictly: true,
+}
+const emits = defineEmits(["cancel", 'save', 'DrawerOpen', 'query', 'NodeClick','radioChange']);
+const userId = ref()
+const username = ref()
+const deptName=ref();
+const searchValue=ref();
+const searchValueShow=ref(false);
+watch(searchValue,()=>{
+  if(!searchValue.value){
+  searchValueShow.value=false;
+  }
+})
+const handleNodeClick = (data: Tree) => {
+  emits('NodeClick', data.id)
+}
+const cancel = () => {
+  emits('cancel')
+}
+const save = () => {
+  emits('save',userId.value,username.value,deptName.value)
+}
+
+const searchValueclick=()=>{
+  searchValueShow.value=true;
+  emits('query',searchValue.value)
+}
+const radioChange=(value:any)=>{
+  let name=props.managerlist.find(val=>val.id==value)?.name;
+  let deptname=props.managerlist.find(val=>val.id==value)?.deptName;
+  username.value=name;
+  deptName.value=deptname;
+}
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+.deptlist {
+  height: 400px;
+  margin-top: 20px;
+  border: 1px solid #dcdfe6;
+  border-radius: 5px;
+
+  .area {
+    flex: 1;
+    height: 100%;
+    padding: 10px;
+    overflow: auto;
+  }
+}
+</style>

+ 522 - 0
src/views/externalAgreement/costManagement.vue

@@ -0,0 +1,522 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <SearchForm :fields="fields" :formInline="formInline" :onSearch="agreementQuery" :onReset="resetForm" />
+      <ComplexTable :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectionChange="selectionChange">
+        <template v-slot:table-title-btn>
+          <el-button type="primary" @click="agreementSelection">
+            添加费用
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+          <el-dropdown placement="bottom-start">
+            <el-button> 批量管理<el-icon>
+                <arrow-down />
+              </el-icon>
+            </el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchDel">删除</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchdisable">禁用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchenable">启用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchUpdate">批量编辑</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button type="primary" link @click="agreemenUpdate(scope.operationData.id)"> 编辑
+          </el-button>
+          <el-dropdown placement="bottom-start" trigger="click">
+            <span class="el-dropdown-link">
+              更 多
+            </span>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item @click="enable(scope.operationData.id)">启用</el-dropdown-item>
+                <el-dropdown-item @click="disable(scope.operationData.id)">禁用</el-dropdown-item>
+                <el-dropdown-item @click="del(scope.operationData.id)">删除</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+      </ComplexTable>
+    </PageMain>
+    <el-dialog v-model="auditMethodVisible" title="选择协议" width="30%">
+
+      <ElRow :gutter="20" class="m-top-20">
+        <el-form :model="Editinfo" ref="FormRef" style="width: 100%;" :rules="rules" class="flex a-c f-wrap"
+          :size="fromSize">
+          <ElCol :md="24" :lg="24">
+            <el-form-item label="审核方式" prop="auditMethod">
+              <el-select v-model="Editinfo.auditMethod" placeholder="选择审核方式">
+                <el-option label="自动审核" value="0" />
+                <el-option label="手动审核" value="1" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="auditMethodVisible = false">取消</el-button>
+          <el-button type="primary" @click="agreementSelectionConfirm(FormRef)">
+            确定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 费用添加组件 -->
+    <contactPersonFeeDialog v-show="addcontactPersonShow" :ruleFeeVisible="addcontactPersonShow"
+      :title='contactPersontitle'   @save="contactPersonSave"
+       addMethod="list" @cancel="addcontactPersonShow = false"  />
+      <!-- 费用编辑组件 -->
+      <contactPersonFeeDialogEdit :ruleFeeVisible="ruleFeeEditVisible" :headerTitle="ruleFeeHeaderTitle"
+       :costitemEditInfo="costitemEditInfo" @save="ruleFeeEditSave"
+      @cancel="ruleFeeEditVisible = false" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage, ElMessageBox, FormRules, FormInstance, ComponentSize } from 'element-plus'
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import contactPersonFeeDialogEdit from "./agreement/components/contactPersonFeeDialogEdit.vue";
+import contactPersonFeeDialog from "./agreement/components/contactPersonFeeDialog.vue";
+
+const FormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')//表单大小
+import api from '@/api';
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+onBeforeMount(async () => {
+  await loadDicts(['valid_status', 'is_enable', 'audit_status', 'insurance_type']);//获取多个字典
+  if (id.value) {
+    formInline.agreement = id.value;
+  }
+  findpage();//列表数据
+  valid_status.value = getDict('valid_status');//生效状态
+  is_enable.value = getDict('is_enable');//协议状态
+  insurance_type.value = getDict('insurance_type')
+  audit_status.value = getDict('audit_status')
+
+});
+const tableData = ref([]);
+const sysUpAndDownList = ref<string[]>([])//选中id
+const insurance_type = ref();
+const valid_status = ref();
+const audit_status = ref();
+const is_enable = ref();
+const route = useRoute();
+const router = useRouter();
+const id = ref(route.query.id as string);//id参数
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 100,
+});
+const formInline = reactive({
+  agreement: '',//协议信息
+  productId: "",//归属
+  rulesName: '',//保险公司id
+  costRules: '',//协议类型
+  agreementStatus: "",//有效状态
+  auditStatus: "",//协议状态
+  validState: "",
+  effectiveTime: [],
+  failureTime: [],
+})
+const rules = ref<FormRules>({
+  auditMethod: [
+    { required: true, trigger: 'blur', message: '请选择审核方式' },
+  ],
+
+})
+//表格字段数据
+const fields = ref([
+  { label: "所属协议", model: "agreement", type: "input", placeholder: "输入代码,名称,简称查找" },
+  {
+    label: "险种", model: "productId", type: "select", options: insurance_type,
+  },
+  {
+    label: "承保规则", model: "rulesName", type: "input", placeholder: "输入业务规则查找",
+  },
+  {
+    label: "费用规则", model: "costRules", type: "input", placeholder: "输入费用规则关键字查找",
+  },
+
+  {
+    label: "费用状态", model: "agreementStatus", type: "select", placeholder: "选择状态", options: is_enable,
+  },
+  {
+    label: "审核状态", model: "auditStatus", type: "select", placeholder: "选择状态", options: audit_status,
+  },
+  {
+    label: "有效状态", model: "validState", type: "select", placeholder: "请选择状态", options: valid_status,
+  },
+  {
+    label: "生效时间", model: "effectiveTime", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      console.log(value)
+      formInline.effectiveTime = value;
+    }
+  },
+  {
+    label: "失效时间", model: "failureTime", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      formInline.failureTime = value;
+    }
+  },
+]);
+
+//费用数据查询
+function findpage() {
+  api.agreementApi.CostExternalFeePageList({ pages: pageInfo.value.pageNum, size: pageInfo.value.pageSize, ...formInline }).then((res: any) => {
+    if (res.code == '200') {
+      tableData.value = res.data.records;
+      pageInfo.value.pageNum = res.data.current;
+      pageInfo.value.pageSize = res.data.size;
+      pageInfo.value.total = res.data.total;
+    } else {
+    }
+  });
+}
+
+const columns = [
+  {
+    prop: "agreementAbbreviation", label: "协议信息", width: '300',
+    formatter: (row: any) => {
+      return `${row.agreementCode}\n${row.agreementAbbreviation}`
+    }
+  },
+  { prop: "productName", label: "险种", width: '120', },
+  {
+    prop: "contactPerson", label: "对接人", width: '200', formatter: (row: any) => {
+      return `${row.contactPerson} ${row.contactMobile}`
+    }
+  },
+  {
+    prop: "isInquiry", label: "是否现询", width: '120', formatter: (row: any) => {
+      if (row.isInquiry == '0') {
+        return '是'
+      } else {
+        return "否"
+      }
+    }
+  },
+  {
+    prop: "auditMethod", label: "审核方式", width: '120', formatter: (row: any) => {
+      if (row.auditMethod == '0') {
+        return '自动审核'
+      } else {
+        return "手动审核"
+      }
+    }
+  },
+  {
+    prop: "isEnabled", label: "费用状态", width: '100', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('is_enable').find(val => val.value == row.isEnabled).label
+      if (name) {
+        return { type: 'primary', text: name };
+      }
+    }
+  },
+  {
+    prop: "auditStatus", label: "审核状态", width: '120', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('audit_status').find(val => val.value == row.auditStatus).label
+      if (name) {
+        return { type: 'warning', text: name };
+      }
+    }
+  },
+  { prop: "effectiveTime", label: "生效时间", width: '200' },
+  { prop: "failureTime", label: "失效时间", width: '200' },
+  {
+    prop: "costExternalTypeAdds", label: "交强险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '交强险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1?.toFixed(2)}% 五级:${info.level5?.toFixed(2)}%`
+      } else {
+        return '-'
+      }
+    }
+  },
+  {
+    prop: "costExternalTypeAdds", label: "商业险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '商业险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1?.toFixed(2)}% 五级:${info.level5?.toFixed(2)}%`
+      } else {
+        return '-'
+      }
+    }
+  },
+  {
+    prop: "costExternalTypeAdds", label: "驾意险费用", width: '300', formatter: (row: any) => {
+      let info = row.costExternalTypeAdds.find((val: any) => val.costType == '驾意险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n一级:${info.level1?.toFixed(2)}% 五级:${info.level5?.toFixed(2)}%`
+      } else {
+        return '-'
+      }
+    }
+  },
+];
+//分页事件
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+
+};
+// 费用添加
+
+const addcontactPersonShow = ref(false);//添加对接人
+const contactPersontitle = ref();//弹窗title
+const agreementSelection = () => {//添加对接人
+  addcontactPersonShow.value = true;
+  contactPersontitle.value = '添加';
+}
+
+//费用提交
+const contactPersonSave = (data: object) => {
+  console.log(data);
+    api.agreementApi.ptlAgreementCostExternalsave(data).then((res: any) => {
+      if (res.code == '200') {
+        addcontactPersonShow.value = false;
+        findpage();
+      } else {
+      }
+    });
+}
+// 费用编辑
+const ruleFeeEditVisible=ref(false);
+const ruleFeeHeaderTitle = ref();//编辑弹窗title
+const costitemEditInfo = ref();
+const agreemenUpdate = async (id: any) => {
+  api.agreementApi.FeegetById(id).then((res: any) => {
+    if (res.code == '200') {
+      ruleFeeHeaderTitle.value = res.data.productName;
+      costitemEditInfo.value = res.data;
+      ruleFeeEditVisible.value = true;
+    } else {
+    }
+  });
+}
+//费用编辑
+const ruleFeeEditSave = (data: object) => {
+  api.agreementApi.FeeupdateById(data).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      ruleFeeEditVisible.value=false;
+      findpage();
+    } else {
+    }
+  });
+}
+//多选事件
+const selectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+//禁用
+const disable = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要禁用该协议吗,禁用后,该协议不在支持报价出单,并且已核保待缴费,待支付的订单,都将失效,请确认是否禁用?',
+    '禁用协议',
+    {
+      confirmButtonText: '确定禁用',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      sysUpAndDown(false, [value]);
+    })
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown(true, [value]);
+}
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该费用吗,删除后,该费用不在支持新的补录订单?',
+    '删除费用',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.deleteById([value]).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.push({
+            name: 'reload',
+          })
+        }
+      });
+    })
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '确定要删除该费用吗,删除后,该费用不在支持新的补录订单?',
+    '删除费用',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.deleteById(sysUpAndDownList.value).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.push({
+            name: 'reload',
+          })
+        }
+      });
+    })
+
+}
+//批量禁用
+const batchdisable = () => {
+  ElMessageBox.confirm(
+    '确定要禁用该费用吗,禁用后,该费用不在支持新的补录订单,请确认是否禁用?',
+    '禁用费用',
+    {
+      confirmButtonText: '确定禁用',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(async () => {
+      await sysUpAndDown(false, undefined);
+    })
+    .catch(() => {
+    })
+
+
+}
+//批量启用
+const batchenable = async () => {
+  await sysUpAndDown(true, undefined);
+}
+const agreementQuery = () => {
+  findpage();
+}
+const sysUpAndDown = async (type: boolean, list?: string[]) => {
+  await api.agreementApi.enableDisableByIds(type, list ? list : sysUpAndDownList.value).then((res: any) => {
+    if (res.code == '200') {
+      findpage();
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+// 批量编辑
+const batchUpdate = () => {
+  auditMethodVisible.value = true;
+
+}
+const resetForm = () => {
+  formInline.agreement = '';//协议信息
+  formInline.productId = "";//归属
+  formInline.rulesName = '';//保险公司id
+  formInline.costRules = '';//协议类型
+  formInline.agreementStatus = "";//有效状态
+  formInline.auditStatus = "";//协议状态
+  formInline.validState = "";
+  formInline.effectiveTime = [];
+  formInline.failureTime = [];
+  findpage();
+}
+//批量修改弹窗
+const auditMethodVisible = ref(false);
+
+const agreementSelectionConfirm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      api.agreementApi.approvalMethodAll(Editinfo.auditMethod, sysUpAndDownList.value).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          auditMethodVisible.value=false;
+          findpage();
+        } else {
+        }
+      });
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+// 批量编辑
+const Editinfo = reactive({
+  auditMethod: "",
+});
+</script>
+
+<style lang="scss" scoped>
+.demo-form-inline .el-input {
+  --el-input-width: 220px;
+}
+
+.demo-form-inline .el-select {
+  --el-select-width: 220px;
+}
+
+:deep(.el-dropdown-link) {
+  display: flex;
+  align-items: center;
+  margin-left: 15px;
+  color: var(--el-color-primary);
+  cursor: pointer;
+}
+
+.el-inputs {
+  width: 100% !important;
+}
+</style>