@dongkboy 1 жил өмнө
parent
commit
809226f24a

+ 22 - 3
src/api/modules/agreement.ts

@@ -16,7 +16,7 @@ const agreementApi = (axiosInstance: ApiInstance) => ({
   //协议授权保存
   authSave: (data: Types.authSaveRequest) => axiosInstance.post('agreement/authSave', data),
   //根据机构获取后线人员列表
-  getUserByDeptId: (data: string) => axiosInstance.get(`agreement/getUserByDeptId?deptId=${data}`),
+  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}`),
   //启用禁用
@@ -39,10 +39,29 @@ const agreementApi = (axiosInstance: ApiInstance) => ({
   //承保规则列表
   agreementRulesList: (id: any) => axiosInstance.get(`agreementRules/rulesList?agreementId=${id}`),
   //承保规则详情
-  agreementRulesGet: (id: any) => axiosInstance.post(`agreementRules/rulesGet?ruleId=${id}`),
+  agreementRulesGet: (id: any) => axiosInstance.get(`agreementRules/rulesGet?ruleId=${id}`),
   //删除承保规则
-  agreementRulesDelete: (id: any) => axiosInstance.delete(`agreementRules/rulesDelete?id=${id}`, ),
+  agreementRulesDelete: (id: any) => axiosInstance.delete(`agreementRules/rulesDelete?id=${id}`,),
 
+  /**
+   *  费用管理接口
+   */
+  //协议费用管理新增
+  ptlAgreementCostsave: (data: any) => axiosInstance.post('ptlAgreementCost/save', data),
+  //协议规则费用修改
+  AgreementFeeupdate: (data: any) => axiosInstance.post('ptlAgreementCost/update', data),
+  //协议规则费用列表查询
+  queryByAgreementId: (data: any) => axiosInstance.post('ptlAgreementCost/queryByAgreementId', data),
+  //费用管理分页列表
+  queryByFeePage: (data: any) => axiosInstance.post('ptlAgreementCost/pageList', data),
+  //协议费用批量删除
+  AgreementFeedeleteByIds: (data: any) => axiosInstance.post('ptlAgreementCost/deleteByIds', data),
+  //协议费用批量启用/禁用
+  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),
 
 })
 export default agreementApi

+ 4 - 4
src/api/modules/institution.ts

@@ -1,7 +1,7 @@
 import { ApiInstance } from '../type'
 interface institutionData {
-  name: string;
-  system: string,
+  name?: string;
+  system?: string,
   [key: string]: any; // 允许其他任意字段
 }
 const institutionApi = (axiosInstance: ApiInstance) => ({
@@ -9,7 +9,7 @@ const institutionApi = (axiosInstance: ApiInstance) => ({
   institutionAdd: (data: institutionData) => axiosInstance.post('/dept/add', data, {}),
 
   //机构树形列表
-  institutionList:(data: institutionData) => axiosInstance.post('/dept/getDeptTree', data, {}),
+  institutionList:(data?: institutionData) => axiosInstance.post('/dept/getDeptTree', data),
 
   //编辑
   institutionEdit: (data: institutionData) => axiosInstance.post('/dept/update', data, {}),
@@ -22,7 +22,7 @@ const institutionApi = (axiosInstance: ApiInstance) => ({
   //获取机构信息
   deptGet: (params: institutionData) => axiosInstance.get('/dept/deptGet', {params})
 
-  
+
 })
 
 export default institutionApi

BIN
src/assets/images/empty.png


BIN
src/assets/images/fee.png


BIN
src/assets/images/pdf.png


+ 513 - 0
src/views/quoteConfig/agreement/agreementAdd.vue

@@ -0,0 +1,513 @@
+<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>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="对接人" prop="dockingPerson">
+              <el-input v-model="agreementFrom.dockingPerson" placeholder="输入对接人姓名">
+              </el-input>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="对接人手机号" prop="dockingPhone">
+              <el-input v-model="agreementFrom.dockingPhone" placeholder="输入对接人手机号" maxlength="11" show-word-limit />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="6">
+            <el-form-item label="出单账号" prop="attributionId">
+              <el-input v-model="agreementFrom.attributionId" placeholder="请选择" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="协议文件">
+              <div class="flex f-c a-start">
+                <el-upload action="" v-if="!imageUrl" :auto-upload="false" :show-file-list="false"
+                  accept=".pdf, .doc, .docx, .xls, .xlsx" @change="uploadImage($event)">
+                  <el-button type="primary" size="default" plain>
+                    <template #icon>
+                      <el-icon>
+                        <Upload />
+                      </el-icon>
+                    </template>
+                    上传</el-button>
+                </el-upload>
+                <div class="agreementNotice flex a-c j-s" v-else>
+                  <div class="flex  a-c">
+                    <img src="../../../assets/images/pdf.png" alt="">
+                    <div class="flex f-c j-s">
+                      <span style="line-height: 19px;">{{ imageName }}</span>
+                      <span style="line-height: 19px;">{{ fileSize }}</span>
+                    </div>
+                  </div>
+                  <div class="fileBtn flex a-c">
+                    <el-upload :show-file-list="false" :auto-upload="false" accept=".pdf, .doc, .docx, .xls, .xlsx"
+                      @change="uploadImage($event)" style="display: flex; align-items: center;">
+                      <el-icon class="btn-icon">
+                        <Refresh />
+                      </el-icon>
+                    </el-upload>
+                    <el-icon class="btn-icon" @click="Fileonload(imageUrl, imageName)">
+                      <Download />
+                    </el-icon>
+                    <el-icon class="btn-icon" @click="deleteImage">
+                      <Delete />
+                    </el-icon>
+                  </div>
+                </div>
+                <span style="font-size: 14px;color: #999;">支持pdf,doc,docx,xls,xlsx格式文件,最大不超过50MB</span>
+              </div>
+            </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 { Fileonload, formatFileSize, processFileName } from '@/utils/composables/fileProcessing';//文件处理工具
+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
+  dockingPerson: string; // 对接人
+  dockingPhone: string; // 对接人手机号
+  attributionId: string; // 出单账号 ID
+  fileId?: 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 imageName = ref('');//文件名称
+const imageUrl = ref('');//文件地址
+const fileSize = ref();//文件大小
+const agreementFrom: AgreementForm = reactive({
+  id: "",
+  agreementTitle: "",//协议名称
+  agreementAbbreviation: "",//协议简称
+  startDate: "",//生效时间
+  endDate: "",//失效时间
+  withTax: "",//是否含增值税结算
+  moneyTime: 0,//结算周期
+  agreementName: "",//协议描述
+  deptId: "",//协议归属
+  companyId: "",//保险公司id
+  agreementType: "",//协议类型
+  custodianId: "",//管理人id
+  dockingPerson: "",//对接人
+  dockingPhone: "",//对接人手机号
+  attributionId: "",//出单账号id
+  fileId: '',//文件
+})
+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 uploadImage = async (file: any) => {
+  let files = file.raw
+  const params = new FormData(); // 声明formData数据类型
+  params.append("file", files);
+  api.commonApi.fileUpload(params).then((res: any) => {
+    if (res.code == '200') {
+      imageName.value = res.data.fileName;
+      imageUrl.value = res.data.downloadUrl;
+      imageName.value = processFileName(res.data.fileName)
+      fileSize.value = formatFileSize(res.data.fileSize)
+      agreementFrom.fileId = res.data.id;
+    } else {
+    }
+  });
+}
+// 图片删除
+const deleteImage = () => {
+  imageName.value = ''
+  imageUrl.value = ''
+}
+//嫌憎编辑提交事件
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (id.value) {
+        api.agreementApi.agreementUpdate({ ...agreementFrom }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back()
+          } else {
+          }
+        });
+      } else {
+        api.agreementApi.agreementAdd({ ...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>

+ 844 - 0
src/views/quoteConfig/agreement/agreementDetails.vue

@@ -0,0 +1,844 @@
+<!-- 模板区域 -->
+<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-item label="出单账号:"> {{ agreementInfo.attributionId }} </el-descriptions-item>
+          </el-descriptions>
+          <span class="strong" style="font-size: 16px;color: #303133;">协议文件</span>
+          <span class="strong m-top-20 m-btn-10" style="display: block;">协议费用政策告知单</span>
+          <div class="agreementNotice flex a-c j-s">
+            <div class="flex  a-c">
+              <img src="../../../assets/images/pdf.png" alt="">
+              <div class="flex f-c j-s">
+                <span>{{ agreementInfo.fileName }}</span>
+                <span>{{ agreementInfo.fileSize }}</span>
+              </div>
+            </div>
+            <div class="fileBtn flex a-c">
+              <el-upload :show-file-list="false" :auto-upload="false" accept=".pdf, .doc, .docx, .xls, .xlsx"
+                @change="uploadImage($event)" style="display: flex; align-items: center;">
+                <el-icon class="btn-icon">
+                  <Refresh />
+                </el-icon>
+              </el-upload>
+              <el-icon class="btn-icon" @click="Fileonload(agreementInfo.fileUrl, agreementInfo.fileName)">
+                <Download />
+              </el-icon>
+            </div>
+          </div>
+        </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="RulesList">
+            <div class="list-item" v-for="(item, index) in RulesList" :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 style="margin-right: 30px;font-size: 16px;" class="strong m-left-10">承保规则一{{ index + 1
+                      }}</span>
+                  </div>
+                  <div class="flex">
+                    <el-button link type="primary" :icon="Delete" @click="ruleDel(item.id)">删除</el-button>
+                    <el-button link type="primary" :icon="Edit" @click="ruleEdit(item.id)">编辑</el-button>
+                    <el-button v-if="!item.ruleFeeShow" link type="default"
+                      @click="item.ruleFeeShow = !item.ruleFeeShow">展开<el-icon class="m-left-10">
+                        <ArrowDownBold />
+                      </el-icon> </el-button>
+                    <el-button v-else link type="default" @click="item.ruleFeeShow = !item.ruleFeeShow">收起<el-icon
+                        class="m-left-10">
+                        <ArrowUpBold />
+                      </el-icon></el-button>
+                  </div>
+                </div>
+                <p class=" m-top-10" style="font-size: 14px;">规则描述:{{ item.ruleDescription }}</p>
+              </div>
+              <div class="header-content" v-if="item.ruleFeeShow">
+                <el-button plain type="primary" @click=" ruleFeeAdd(item)" class="m-top-20 m-btn-20">添加费用
+                  <template #icon>
+                    <el-icon>
+                      <Plus />
+                    </el-icon>
+                  </template>
+                </el-button>
+                <el-tabs :tab-position="ruleFeePosition" class="ruleFee" v-if="item.costVoList">
+                  <el-tab-pane v-for="(costitem, costindex) in item.costVoList" :key="costindex"
+                    :label="costitem.productName" :name="costitem.productName">
+                    <span class="strong">单交费用({{ costitem.ptlAgreementCostVoList.length }}条)</span>
+                    <ComplexTable :tableData="costitem.ptlAgreementCostVoList" :columns="columns" :maxHeight="250"
+                      :showIndex="false" :columnwidth="200" :showSelect="false">
+                      <template v-slot:operation="scope">
+                        <el-button type="primary" link @click="costitemEdit(scope.operationData.id, item)"> 编辑
+                        </el-button>
+                        <el-button type="primary" link @click="costitemDel(scope.operationData.id)"> 删除
+                        </el-button>
+                      </template>
+                    </ComplexTable>
+                  </el-tab-pane>
+                </el-tabs>
+                <Empty v-else />
+              </div>
+            </div>
+          </div>
+          <Empty v-else />
+        </el-tab-pane>
+      </el-tabs>
+    </PageMain>
+    <el-drawer v-model="addUnderwritingRuleShow" :direction="direction" :close-on-click-modal="false" size="50%">
+      <template #header>
+        <h4>添加承保规则</h4>
+      </template>
+      <template #default>
+        <div class="flex a-c j-s ">
+          <span class="strong title" style="margin-right: 20px;">规则条件</span>
+          <el-button type="primary" link @click="ruleConditionVisible = true">
+            添加条件
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+        </div>
+        <!-- 规则选择容器 -->
+        <rule-Condition-Container :rulesAttrList="rulesAttrListInfo.rulesAttrList" @Del="rulesAttrDel"
+          @selectChange="rulesAttrselectChange" />
+      </template>
+      <template #footer>
+        <div class="flex a-c j-c">
+          <el-button @click="addUnderwritingRuleShow = false">取消</el-button>
+          <el-button type="primary" @click="UnderwritingRuleSave">保存</el-button>
+        </div>
+      </template>
+    </el-drawer>
+    <!-- 协议授权弹窗 -->
+    <auth-Drawer :Drawer="agreementAuthDrawer" @cancel="agreementAuthDrawer = false" @save="DrawerSave"
+      @query="Drawerquery" :deptlist="deptauthList" :checkedKeys="checkedKeys" :checkedIds="checkedIds"></auth-Drawer>
+    <!-- 规则条件弹窗 -->
+    <RuleCondition v-if="ruleConditionVisible" :ruleConditionVisible="ruleConditionVisible"
+      @cancel="ruleConditionVisible = false" @save="ruleConditionSave" :attrIdList="rulesAttrListInfo.rulesAttrList" />
+    <!-- 规则费用抽屉组件 -->
+    <ruleFeeDialog v-if="ruleFeeVisible" :ruleFeeVisible="ruleFeeVisible" :agreementInfo="agreementInfo"
+      :RulesInfo="RulesInfo" @save="ruleFeeSave" @cancel="ruleFeeVisible = false" />
+    <!-- 规则费用编辑组件 -->
+    <ruleFeeDialogEdit :ruleFeeVisible="ruleFeeEditVisible" :headerTitle="ruleFeeHeaderTitle"
+      :agreementInfo="agreementInfo" :RulesInfo="RulesInfo" :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, DrawerProps, TabsInstance } from 'element-plus'
+import { ref, reactive } from 'vue'
+import { useRoute, useRouter } from 'vue-router';
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import { Fileonload, formatFileSize,processFileName } from '@/utils/composables/fileProcessing';//文件处理工具
+import authDrawer from "./components/agreementAuth.vue";
+import ruleFeeDialog from "./components/ruleFeeDialog.vue";
+import ruleFeeDialogEdit from "./components/ruleFeeDialogEdit.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('agreementInfo')//默认标签页
+const authDeptlength = ref<number>(0);
+onBeforeMount(async () => {
+  await loadDicts(['agreement_type', 'valid_status', 'is_enable', 'with_tax', 'audit_status']);//获取多个字典
+
+  if (id.value) {
+    detailsInfo();//协议信息
+    agreementRulesList();//承保规则
+    regionLicenseRegion('0');//车牌地区
+    regionLicenseNumber('14');//车牌号
+    rulesAttrListInfo.agreementId = id.value;//赋值协议id
+  }
+})
+//协议详情
+const detailsInfo = () => {
+  api.agreementApi.agreementGet(id.value).then((res: any) => {
+    if (res.code == '200') {
+      res.data.agreement.fileName=processFileName(res.data.agreement.fileName)
+      res.data.agreement.fileSize=formatFileSize(res.data.agreement.fileSize);
+      agreementInfo.value = res.data.agreement;
+
+      authDeptlength.value = res.data.dept.length;
+      imageUrl.value = res.data.agreement.fileUrl;
+    } else {
+    }
+  });
+}
+
+//编辑跳转
+const agreementEdit = () => {
+  router.push({
+    name: 'AgreementAdd',
+    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 licensePlateRegion = ref();//车牌地区
+const licensePlateNumber = ref();//车牌号
+const regionLicenseRegion = (value: string) => {
+  api.agreementApi.getAreaLicense(value).then((res: any) => {
+    if (res.code == '200') {
+      let data: any = [];
+      res.data.map((val: any) => {
+        data.push({
+          code: val.areaNumber,
+          name: val.license,
+        })
+        return val;
+      })
+      licensePlateRegion.value = data;
+    } else {
+    }
+  });
+}
+const regionLicenseNumber = (value: string) => {
+  api.agreementApi.getAreaLicense(value).then((res: any) => {
+    if (res.code == '200') {
+      let data: any = [];
+      res.data.map((val: any) => {
+        data.push({
+          code: val.areaNumber,
+          name: val.license,
+        })
+        return val;
+      })
+      licensePlateNumber.value = data;
+    } else {
+    }
+  });
+}
+//  添加承保规则
+const RulesList = ref();//承保规则数据
+const addUnderwritingRuleShow = ref(false);//添加规则抽屉
+
+const ruleFeePosition = ref<TabsInstance['tabPosition']>('left')
+const direction = ref<DrawerProps['direction']>('rtl')
+const addUnderwritingRule = () => {
+  addUnderwritingRuleShow.value = true;
+}
+//承保规则保存事件
+const UnderwritingRuleSave = () => {
+  if (rulesAttrListInfo.id) {//判断id是否存在
+    api.agreementApi.agreementRulesEdit(rulesAttrListInfo).then((res: any) => {
+      if (res.code == '200') {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+        addUnderwritingRuleShow.value = false;
+        router.push({
+          name: 'reload',
+        })
+      } else {
+      }
+    });
+  } else {
+    api.agreementApi.agreementRulesAdd(rulesAttrListInfo).then((res: any) => {
+      if (res.code == '200') {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+        addUnderwritingRuleShow.value = false;
+        router.push({
+          name: 'reload',
+        })
+      } else {
+      }
+    });
+  }
+
+}
+//获取承保规则列表
+const agreementRulesList = () => {
+  api.agreementApi.agreementRulesList(id.value).then((res: any) => {
+    if (res.code == '200') {
+      res.data.map((val: any) => {
+        val.ruleFeeShow = false;
+        return val;
+      })
+      RulesList.value = res.data;
+
+    } else {
+    }
+  });
+}
+//修改规则
+const ruleEdit = (id: string) => {
+  rulesAttrListInfo.id = id;
+  addUnderwritingRuleShow.value = true;
+  api.agreementApi.agreementRulesGet(id).then((res: any) => {
+    if (res.code == '200') {
+      rulesAttrListInfo.rulesAttrList = processArray(res.data.costsAttrLinks);
+    } else {
+    }
+  });
+}
+//添加车牌和车牌地区
+const processArray = (array: any[]) => {
+  return array.map((val) => {
+    switch (val.attrType) {
+      case 'inputNumber':
+      case 'datetime':
+        if (!val.operator) {
+          val.operator = '1'; // 设置默认值
+        }
+        break;
+      default:
+        break;
+    }
+    if (val.attrCode == 'LicenseNo') {
+      val.dictLabal = licensePlateNumber.value;
+    }
+    if (val.attrCode == 'LicenseArea') {
+      val.dictLabal = licensePlateRegion.value;
+    }
+    return val;
+  });
+};
+//删除规则
+const ruleDel = (id: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该承保规则吗,删除后,该承保规则的费用也将同步删除,请确认是否删除?',
+    '删除承保规则',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.agreementRulesDelete(id).then((res: any) => {
+        if (res.code == '200') {
+          agreementRulesList();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        }
+      });
+    })
+}
+//添加费用
+const ruleFeeVisible = ref(false);//规则费用弹窗
+const ruleFeeEditVisible = ref(false);//编辑弹窗
+const ruleFeeHeaderTitle = ref();//编辑弹窗title
+const costitemEditInfo = ref();
+const RulesInfo = ref({});
+const columns = [
+  { prop: "costRules", label: "费用规则", width: '200', },
+  {
+    prop: "ptlAgreementCostTypeList", label: "交强险费用", width: '300', formatter: (row: any) => {
+      let info = row.ptlAgreementCostTypeList.find((val: any) => val.costType == '交强险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      }
+    }
+  },
+  {
+    prop: "agreementAbbreviation", label: "商业险费用", width: '300', formatter: (row: any) => {
+      let info = row.ptlAgreementCostTypeList.find((val: any) => val.costType == '商业险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      }
+    }
+  },
+  {
+    prop: "deptName", label: "驾意险费用", width: '300', formatter: (row: any) => {
+      let info = row.ptlAgreementCostTypeList.find((val: any) => val.costType == '驾意险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      }
+    }
+  },
+  {
+    prop: "auditMethod", label: "审核方式", width: '200',
+    formatter: (row: any) => {
+      if (row.auditMethod == '0') {
+        return '自动审核'
+      } 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: "approved", label: "审核状态", width: '200', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('audit_status').find(val => val.value == row.approved).label
+      if (name) {
+        return { type: 'warning', text: name };
+      }
+    }
+  },
+  {
+    prop: "isValid", label: "有效状态", width: '200',
+    component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('valid_status').find(val => val.value == row.isValid).label
+      if (name) {
+        return { type: 'danger', text: name };
+      }
+    }
+  },
+];
+const ruleFeeAdd = (item: any) => {
+  RulesInfo.value = item;
+  ruleFeeVisible.value = true;
+}
+//费用提交
+const ruleFeeSave = (data: object) => {
+
+  api.agreementApi.ptlAgreementCostsave(data).then((res: any) => {
+    if (res.code == '200') {
+      ruleFeeVisible.value = false;
+    } else {
+    }
+  });
+}
+const ruleFeeEditSave = (data: object) => {
+  api.agreementApi.AgreementFeeupdate(data).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    } else {
+    }
+  });
+}
+//编辑删除费用
+const costitemEdit =  (id: string, item: any) => {
+  RulesInfo.value = item;
+   api.agreementApi.AgreementFeeById(id).then((res: any) => {
+    if (res.code == '200') {
+      res.data.ruleConditions = processArray(res.data.ruleConditions);
+      ruleFeeHeaderTitle.value = res.data.productName;
+      costitemEditInfo.value = res.data;
+      ruleFeeEditVisible.value = true;
+    } else {
+    }
+  });
+}
+
+//删除费用
+const costitemDel = (id: string) => {
+  ElMessageBox.confirm(
+    '确定要删除该费用,请确认是否删除?',
+    '删除费用',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.agreementApi.AgreementFeedeleteByIds([id]).then((res: any) => {
+        if (res.code == '200') {
+          agreementRulesList();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        } else {
+        }
+      });
+    })
+}
+interface rulesAttrListInfo {
+  id: string,
+  agreementId: string,
+  rulesAttrList: any[],
+}
+const ruleConditionVisible = ref(false);//规则条件弹窗
+const rulesAttrListInfo = reactive<rulesAttrListInfo>({
+  id: '',
+  agreementId: "",
+  rulesAttrList: []
+});//规则条件列表数据
+const ruleConditionSave = (list: string[]) => {//获取规则条件选中集合
+
+  rulesAttrListInfo.rulesAttrList = list;
+  ruleConditionVisible.value = false;
+}
+//条件删除
+const rulesAttrDel = (index: number) => {
+  rulesAttrListInfo.rulesAttrList.splice(index, 1)
+}
+//多选框事件数据格式处理
+const rulesAttrselectChange = (item: any) => {
+  rulesAttrListInfo.rulesAttrList[item.index].min = '';
+  item.e.map((ele: any) => {
+    rulesAttrListInfo.rulesAttrList[item.index].min += ele + ',';
+  })
+  // 去掉最后一个逗号
+  if (rulesAttrListInfo.rulesAttrList[item.index].min.endsWith(',')) {
+    rulesAttrListInfo.rulesAttrList[item.index].min = rulesAttrListInfo.rulesAttrList[item.index].min.slice(0, -1);
+  }
+}
+//协议文件处理
+//上传
+const uploadImage =  (file: any) => {
+  let files = file.raw
+  const params = new FormData(); // 声明formData数据类型
+  params.append("file", files);
+  api.commonApi.fileUpload(params).then((res: any) => {
+    if (res.code == '200') {
+      // imageName.value = processFileName(res.data.fileName);
+      imageUrl.value = res.data.downloadUrl;
+      api.agreementApi.agreementEditFile({ id: id.value, fileId: res.data.id }).then((res: any) => {
+        if (res.code == '200') {
+
+        }
+      });
+    } 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>

+ 486 - 11
src/views/quoteConfig/agreement/agreementIndex.vue

@@ -1,17 +1,492 @@
-<!-- 模板区域 -->
 <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: 'AgreementAdd' }">
+            <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">复制协议</el-dropdown-item>
+                <el-dropdown-item @click="batchAuthorization(scope.operationData.id)">协议授权</el-dropdown-item>
+                <el-dropdown-item @click="agreemenUpdate(scope.operationData)">费用管理</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: "",
+})
+
+//表格字段数据
+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: "dockingPerson", type: "input", placeholder: "输入姓名,手机号查找"
+  },
+  {
+    label: "出单账号", model: "attributionName", type: "input", placeholder: "选择出单账号"
+  },
+  {
+    label: "生效时间", model: "takeStartDate", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      formInline.takeStartDate = value[0];
+      formInline.takeEndDate = value[1];
+    }
+  },
+  {
+    label: "失效时间", model: "loseStartDate", 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: "attributionId", label: "出单账号", width: '120', },
+  { prop: "company", label: "保险公司", width: '200' },
+  { prop: "dockingPerson", 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' },
+  { prop: "fileName", 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: 'AgreementDetails',
+    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 = () => {
+
+}
+//禁用
+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 subOrganizationAdd = (code: string) => {
+  router.push({
+    name: 'OrganizationDeptAdd',
+    query: {
+      code: code
+    }
+  })
+}
+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/quoteConfig/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>

+ 110 - 0
src/views/quoteConfig/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>

+ 431 - 0
src/views/quoteConfig/agreement/components/ruleFeeDialog.vue

@@ -0,0 +1,431 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-drawer v-model="props.ruleFeeVisible" :direction="direction" :close-on-click-modal="false" size="57%">
+      <template #header>
+        <h4>添加费用</h4>
+      </template>
+      <template #default>
+        <div class="agreement ">
+          <ElRow :gutter="20">
+            <ElCol :md="10"><span><span style="color: #666;">协议号:</span> {{ props.agreementInfo.agreementCode }}</span>
+            </ElCol>
+            <ElCol :md="10"><span><span style="color: #666;">协议名称:</span>{{ props.agreementInfo.agreementTitle }}</span>
+            </ElCol>
+            <ElCol :md="24">
+              <p><span style="color: #666;">核保规则:</span>{{ props.RulesInfo.ruleDescription }}</p>
+            </ElCol>
+          </ElRow>
+        </div>
+        <div class="flex f-c m-top-20">
+          <!-- <span class="strong">险种</span> -->
+          <el-tabs v-model="activeName" type="card" @tab-click="hledclick" style="position: relative;"
+            class="ruleFeetabs">
+            <el-button type="default" @click="feeItemAdd(insuranceTypeIndex)"
+              style="position: absolute;top: 0;right: 0;z-index: 1;">
+              添加费用
+              <template #icon>
+                <el-icon>
+                  <Plus />
+                </el-icon>
+              </template>
+            </el-button>
+            <el-tab-pane v-for="(item, index) in ruleFeeinfo" :key="index" :label="item.productName"
+              :name="item.productName">
+              <div style="max-height: 590px;overflow-y: auto;" v-if="item.ptlAgreementCostAddList.length">
+                <div class="feeItem m-top-20" v-for="(Costitem, Costindex) in item.ptlAgreementCostAddList"
+                  :key="Costindex">
+                  <div class="flex j-s a-c header">
+                    <span class="strong title">出单费用{{ Costindex + 1 }}</span>
+                    <div class="flex a-c">
+                      <el-icon style=" margin-right: 10px;color: #666;" @click="feeItemDel(index, Costindex)">
+                        <Delete />
+                      </el-icon>
+                      <el-button v-if="!Costitem.ruleFeeShow" link type="default"
+                        @click="Costitem.ruleFeeShow = !Costitem.ruleFeeShow">展开<el-icon class="m-left-10">
+                          <ArrowDownBold />
+                        </el-icon> </el-button>
+                      <el-button v-else link type="default"
+                        @click="Costitem.ruleFeeShow = !Costitem.ruleFeeShow">收起<el-icon class="m-left-10">
+                          <ArrowUpBold />
+                        </el-icon></el-button>
+                    </div>
+                  </div>
+                  <div style="max-height: 500px;padding: 20px 14px;overflow-y: auto;border-top: 1px solid #e5e5e5;"
+                    v-if="Costitem.ruleFeeShow">
+                    <ElRow :gutter="20" class="m-top-20">
+                      <el-form :model="Costitem" :id="Costitem.fromID" 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="Costitem.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, Costindex)" />
+                          </el-form-item>
+                        </ElCol>
+                        <ElCol :md="24" :lg="12">
+                          <el-form-item label="失效时间" prop="failureTime">
+                            <el-date-picker v-model="Costitem.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, Costindex)" />
+                          </el-form-item>
+                        </ElCol>
+                        <ElCol :md="24" :lg="12">
+                          <el-form-item label="审核方式" prop="auditMethod">
+                            <el-select v-model="Costitem.auditMethod" placeholder="选择审核方式">
+                              <el-option label="自动审核" value="0" />
+                              <el-option label="手动审核" value="1" />
+                            </el-select>
+                          </el-form-item>
+                        </ElCol>
+                        <ElCol :md="24" :lg="24">
+                          <el-form-item label="费用描述" prop="costDescribe">
+                            <el-input v-model="Costitem.costDescribe" maxlength="500" placeholder="请输入费用描述"
+                              show-word-limit type="textarea" />
+                          </el-form-item>
+                        </ElCol>
+                      </el-form>
+                    </ElRow>
+                    <!-- 规则选择容器 -->
+                    <div class="flex a-start">
+                      <ElCol :md="24" :lg="2">
+                        <span class="strong m-right-20">规则条件</span>
+                      </ElCol>
+                      <ElCol :md="24" :lg="22">
+                        <el-button type="default" @click="addRuleCondition(index, Costindex)">
+                          添加条件
+                          <template #icon>
+                            <el-icon>
+                              <Plus />
+                            </el-icon>
+                          </template>
+                        </el-button>
+                        <rule-Condition-Container :rulesAttrList="Costitem.ruleConditions" @Del="rulesAttrDel"
+                          @selectChange="rulesAttrselectChange" />
+                      </ElCol>
+                    </div>
+                    <el-table :data="Costitem.ptlAgreementCostTypeList" 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="commission" label="手续费比例" width="180" align="center">
+                        <template #default="scope">
+                          <el-input-number v-model="scope.row.commission" :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="addThrow" label="加投比例" width="180" align="center">
+                        <template #default="scope">
+                          <el-input-number v-model="scope.row.addThrow" :precision="2" controls-position="right"
+                            style="width: 100%;" :min="0" :max="100">
+                            <template #suffix>%</template>
+                          </el-input-number>
+                        </template>
+                      </el-table-column>
+                    </el-table>
+                  </div>
+                </div>
+              </div>
+              <Empty v-else />
+            </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>
+    <!-- 规则条件弹窗 -->
+    <RuleCondition :ruleConditionVisible="ruleConditionVisible" @cancel="ruleConditionVisible = false"
+      @save="ruleConditionSave" :attrIdList="echoResponse" />
+  </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";
+const direction = ref<DrawerProps['direction']>('rtl')
+const fromSize = ref<ComponentSize>('default')
+
+const activeName = ref('单交')
+const props = defineProps<{
+  ruleFeeVisible: boolean,
+  agreementInfo: any,
+  RulesInfo: any,
+}>();
+const emits = defineEmits(["cancel", 'save']);
+const ruleFeeinfo = ref();//规则费用集合
+const ruleConditionVisible = ref(false);//规则条件弹窗
+const insuranceTypeIndex = ref(0);//险种区域下标
+const feeIndex = ref(0);//费用区域下标
+
+const feeFormRef = ref<FormInstance>()
+const feerules = ref<FormRules>({
+  effectiveTime: [
+    { required: true, trigger: 'change', message: '请选择生效时间' },
+  ],
+  failureTime: [
+    { required: true, trigger: 'change', message: '请选择失效时间' },
+  ],
+  auditMethod: [
+    { required: true, trigger: 'change', message: '请选择审核方式' },
+  ],
+
+})
+onMounted(async () => {
+  await loadDicts(['insurance_type'])
+  const dict = getDict('insurance_type');
+  ruleFeeinfo.value = dict.map((val: any) => {
+    return {
+      productName: val.label,
+      productId: val.value,
+      ptlAgreementId: "",//协议id
+      coverRuleId: "",//规则id
+      ptlAgreementCostAddList: [],//费用集合
+    }
+  })
+})
+// 禁用失效时间选择
+const disabledEndDate = (date: any, index: number, Costindex: number) => {
+  let start = ruleFeeinfo.value[index].ptlAgreementCostAddList[Costindex]?.effectiveTime;
+  const startDate = new Date(start);
+  return startDate && date < startDate; // 禁用小于生效时间的日期
+};
+
+// 处理生效时间变化
+/**
+ * @param index  险种下标
+ * @param Costindex 费用下标
+ */
+const handleStartDateChange = (value: any, index: number, Costindex: number) => {
+  let endDate = ruleFeeinfo.value[index].ptlAgreementCostAddList[Costindex]?.failureTime;
+  // 如果生效时间改变,检查失效时间是否需要更新
+  if (endDate && new Date(endDate) < new Date(value)) {
+    ruleFeeinfo.value[index].ptlAgreementCostAddList[Costindex].failureTime = ''; // 清空失效时间
+  }
+};
+const echoResponse = ref();//
+//条件删除
+const rulesAttrDel = (index: number) => {
+  ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions.splice(index, 1)
+}
+//多选框事件数据格式处理
+const rulesAttrselectChange = (item: any) => {
+  ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions[item.index].min = "";
+  item.e.map((ele: any) => {
+    ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions[item.index].min += ele + ',';
+  })
+  // 去掉最后一个逗号
+  if (ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions[item.index].min.endsWith(',')) {
+    ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions[item.index].min = ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions[item.index].min.slice(0, -1);
+  }
+}
+//获取到的规则条件列表数据
+const ruleConditionSave = (list: string[]) => {//获取规则条件选中集合
+  ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions = list;
+  echoResponse.value = ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[feeIndex.value].ruleConditions;
+  ruleConditionVisible.value = false;
+}
+const cancel = () => {
+  emits("cancel")
+}
+
+
+
+const save = async (formEl: FormInstance | undefined | any) => {
+  if (!formEl) return
+  const validationPromises = ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList.map((formRef: any, index: any) => {
+    // 轮询验证所有表单校验
+    return new Promise((resolve, reject) => {
+      if (formRef) {
+        formEl[index].validate((valid: boolean) => {
+          if (valid) {
+            if (!ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[index].ruleConditions.length) {
+              ElMessage({
+                type: 'error',
+                message: `${ruleFeeinfo.value[insuranceTypeIndex.value].productName}费用${index + 1}至少添加一条规则`,
+                plain: true,
+              });
+            } else {
+              resolve(true);
+            }
+          } else {
+            reject(index);
+          }
+        });
+      } else {
+        reject(index);
+      }
+    });
+  });
+
+  try {
+    await Promise.all(validationPromises);
+    // 所有表单验证通过后执行提交逻辑
+    ruleFeeinfo.value.map((val: any) => {
+      val.ptlAgreementId = props.agreementInfo.id;
+      val.coverRuleId = props.RulesInfo.id;
+      return val;
+    })
+    emits("save", ruleFeeinfo.value)
+  } catch (error) {
+    const invalidIndex: any = error; // 获取未通过校验的表单索引
+    // const invalidForm = ruleFeeinfo.value[insuranceTypeIndex.value].ptlAgreementCostAddList[invalidIndex]; // 获取未通过校验的表单对象
+    const invalidFormElement = document.getElementById(`from-${insuranceTypeIndex.value}-${invalidIndex}`); // 获取未通过校验的表单元素
+    if (invalidFormElement) {
+      invalidFormElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); // 定位到未通过校验的表单区域
+    }
+    ElMessage({
+      type: 'error',
+      message: `${ruleFeeinfo.value[insuranceTypeIndex.value].productName}费用${invalidIndex + 1}未填写完整`,
+      plain: true,
+    });
+  }
+};
+//添加规则条件
+const addRuleCondition = (index: number, Costindex: number) => {
+  insuranceTypeIndex.value = index;
+  feeIndex.value = Costindex;
+  ruleConditionVisible.value = true;
+}
+//对应险种下添加费用
+const feeItemAdd = (index: number) => {
+  let length = ruleFeeinfo.value[index].ptlAgreementCostAddList.length;
+  const filteredList: any = filterCostTypeList(activeName.value);//根据险种匹配列表
+  let list = JSON.parse(JSON.stringify(filteredList)); // 深拷贝
+  insuranceTypeIndex.value = index;
+  ruleFeeinfo.value[index].ptlAgreementCostAddList.push({
+    fromID: `from-${index}-${length}`,
+    effectiveTime: "",//生效时间
+    failureTime: "",//生效时间
+    auditMethod: "",//审核方式
+    costDescribe: "",//费用描述
+    ruleConditions: [],//规则集合
+    ptlAgreementCostTypeList: list,
+    ruleFeeShow: length ? false : true,
+
+  })
+}
+// 过滤方法
+const ptlAgreementCostTypeList = [
+  {
+    costType: "交强险",
+    inlet: 0,
+    export: 0,
+    commission: 0,
+    addThrow: 0,
+  },
+  {
+    costType: "商业险",
+    inlet: 0,
+    export: 0,
+    commission: 0,
+    addThrow: 0,
+  },
+  {
+    costType: "驾意险",
+    inlet: 0,
+    export: 0,
+    commission: 0,
+    addThrow: 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,不包含该项
+    }
+    return true; // 其他情况返回 true,保留该项
+  });
+};
+const hledclick = (pane: any) => {
+  insuranceTypeIndex.value = pane.index;
+  activeName.value = pane.props.name;
+}
+// 费用删除
+const feeItemDel = (index: number, Costindex: number) => {
+  ruleFeeinfo.value[index].ptlAgreementCostAddList.splice(Costindex, 1);
+}
+</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>

+ 256 - 0
src/views/quoteConfig/agreement/components/ruleFeeDialogEdit.vue

@@ -0,0 +1,256 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <el-drawer v-model="props.ruleFeeVisible" :direction="direction" :close-on-click-modal="false" size="57%">
+      <template #header>
+        <h4>{{ props.headerTitle }}费用编辑</h4>
+      </template>
+      <template #default>
+        <div class="agreement ">
+          <ElRow :gutter="20">
+            <ElCol :md="10"><span><span style="color: #666;">协议号:</span> {{ props.agreementInfo.agreementCode }}</span>
+            </ElCol>
+            <ElCol :md="10"><span><span style="color: #666;">协议名称:</span>{{ props.agreementInfo.agreementTitle }}</span>
+            </ElCol>
+            <ElCol :md="24">
+              <p><span style="color: #666;">核保规则:</span>{{ props.RulesInfo.ruleDescription }}</p>
+            </ElCol>
+          </ElRow>
+          <ElRow :gutter="20" class="m-top-20">
+            <el-form :model="costitemEditInfo" label-width="80px" ref="productFormRef" style="width: 100%;"
+              class="flex a-c f-wrap" :size="fromSize">
+              <ElCol :md="24" :lg="12">
+                <el-form-item label="生效时间" prop="effectiveTime">
+                  <el-date-picker v-model="costitemEditInfo.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="costitemEditInfo.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="costitemEditInfo.auditMethod" placeholder="选择审核方式">
+                    <el-option label="自动审核" value="0" />
+                    <el-option label="手动审核" value="1" />
+                  </el-select>
+                </el-form-item>
+              </ElCol>
+              <ElCol :md="24" :lg="24">
+                <el-form-item label="费用描述" prop="costDescribe">
+                  <el-input v-model="costitemEditInfo.costDescribe" maxlength="500" placeholder="请输入费用描述"
+                    show-word-limit type="textarea" />
+                </el-form-item>
+              </ElCol>
+            </el-form>
+          </ElRow>
+          <!-- 规则选择容器 -->
+          <div class="flex a-start">
+            <ElCol :md="24" :lg="2">
+              <span class="strong m-right-20">规则条件</span>
+            </ElCol>
+            <ElCol :md="24" :lg="22">
+              <el-button type="primary" plain @click="ruleConditionVisible = true">
+                添加条件
+                <template #icon>
+                  <el-icon>
+                    <Plus />
+                  </el-icon>
+                </template>
+              </el-button>
+              <rule-Condition-Container :rulesAttrList="costitemEditInfo.ruleConditions" @Del="rulesAttrDel"
+                @selectChange="rulesAttrselectChange" />
+            </ElCol>
+          </div>
+          <el-table :data="costitemEditInfo.ptlAgreementCostTypeList" class="m-top-20" 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="commission" label="手续费比例" width="180" align="center">
+              <template #default="scope">
+                <el-input-number v-model="scope.row.commission" :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="addThrow" label="加投比例" width="180" align="center">
+              <template #default="scope">
+                <el-input-number v-model="scope.row.addThrow" :precision="2" controls-position="right"
+                  style="width: 100%;" :min="0" :max="100">
+                  <template #suffix>%</template>
+                </el-input-number>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+
+      </template>
+      <template #footer>
+        <div class="flex a-c j-c">
+          <el-button @click="cancel">取消</el-button>
+          <el-button type="primary" @click="save">提交审核</el-button>
+        </div>
+      </template>
+    </el-drawer>
+    <!-- 规则条件弹窗 -->
+    <RuleCondition :ruleConditionVisible="ruleConditionVisible" @cancel="ruleConditionVisible = false"
+      @save="ruleConditionSave"  :attrIdList="ruleConditions" />
+
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { ref,  } from 'vue'
+import { DrawerProps, ComponentSize,  } from 'element-plus';
+import { loadDicts } from '@/utils/composables/dictService';//字典组件
+import { defineProps, defineEmits } from "vue";
+const direction = ref<DrawerProps['direction']>('rtl')
+const fromSize = ref<ComponentSize>('default')
+
+const props = defineProps<{
+  ruleFeeVisible: boolean,
+  agreementInfo: any,
+  RulesInfo: any,
+  costitemEditInfo: any,
+  headerTitle?: string,
+}>();
+const emits = defineEmits(["cancel", 'save']);
+const ruleConditions=ref();
+const ruleConditionVisible = ref(false);//规则条件弹窗
+const costitemEditInfo = ref();//编辑费用数据
+onMounted(async () => {
+  await loadDicts(['insurance_type'])
+})
+watch(props, () => {
+  if (props.costitemEditInfo) {
+    costitemEditInfo.value = props.costitemEditInfo;
+    ruleConditions.value=props.costitemEditInfo.ruleConditions;
+  }
+})
+
+
+// 禁用失效时间选择
+const disabledEndDate = (date: any,) => {
+  let start = costitemEditInfo.value.effectiveTime;
+  const startDate = new Date(start);
+  return startDate && date < startDate; // 禁用小于生效时间的日期
+};
+
+// 处理生效时间变化
+const handleStartDateChange = (value: any,) => {
+  let endDate = costitemEditInfo.value.failureTime;
+  // 如果生效时间改变,检查失效时间是否需要更新
+  if (endDate && new Date(endDate) < new Date(value)) {
+    costitemEditInfo.value.failureTime = ''; // 清空失效时间
+  }
+};
+
+//条件删除
+const rulesAttrDel = (index: number) => {
+  costitemEditInfo.value.ruleConditions.splice(index, 1)
+}
+//多选框事件数据格式处理
+const rulesAttrselectChange = (item: any) => {
+  costitemEditInfo.value.ruleConditions[item.index].min = "";
+  item.e.map((ele: any) => {
+    costitemEditInfo.value.ruleConditions[item.index].min += ele + ',';
+  })
+  // 去掉最后一个逗号
+  if (costitemEditInfo.value.ruleConditions[item.index].min.endsWith(',')) {
+    costitemEditInfo.value.ruleConditions[item.index].min = costitemEditInfo.value.ruleConditions[item.index].min.slice(0, -1);
+  }
+}
+//获取到的规则条件列表数据
+const ruleConditionSave = (list: string[]) => {//获取规则条件选中集合
+  console.log(list)
+  costitemEditInfo.value.ruleConditions = list;
+  ruleConditionVisible.value = false;
+}
+const cancel = () => {
+  emits("cancel")
+}
+const save = () => {
+  console.log(costitemEditInfo.value)
+  // return;
+  emits("save",costitemEditInfo.value)
+}
+
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+:deep(.el-drawer__header) {
+  padding-top: 0;
+  margin-bottom: 0;
+  font-size: 20px;
+  color: #333;
+  border-bottom: 1px solid #f2f2f2;
+}
+
+.agreement {
+  width: 100%;
+  padding: 24px;
+  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>

+ 611 - 0
src/views/quoteConfig/costManagement.vue

@@ -0,0 +1,611 @@
+<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" uniqueIdName="costId">
+        <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-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.costId)">启用</el-dropdown-item>
+                <el-dropdown-item @click="disable(scope.operationData.costId)">禁用</el-dropdown-item>
+                <el-dropdown-item @click="del(scope.operationData.costId)">删除</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+      </ComplexTable>
+    </PageMain>
+    <el-dialog v-model="agreementSelectionVisible" title="选择协议" width="50%">
+      <div class="Selection flex ">
+        <div class="agreement flex f-c">
+          <div class="flex a-c">
+            <el-input v-model="agreementQueryName" style="width: 240px;margin-right: 10px;"
+              placeholder="输入协议名称,简称,编码查找" />
+            <el-button type="primary">查询</el-button>
+          </div>
+          <div class="flex f-c m-top-20" style="max-height: 400px;overflow-y: auto;">
+            <div class="agreementItem flex f-c " :class="{ active: agritem.id == AgreementId }"
+              v-for="(agritem, agrindex) in agreementList" :key="agrindex" @click="ItemClick(agritem)">
+              <span class="m-btn-10">{{ agritem.agreementCode }}</span>
+              <span>{{ agritem.agreementTitle }}</span>
+            </div>
+          </div>
+        </div>
+        <div class="rule flex f-c">
+          <span class="strong">承保规则</span>
+          <div class="flex f-c m-top-20" style="max-height: 400px;overflow-y: auto;">
+            <el-radio-group v-model="ruleId">
+              <div class="flex f-c  ">
+                <el-radio :value="item.id" v-for="(item, index) in ruleList" :key="index" @change="ruleItemclick"
+                  style="margin: 20px 0;">
+                  <p class="white-space">{{ item.ruleDescription }}</p>
+                </el-radio>
+              </div>
+            </el-radio-group>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="agreementSelectionVisible = false">取消</el-button>
+          <el-button type="primary" @click="agreementSelectionConfirm">
+            确定
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 规则费用抽屉组件 -->
+    <ruleFeeDialog v-if="ruleFeeVisible" :ruleFeeVisible="ruleFeeVisible" :agreementInfo="AgreementInfo"
+      :RulesInfo="ruleInfo" @save="ruleFeeSave" @cancel="ruleFeeVisible = false" />
+    <!-- 规则费用编辑组件 -->
+    <ruleFeeDialogEdit :ruleFeeVisible="ruleFeeEditVisible" :headerTitle="ruleFeeHeaderTitle"
+      :agreementInfo="AgreementInfo" :RulesInfo="ruleInfo" :costitemEditInfo="costitemEditInfo" @save="ruleFeeEditSave"
+      @cancel="ruleFeeEditVisible = false" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import {  useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { loadDicts, getDict } from '@/utils/composables/dictService';//字典组件
+import ruleFeeDialog from "./agreement/components/ruleFeeDialog.vue";
+import ruleFeeDialogEdit from "./agreement/components/ruleFeeDialogEdit.vue";
+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']);//获取多个字典
+  findpage();//列表数据
+  getDeptTree();
+  agreement();
+  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 agreementQueryName = ref();//选择协议查询
+const agreementList = ref();//协议列表
+const sysUpAndDownList = ref<string[]>([])//选中id
+const deptOptions = ref()//机构数据
+const insurance_type=ref();
+const valid_status = ref();
+const audit_status=ref();
+const is_enable = 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: "",
+})
+
+//表格字段数据
+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.takeStartDate = value[0];
+      formInline.takeEndDate = value[1];
+    }
+  },
+  {
+    label: "失效时间", model: "failureTime", type: "date", dateType: "datetimerange", startplaceholder: "开始时间", endplaceholder: "结束时间",
+    onChange: (value: any) => {
+      formInline.loseStartDate = value[0];
+      formInline.loseEndDate = value[1];
+    }
+  },
+]);
+
+//费用数据查询
+function findpage() {
+  api.agreementApi.queryByFeePage({ 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 {
+    }
+  });
+}
+//协议列表查询
+function agreement() {
+  api.agreementApi.agreementList({ pages: pageInfo.value.pageNum, size: pageInfo.value.pageSize, searchValue: agreementQueryName.value }).then((res: any) => {
+    if (res.code == '200') {
+      agreementList.value = res.data.records;
+    } else {
+    }
+  });
+}
+const columns = [
+  {
+    prop: "agreementAbbreviation", label: "协议信息", width: '300',
+    formatter: (row: any) => {
+      return `${row.agreementCode}\n${row.agreementAbbreviation}`
+    }
+  },
+  {
+    prop: "ruleDescription", label: "承保规则", width: '500',
+  },
+  { prop: "productName", label: "险种", width: '120', },
+  {
+    prop: "costRules", label: "费用规则",width: '500',
+  },
+  {
+    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: "approved", label: "审核状态", width: '120', component: "Tag",
+    formatter: (row: any) => {
+      let name = getDict('audit_status').find(val => val.value == row.approved).label
+      if (name) {
+        return { type: 'warning', text: name };
+      }
+    }
+  },
+  { prop: "effectiveTime", label: "生效时间", width: '200' },
+  { prop: "failureTime", label: "失效时间", width: '200' },
+  {
+    prop: "ruleConditions", label: "交强费用", width: '300', formatter: (row: any) => {
+      let info = row.ruleConditions.find((val: any) => val.costType == '交强险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      } else {
+        return '-';
+      }
+    }
+  },
+  {
+    prop: "ruleConditions", label: "商业费用", width: '300', formatter: (row: any) => {
+      let info = row.ruleConditions.find((val: any) => val.costType == '商业险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      } else {
+        return '-';
+      }
+    }
+  },
+  {
+    prop: "ruleConditions", label: "驾意险费用", width: '300', formatter: (row: any) => {
+      let info = row.ruleConditions.find((val: any) => val.costType == '驾意险');
+      if (info) {
+        return `入口:${info.inlet.toFixed(2)}% 出口:${info.export.toFixed(2)}%\n加投:${info.addThrow.toFixed(2)}% 手续费:${info.commission.toFixed(2)}%`
+      } else {
+        return '-';
+      }
+    }
+  },
+];
+//分页事件
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+
+};
+
+const agreemenUpdate = async (item: any) => {
+  ruleFeeHeaderTitle.value = item.productName;
+  AgreementInfo.value = { agreementCode: item.agreementCode, agreementTitle: item.agreementAbbreviation };
+  ruleInfo.value = { ruleDescription: item.ruleDescription };
+  await regionLicenseRegion('0');//车牌地区
+  await regionLicenseNumber('14');//车牌号
+  await api.agreementApi.AgreementFeeById(item.costId).then((res: any) => {
+    if (res.code == '200') {
+      res.data.ruleConditions = processArray(res.data.ruleConditions);
+      costitemEditInfo.value = res.data;
+      ruleFeeEditVisible.value = true;
+    } else {
+    }
+  });
+}
+const ruleFeeEditSave = (data: object) => {
+  api.agreementApi.AgreementFeeupdate(data).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      ruleFeeEditVisible.value=false;
+      findpage();
+    } else {
+    }
+  });
+}
+//添加车牌和车牌地区
+const processArray = (array: any[]) => {
+  return array.map((val) => {
+    switch (val.attrType) {
+      case 'inputNumber':
+      case 'datetime':
+        if (!val.operator) {
+          val.operator = '1'; // 设置默认值
+        }
+        break;
+      default:
+        break;
+    }
+    if (val.attrCode == 'LicenseNo') {
+      val.dictLabal = licensePlateNumber.value;
+    }
+    if (val.attrCode == 'LicenseArea') {
+      val.dictLabal = licensePlateRegion.value;
+    }
+    return val;
+  });
+};
+const licensePlateRegion = ref();//车牌地区
+const licensePlateNumber = ref();//车牌号
+const regionLicenseRegion = (value: string) => {
+  api.agreementApi.getAreaLicense(value).then((res: any) => {
+    if (res.code == '200') {
+      let data: any = [];
+      res.data.map((val: any) => {
+        data.push({
+          code: val.areaNumber,
+          name: val.license,
+        })
+        return val;
+      })
+      licensePlateRegion.value = data;
+    } else {
+    }
+  });
+}
+const regionLicenseNumber = (value: string) => {
+  api.agreementApi.getAreaLicense(value).then((res: any) => {
+    if (res.code == '200') {
+      let data: any = [];
+      res.data.map((val: any) => {
+        data.push({
+          code: val.areaNumber,
+          name: val.license,
+        })
+        return val;
+      })
+      licensePlateNumber.value = data;
+    } else {
+    }
+  });
+}
+//多选事件
+const selectionChange = (val: any) => {
+  console.log(val)
+  sysUpAndDownList.value = val;
+}
+//禁用
+const disable = (value: string) => {
+  console.log(value)
+  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.AgreementFeedeleteByIds([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(false, undefined);
+    })
+    .catch(() => {
+    })
+
+
+}
+//批量启用
+const batchenable = async () => {
+  await sysUpAndDown(true, undefined);
+}
+const agreementQuery = () => {
+  findpage();
+}
+const sysUpAndDown = async (type: boolean, list?: string[]) => {
+  await api.agreementApi.AgreementFeeEnableByIds(type, list ? list : sysUpAndDownList.value).then((res: any) => {
+    if (res.code == '200') {
+      findpage();
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const resetForm = () => {
+  for(let key in formInline){
+    formInline[key as keyof typeof formInline]='';
+  }
+  findpage();
+}
+const getDeptTree = () => {
+  api.institutionApi.institutionList({}).then((res: any) => {
+    if (res.code == '200') {
+      deptOptions.value = res.data;
+    } else {
+    }
+  });
+}
+//选择协议弹窗
+const agreementSelectionVisible = ref(false);
+const ruleFeeVisible = ref(false);
+const ruleFeeEditVisible = ref(false);//编辑弹窗
+const ruleFeeHeaderTitle = ref();//编辑弹窗title
+const costitemEditInfo = ref();//编辑对象
+const AgreementInfo = ref();//协议对象
+const ruleInfo = ref();//规则对象
+const AgreementId = ref();//规则id
+const ruleId = ref();//规则id
+const ruleList = ref();//协议规则列表
+const agreementSelection = () => {
+  agreementSelectionVisible.value = true;
+}
+const ItemClick = (item: any) => {
+  AgreementInfo.value = item;
+  AgreementId.value = item.id;
+  api.agreementApi.agreementRulesList(AgreementId.value).then((res: any) => {
+    if (res.code == '200') {
+      ruleList.value = res.data;
+    }
+  });
+}
+//
+const ruleItemclick = (value: string | number | boolean | undefined) => {
+  let info = ruleList.value.find((val: any) => val.id == value);
+  ruleInfo.value = info;
+}
+const agreementSelectionConfirm = () => {
+  agreementSelectionVisible.value = false;
+  ruleFeeVisible.value = true;
+}
+//规则弹窗回调方法
+const ruleFeeSave = (data: object) => {
+  console.log(data)
+  api.agreementApi.ptlAgreementCostsave(data).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      ruleFeeVisible.value = false;
+      findpage();
+    } else {
+    }
+  });
+}
+</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;
+}
+
+.Selection {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+
+  .agreement {
+    width: 40%;
+    padding: 15px;
+    border-right: 1px solid #dcdfe6;
+
+    .agreementItem {
+      padding: 10px;
+      border-radius: 5px;
+
+      &:hover {
+        background: #f2f2f2;
+      }
+    }
+  }
+
+  .active {
+    color: #1890ff;
+    background: #f2f2f2;
+  }
+
+  .rule {
+    width: 60%;
+    padding: 15px;
+
+    .white-space {
+      display: inline-block;
+      margin-bottom: 10px;
+      white-space: normal;
+    }
+  }
+}
+</style>