Commit f4cb66d2 by Sweet Zhang

Merge branch 'test' of http://139.224.139.2:9091/yuzhenWang/yd-csf-front into test

parents a8bfbe34 bfe1b96c
......@@ -63,3 +63,19 @@ export function getMoreDicts(data) {
data: data
})
}
// 查询保险产品列表
export function getInsuranceProductList(data) {
return request({
url: '/insurance/base/api/insuranceProduct/page',
method: 'post',
data: data
})
}
// 查询保险附加产品列表
export function getAdditionalProductList(data) {
return request({
url: '/insurance/base/api/insuranceAdditionalProduct/page',
method: 'post',
data: data
})
}
import request from '@/utils/request'
// 查询预约列表
export function getAppointmentList(data) {
return request({
url: '/csf/api/appointment/page',
method: 'post',
data: data
})
}
// 导出预约信息
export function getAppointmentExprot(appointmentBizId) {
return request({
url: `/csf/api/appointmentFile/excel/export/appointment?appointmentBizId=${appointmentBizId}`,
method: 'get'
})
}
// 导出行程单信息
export function getItineraryExprot(appointmentBizId) {
return request({
url: `/csf/api/appointmentFile/pdf/itinerary?appointmentBizId=${appointmentBizId}`,
method: 'get'
})
}
// 新增预约
export function addAppointment(data) {
return request({
url: '/csf/api/appointment/add',
method: 'post',
data: data
})
}
// 编辑预约
export function editAppointmentDetail(data) {
return request({
url: '/csf/api/appointment/edit',
method: 'put',
data: data
})
}
// 暂存预约
export function storageAppointment(data) {
return request({
url: '/csf/api/appointment/add/storage',
method: 'post',
data: data
})
}
// 获取预约详情
export function getAppointmentDetail(appointmentBizId) {
return request({
url: `/csf/api/appointment/detail?appointmentBizId=${appointmentBizId}`,
method: 'get'
})
}
// 获取预约健康信息数据
export function getQuestionnaires(appointmentBizId) {
return request({
url: `/question/api/questionnaires/detail?questionnaireBizId=questionnaires_1001&objectBizId=${appointmentBizId}`,
method: 'get'
})
}
// 预约-提交预约信息模块
export function editAppointmentInfo(data) {
return request({
url: '/csf/api/appointment/single/edit',
method: 'put',
data: data
})
}
// 预约-生成新单
export function newPolicy(data) {
return request({
url: '/csf/api/appointment/edit/confirm/time',
method: 'put',
data: data
})
}
// 预约-提交产品计划模块
export function editProductPlanInfo(data) {
return request({
url: '/csf/api/productPlan/edit/plan',
method: 'put',
data: data
})
}
// 删除附加险
export function delAdditional(additionalBizId) {
return request({
url: '/csf/api/additional/del?additionalBizId=' + additionalBizId,
method: 'delete'
})
}
// 预约-提交投保人模块
export function editPolicyholderInfo(data) {
return request({
url: '/csf/api/policyholder/edit',
method: 'put',
data: data
})
}
// 预约-提交受保人模块
export function editInsurantInfo(data) {
return request({
url: '/csf/api/insurant/edit',
method: 'put',
data: data
})
}
// 预约-提交第二持有人模块
export function editSecondHolderInfo(data) {
return request({
url: '/csf/api/secondHolder/edit',
method: 'put',
data: data
})
}
// 预约-提交受益人模块
export function editBeneficiaryInfo(data) {
return request({
url: '/csf/api/beneficiary/batch/edit',
method: 'put',
data: data
})
}
// 删除单个受益人
export function delBeneficiary(beneficiaryBizId) {
return request({
url: '/csf/api/beneficiary/del?beneficiaryBizId=' + beneficiaryBizId,
method: 'delete'
})
}
// 预约-编辑关联FNA
export function editFna(data) {
return request({
url: '/csf/api/appointment/edit/fna',
method: 'put',
data: data
})
}
// 预约-解除关联FNA
export function unlinkFna(data) {
return request({
url: '/csf/api/appointment/remove/fna',
method: 'put',
data: data
})
}
// 预约-编辑转保声明
export function editPolicytransfer(data) {
return request({
url: '/csf/api/appointment/edit/policy/transfer',
method: 'put',
data: data
})
}
// 新增附件
export function addFile(data) {
return request({
url: '/csf/api/appointmentFile/add',
method: 'post',
data: data
})
}
// 附件列表
export function getAppointmentFile(data) {
return request({
url: '/csf/api/appointmentFile/page',
method: 'post',
data: data
})
}
// 删除单个附件
export function delFile(appointmentFileBizId) {
return request({
url: '/csf/api/appointmentFile/del?appointmentFileBizId=' + appointmentFileBizId,
method: 'delete'
})
}
// 删除单个预约
export function delSigleAppointment(appointmentBizId) {
return request({
url: '/csf/api/appointment/del?appointmentBizId=' + appointmentBizId,
method: 'delete'
})
}
......@@ -42,6 +42,14 @@ export function updateProcess(data) {
data: data
})
}
// 生成副本
export function subProcess(data) {
return request({
url: '/csf/api/Fna/copy',
method: 'post',
data: data
})
}
/*
流程接口结束
*/
......
<template>
<div class="detail-panel">
<div v-for="(row, rowIndex) in processedData" :key="rowIndex" class="detail-row">
<div
v-for="(item, colIndex) in row"
:key="colIndex"
class="detail-col"
:class="getColClass(item)"
:style="{ width: getColWidth(item.span) }"
>
<!-- 每个列都有独立的边框 -->
<div class="col-container">
<!-- 左侧 Label -->
<div class="detail-label">
<el-tooltip
:content="item.label"
placement="top"
:disabled="!isTextOverflow(item.label, 'label', `${rowIndex}-${colIndex}`)"
>
<span class="label-text">{{ item.label }}</span>
</el-tooltip>
</div>
<!-- 右侧 Value -->
<div class="detail-value">
<el-tooltip
:content="item.value"
placement="top"
:disabled="!isTextOverflow(item.value, 'value', `${rowIndex}-${colIndex}`)"
>
<span class="value-text">{{ item.value || '-' }}</span>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, nextTick, watch } from 'vue'
const props = defineProps({
// 详情数据,支持两种格式:
// 1. 一维数组: [{ label: '标签', value: '值', span: 8 }, ...]
// 2. 二维数组: [[{ label: '标签1', value: '值1', span: 12 }, { label: '标签2', value: '值2', span: 12 }], ...]
data: {
type: [Array, Object],
default: () => []
},
// 每行总span数(类似Element Plus的24分栏)
rowSpan: {
type: Number,
default: 24
},
// 标签宽度,默认120px
labelWidth: {
type: String,
default: '120px'
},
// 行高
rowHeight: {
type: String,
default: '40px'
},
// 列间距
colGap: {
type: String,
default: '16px'
},
// 是否显示列边框
showColBorder: {
type: Boolean,
default: true
}
})
// 处理数据,确保是二维数组格式
const processedData = computed(() => {
if (!props.data || props.data.length === 0) return []
// 如果已经是二维数组,直接返回
if (Array.isArray(props.data[0]) && Array.isArray(props.data[0])) {
return props.data
}
// 一维数组转换为二维数组
const result = []
let currentRow = []
let currentSpan = 0
props.data.forEach(item => {
const span = item.span || Math.floor(props.rowSpan / 2) // 默认占一半
if (currentSpan + span > props.rowSpan) {
// 当前行已满,创建新行
result.push([...currentRow])
currentRow = [item]
currentSpan = span
} else {
// 添加到当前行
currentRow.push(item)
currentSpan += span
}
})
// 添加最后一行
if (currentRow.length > 0) {
result.push(currentRow)
}
return result
})
// 存储文本溢出状态
const overflowState = ref({})
// 检查文本是否溢出
const isTextOverflow = (text, type, key) => {
if (!text) return false
return overflowState.value[`${type}-${key}`] || false
}
// 获取列的宽度
const getColWidth = span => {
const percentage = (span / props.rowSpan) * 100
return `calc(${percentage}% - ${props.colGap})`
}
// 获取列的class
const getColClass = item => {
const classes = []
if (item.align) {
classes.push(`align-${item.align}`)
}
return classes
}
// 检测文本溢出
const checkTextOverflow = () => {
nextTick(() => {
const labelElements = document.querySelectorAll('.label-text')
const valueElements = document.querySelectorAll('.value-text')
// 重置溢出状态
overflowState.value = {}
// 检测label溢出
labelElements.forEach((el, index) => {
const parentCol = el.closest('.detail-col')
if (parentCol) {
const rowIndex = Array.from(parentCol.parentNode.children).indexOf(parentCol)
const colIndex = Array.from(parentCol.parentNode.parentNode.children).indexOf(
parentCol.parentNode
)
const key = `${colIndex}-${rowIndex}`
overflowState.value[`label-${key}`] = el.scrollWidth > el.clientWidth
}
})
// 检测value溢出
valueElements.forEach((el, index) => {
const parentCol = el.closest('.detail-col')
if (parentCol) {
const rowIndex = Array.from(parentCol.parentNode.children).indexOf(parentCol)
const colIndex = Array.from(parentCol.parentNode.parentNode.children).indexOf(
parentCol.parentNode
)
const key = `${colIndex}-${rowIndex}`
overflowState.value[`value-${key}`] = el.scrollWidth > el.clientWidth
}
})
})
}
onMounted(() => {
checkTextOverflow()
})
// 监听数据变化
watch(
() => props.data,
() => {
nextTick(() => {
checkTextOverflow()
})
},
{ deep: true }
)
</script>
<style lang="scss" scoped>
.detail-panel {
/* border: 1px solid #dcdfe6; */
border-radius: 4px;
background-color: #fff;
}
.detail-row {
display: flex;
flex-wrap: wrap;
min-height: v-bind(rowHeight);
/* border-bottom: 1px solid #ebeef5; */
gap: v-bind(colGap);
padding: 0 calc(v-bind(colGap) / 2);
&:last-child {
border-bottom: none;
}
}
.detail-col {
display: flex;
min-height: v-bind(rowHeight);
// 对齐方式
&.align-center {
.detail-label,
.detail-value {
justify-content: center;
text-align: center;
}
}
&.align-right {
.detail-label,
.detail-value {
justify-content: flex-end;
text-align: right;
}
}
}
// 每个列的容器,添加独立边框
.col-container {
display: flex;
width: 100%;
/* border: v-bind(showColBorder ? '1px solid #ebeef5' : 'none'); */
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
background-color: #fff;
margin-bottom: 15px;
// 鼠标悬停效果
/* .detail-row:hover & {
background-color: #f5f7fa;
.detail-label {
background-color: #e6f3ff;
}
} */
}
.detail-label {
width: v-bind(labelWidth);
min-width: v-bind(labelWidth);
padding: 8px 12px;
/* background-color: #f5f7fa; */
border-right: 1px solid #ebeef5;
display: flex;
align-items: center;
font-weight: 500;
color: #606266;
.label-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
font-size: 14px;
}
}
.detail-value {
flex: 1;
padding: 8px 12px;
display: flex;
align-items: center;
.value-text {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
color: #303133;
font-size: 14px;
}
}
// 响应式处理
@media (max-width: 768px) {
.detail-col {
width: 100% !important;
}
.col-container {
border: 1px solid #ebeef5;
margin-bottom: 8px;
}
}
</style>
/**
* 1.普通模块得数据格式,无子级:
* {
一些页面用到得属性,比如个人资料模块得数据格式
data:[] data主要是这个模块对应得表单dom数据
}
2. 有子级得模块得数据格式:
{
一些页面用到得属性,比如家庭状况模块得数据格式
data:[ data主要父级模块对应得表单
{
比如家庭状况中有父亲,母亲,配偶,这个对象就代表这些人
children:[],代表得是这些人对应得表单dom
}
]
}
以上两种格式说明了fnaForm页面得dom数据格式,如果有子级得dom请参照2格式添加,否则可能有未知错误
*/
const fnaForm = [
{
fatherTitle: '个人资料',
......@@ -159,8 +179,6 @@ const fnaForm = [
childTitle: '父亲',
id: Date.now() + Math.floor(Math.random() * 1000), //唯一标识
span: 24, //栅格布局份数
// age: '',
// needProvide: '',
children: [
{
label: '年龄',
......@@ -197,8 +215,6 @@ const fnaForm = [
childTitle: '母亲',
id: Date.now() + Math.floor(Math.random() * 1000), //唯一标识
span: 24, //栅格布局份数
// age: '',
// needProvide: '',
children: [
{
label: '年龄',
......@@ -235,8 +251,6 @@ const fnaForm = [
childTitle: '配偶',
id: Date.now() + Math.floor(Math.random() * 1000), //唯一标识
span: 24, //栅格布局份数
// age: '',
// needProvide: '',
children: [
{
label: '年龄',
......
const policyTransferInfo = [
{
fatherTitle: '',
type: 'object',
key: 'policyTransfer',
showMoudle: true, //模块是否展示
label: '',
title:
'阁下是否使用或打算使用现有人寿保险保单的部分或全部资金,或使用或打算使用通过减少现有人寿保险保单的应付保费而节省的金额,以资助阁下购买新的人寿保险保单?例如,此等资金或金额可能来自:',
domType: 'Div',
required: false,
maxLength: 30,
disabled: false,
placeholder: '请输入',
show: true,
dictType: 'csf_ap_policy_transfer',
tip: '请在适当的方格内填上剔号(只可选择一项)',
informationList: [
{ name: 'a. 就 阁下现有人寿保险保单作出退保/部分退保的安排,以获得其退保价值' },
{ name: 'b. 从 阁下现有人寿保险保单中提取保单贷款(包括自动保费贷款)' },
{ name: 'c. 从 阁下现有人寿保险保单中提取保单价值(例如:套现红利或赎回基金单位等)' },
{ name: 'd. 容许 阁下现有人寿保险保单失效(例如:终止支付保费)' },
{ name: 'e. 行使 阁下现有人寿保险保单中「保费假期」的权利' }
],
labelWidth: '120px', //标签宽度
sm: 24, //栅格布局份数
lg: 24 //栅格布局份数
}
]
export default policyTransferInfo
const secondHolder = [
// 基础信息
{
fatherTitle: '',
type: 'object',
key: 'person',
labelPosition: 'top', //标签的位置
showMoudle: true, //模块是否展示
// description: '证件信息至少填写一项',
data: [
{
label: '与受保人关系',
key: 'insurantRel',
domType: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'csf_ap_rel',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '名字',
key: 'name',
customerKey: 'name',
domType: 'Input',
inputType: 'text',
required: false,
maxLength: 20,
disabled: false,
placeholder: '请输入',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '名字-英文',
key: 'nameEn',
customerKey: 'firstNamePinyin',
showEn: true, //是否填写英文
domType: 'Input',
inputType: 'text',
required: false,
maxLength: 20,
disabled: false,
placeholder: '请输入',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '性别',
key: 'gender',
customerKey: 'gender',
domType: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'sys_gender',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '出生日期',
key: 'birthTime',
customerKey: 'birthdate',
domType: 'DatePicker',
required: false,
disabled: false,
placeholder: '请选择',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '年龄',
key: 'age',
customerKey: 'age',
domType: 'Input',
inputType: 'number',
required: false,
maxLength: 20,
disabled: true,
placeholder: '请输入',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '证件类型',
key: 'documentType',
customerKey: 'idType',
domType: 'Select',
required: false,
disabled: false,
placeholder: '请选择',
dictType: 'csf_id_type',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
},
{
label: '证件号码',
key: 'idNumber',
customerKey: 'idCard',
domType: 'Input',
inputType: 'number',
required: false,
maxLength: 20,
disabled: false,
placeholder: '请输入',
show: true,
labelPosition: 'top', //标签的位置
labelWidth: '120px', //标签宽度
sm: 12, //栅格布局份数
lg: 8 //栅格布局份数
}
]
}
]
export default secondHolder
const useDictStore = defineStore('dict', {
state: () => ({
dict: new Array(),
tenantUserList: [],
tenantUserList: [], //租户用户数据
insureProductList: [], //保险产品数据
additionalProductList: [], //附加险产品数据
dictTypeLists: [] //字典列表,根据请求得不同会变化,所以使用之前需要使用useDictLists请求数据
}),
actions: {
......@@ -54,6 +56,14 @@ const useDictStore = defineStore('dict', {
setTenantUserList(user) {
this.tenantUserList = user
},
// 设置保险产品列表
setInsureProductList(product) {
this.insureProductList = product
},
// 设置附加险产品列表
setAdditionalProductList(product) {
this.additionalProductList = product
},
// 设置字典列表
setDictTypeLists(typeList) {
this.dictTypeLists = typeList
......
{
"id": 587,
"fnaBizId": "fna_nJEdXVIhXsp8uaO0",
"fnaNo": "CSF-B-20250924-0587",
"userBizId": "user_dMnkKPIwemvY0zhk",
"customerName": "33",
"customerBizId": "customer_cdV0p5a86hqmMnDw",
"fnaFormBizId": "fna_form_0PjYRkZFt2oMJ62b",
"fnaFormStatus": "1",
"appointmentNo": "Y2509295725815",
"appointmentBizId": "appointment_2ImPDnPjXQLD2GOQ",
"policyBizId": null,
"policyNo": null,
"status": "UNCOMPLETED",
"productCode": null,
"productName": null,
"url": null,
"edit": null,
"remark": null,
"isDeleted": null,
"createTime": "2025-09-24T16:01:07.000+08:00"
}
......@@ -12,7 +12,7 @@ const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
const DATE_FORMAT = 'YYYY-MM-DD'
export function formatToDateTime(date, format = DATE_TIME_FORMAT) {
if (!date) return ''
if (!date) return ''
return dayjs(date).format(format)
}
......@@ -21,10 +21,39 @@ export function formatToDate(date, format = DATE_FORMAT) {
return dayjs(date).format(format)
}
export const getNowTime = (format = DATE_TIME_FORMAT) => {
if (!date) return ''
if (!date) return ''
return dayjs().format(format)
}
/**
* 更精确的年龄计算(精确到月日)
* @param {string|Date} birthDate - 出生日期
* @returns {number} 年龄
*/
export const calculateExactAge = birthDate => {
if (!birthDate) return 0
try {
const birth = dayjs(birthDate)
const today = dayjs()
if (!birth.isValid() || birth.isAfter(today)) {
return 0
}
let age = today.diff(birth, 'year')
const remainder = today.diff(birth.add(age, 'year'), 'month')
// 如果还没到今年的生日,年龄减1
if (remainder < 0) {
age--
}
return age > 0 ? age : 0
} catch (error) {
console.error('计算精确年龄时出错:', error)
return 0
}
}
export default {
formatIsoToDateTime,
formatToDateTime,
......
......@@ -11,14 +11,18 @@ export function useDict(...args) {
args.forEach((dictType, index) => {
res.value[dictType] = []
const dicts = useDictStore().getDict(dictType)
if (dicts) {
res.value[dictType] = dicts
} else {
getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue }))
useDictStore().setDict(dictType, res.value[dictType])
})
}
// if (dicts) {
// res.value[dictType] = dicts
// } else {
// getDicts(dictType).then(resp => {
// res.value[dictType] = resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue }))
// useDictStore().setDict(dictType, res.value[dictType])
// })
// }
getDicts(dictType).then(resp => {
res.value[dictType] = resp.data.map(p => ({ label: p.itemLabel, value: p.itemValue }))
useDictStore().setDict(dictType, res.value[dictType])
})
})
return toRefs(res.value)
})()
......@@ -29,18 +33,20 @@ export function useDict(...args) {
export function useDictLists(typeLists) {
let params = { typeList: typeLists }
let dictArray = []
getMoreDicts(params).then(resp => {
if (resp.code === 200) {
dictArray = resp.data.map(item => {
item.dictItemList.forEach(dict => {
dict.label = dict.itemLabel
dict.value = dict.itemValue
return (() => {
getMoreDicts(params).then(resp => {
if (resp.code === 200) {
dictArray = resp.data.map(item => {
item.dictItemList.forEach(dict => {
dict.label = dict.itemLabel
dict.value = dict.itemValue
})
return item
})
return item
})
useDictStore().setDictTypeLists(dictArray)
}
})
useDictStore().setDictTypeLists(dictArray)
}
})
})()
}
// /**
// * 获取字典数据
......
......@@ -90,7 +90,7 @@ const menuList = ref([])
const quickList = ref([])
const confirmCountry = item => {
menuList.value[0].value = item.areaCode
hanleCountryClose()
}
const openCountryDrawer = item => {
......@@ -129,7 +129,6 @@ const confirmClick = () => {
info[key] = phone
}
}
emit('confirmDrawer', info)
drawer.value = false
}
......
......@@ -180,8 +180,8 @@ import Country from '@/views/components/country'
import Phone from '@/views/components/phone'
import Address from '@/views/components/address'
import { getDicts } from '@/api/system/dict/data'
import { watch, nextTick } from 'vue'
import { watch, } from 'vue'
import { addCustomer, getCustomerDetail, editCustomer, getCustomerList } from '@/api/sign/fna'
import useDictStore from '@/store/modules/dict'
const dictStore = useDictStore() //获取字典数据
......@@ -373,6 +373,7 @@ const processFormData = async () => {
if (props.customerBizId) {
getCustomerInfo(props.customerBizId, processedData)
editStatus.value = true
} else {
editStatus.value = false
processedCustomerData.value = oldCustomerData.value = processedData
......@@ -768,6 +769,7 @@ watch(
newVal => {
if (newVal === 'customer') {
openList.value = false
processFormData()
}
}
......
......@@ -190,13 +190,21 @@
<el-row>
<el-col>
<div class="tabButton">
<!-- :disabled="editStatus" -->
<el-button type="primary" icon="RefreshRight" size="large" @click="submitForm('temp')"
<el-button
type="primary"
icon="RefreshRight"
size="large"
@click="submitForm('temp')"
:disabled="editStatus"
>暂存</el-button
>
<!-- :disabled="editStatus" -->
<el-button type="primary" icon="Check" @click="submitForm('save')" size="large"
>提交</el-button
<el-button
type="success"
icon="Check"
@click="submitForm('save')"
size="large"
:disabled="editStatus"
>立即预约</el-button
>
</div>
</el-col>
......@@ -366,8 +374,10 @@ const processFormData = async () => {
if (props.fnaFormBizId) {
getFanformInfo(props.fnaFormBizId, processedData)
editStatus.value = true
} else {
processedFanFormData.value = oldFanFormData.value = processedData
editStatus.value = false
}
}
// 添加表单子级dom
......
......@@ -25,7 +25,7 @@
</div>
</div>
</div>
<div class="right">
<!-- <div class="right">
<el-button
:disabled="!(processInfo.customerBizId && processInfo.fnaFormBizId)"
type="success"
......@@ -38,12 +38,12 @@
plain
>生成副本</el-button
>
</div>
</div> -->
</div>
<div class="tabsBox">
<el-tabs v-model="activeName" class="demo-tabs" :before-leave="beforeTabLeave">
<el-tab-pane v-for="tab in tabsList" :key="tab.name" :label="tab.label" :name="tab.name">
<div class="tabPaneBox">
<div :class="{ tabPaneBox: activeName !== 'appointment' }">
<div v-if="tab.name === 'overview'" class="overviewBox">
<div
class="oneItem"
......@@ -68,7 +68,7 @@
unFinishTitle: item.status == '0'
}"
>
{{ item.title }}
{{ item.label }}
</div>
<div
class="status"
......@@ -109,9 +109,16 @@
@handleSuccess="handleSuccess"
/>
</div>
<div v-if="tab.name === 'appointment'">关联预约内容</div>
<div v-if="tab.name === 'appointment'">
<AppointmentEdit
:embed="true"
editStatus="add"
:tabName="activeName"
:processDetail="processInfo"
@handleSuccess="handleSuccess"
/>
</div>
<div v-if="tab.name === 'newpolicy'">关联新单内容</div>
<div v-if="tab.name === 'policy'">关联保单内容</div>
</div>
</el-tab-pane>
</el-tabs>
......@@ -120,12 +127,14 @@
</div>
</template>
<script setup name="FnaEdit">
import AppointmentEdit from '@/views/sign/appointment/appointmentEdit'
import useUserStore from '@/store/modules/user'
import useDictStore from '@/store/modules/dict'
import { addFna, getProcessDetail, updateProcess } from '@/api/sign/fna'
import { listTenantUser } from '@/api/common'
import { listTenantUser, getInsuranceProductList, getAdditionalProductList } from '@/api/common'
import Customer from './components/customer'
import FanForm from './components/fanForm'
import { Check } from '@element-plus/icons-vue'
import { ref } from 'vue'
const userStore = useUserStore()
......@@ -135,10 +144,10 @@ const route = useRoute()
const router = useRouter()
const activeName = ref('overview')
const processInfo = ref({
fnaNo: '暂无',
status: '未完成',
createTime: proxy.parseTime(new Date()),
customerName: userStore.name
// fnaNo: '暂无',
// status: '未完成',
// createTime: proxy.parseTime(new Date()),
// customerName: userStore.name
}) // 流程详情信息
const updateStatus = ref(false)
const dictTypeLists = ref([])
......@@ -175,28 +184,61 @@ const tabsList = ref([
id: 4,
status: '0',
key: 'policyBizld'
},
{
label: '关联保单',
name: 'policy',
id: 5,
status: '0',
key: 'policyNo'
}
// {
// label: '关联保单',
// name: 'policy',
// id: 5,
// status: '0',
// key: 'policyNo'
// }
])
const { csf_fna_status } = proxy.useDict('csf_fna_status')
// 获取各个流程所需要得字典数据
const getDictsData = async () => {
// 获取租户用户列表
const params = {
const params1 = {
tenantBizId: userStore.projectInfo.tenantBizId,
pageNo: 1,
pageSize: 10
}
const response = await listTenantUser(params)
if (response.code == 200) {
dictStore.setTenantUserList(response.data.records)
const response1 = await listTenantUser(params1)
if (response1.code == 200) {
dictStore.setTenantUserList(response1.data.records)
}
const params2 = {
loginTenantBizId: userStore.projectInfo.tenantBizId,
pageNo: 1,
pageSize: 10
}
const response2 = await getInsuranceProductList(params2)
if (response2.code == 200) {
response2.data.records = response2.data.records.map(item => {
return {
...item,
label: item.productName,
value: item.productBizId
}
})
dictStore.setInsureProductList(response2.data.records)
}
const params3 = {
pageNo: 1,
pageSize: 10
}
const response3 = await getAdditionalProductList(params3)
if (response3.code == 200) {
response3.data.records = response3.data.records.map(item => {
return {
...item,
label: item.productName,
value: item.additionalProductBizId
}
})
dictStore.setAdditionalProductList(response3.data.records)
}
// 请求每个流程中所涉及到的字典值数据
proxy.useDictLists([
......@@ -210,7 +252,20 @@ const getDictsData = async () => {
'sys_gender',
'csf_marriage',
'csf_education',
'csf_id_type'
'csf_id_type',
'csf_ap_apply_type',
'csf_ap_meeting_point',
'csf_ap_first_issue',
'csf_ap_dividend',
'csf_ap_frequency',
'csf_ap_rel',
'csf_ap_registration',
'csf_ap_exercise',
'csf_ap_risk',
'csf_ap_movie',
'csf_ap_game',
'wj_question_first_category',
'wj_question_second_category'
])
}
......@@ -246,7 +301,9 @@ function getProcessInfo(fnaBizId, changeTab) {
}
})
csf_fna_status._object.csf_fna_status.forEach(item => {
if (item.value == res.data.status) processInfo.value.status = item.label
if (item.value == res.data.status) {
processInfo.value.status = item.label
}
})
if (changeTab) {
activeName.value = changeTab
......@@ -310,7 +367,9 @@ const handleBack = () => {
if (route.query.type == 'add') {
getAddInfo()
} else if (route.query.type == 'edit') {
getProcessInfo(route.query.fnaBizId)
setTimeout(() => {
getProcessInfo(route.query.fnaBizId)
}, 100)
}
const handleSuccess = info => {
switch (info.tab) {
......@@ -345,6 +404,12 @@ const handleSuccess = info => {
})
}
break
case 'appointment':
if (info.type == 'add') {
getProcessInfo(processInfo.value.fnaBizId, 'newpolicy')
} else {
}
break
case 'newpolicy':
break
case 'policy':
......@@ -354,10 +419,6 @@ const handleSuccess = info => {
}
}
getDictsData()
// handleDomData()
// 1.通过新建流程进来的,要请求创建/csf/api/Fna/add拿到头部dom等信息
//2.每次进来要请求根据id获取/csf/api/Fna/get/vo,根据流程BizId获取进行到哪一步了,还可以拿到头部dom等信息
//3.每个模块保存完要重新请求/csf/api/Fna/get/vo,还要再请求/csf/api/Fna/update,更新后端数据库
</script>
<style lang="scss" scoped>
::v-deep .el-card {
......
<template>
<div class="app-container">
<!-- 步骤条 -->
<el-row style="margin-bottom: 30px">
<!-- <el-row style="margin-bottom: 30px">
<el-col :span="24">
<Step :stepList="stepList"></Step>
</el-col>
</el-row>
</el-row> -->
<!-- 条件查询 -->
<el-row>
<el-col :span="24">
......@@ -79,15 +79,15 @@
:data="tenantList"
@selection-change="tableSelect"
@sort-change="sortChange"
>
<el-table-column type="selection" width="55" />
<el-table-column type="index" width="50" />
<el-table-column label="流程编号" align="center" prop="fnaNo" width="200" />
<el-table-column label="预约编号" align="center" prop="appointmentNo" width="150" />
<el-table-column label="预约编号" align="center" prop="appointmentNo" />
<el-table-column label="新单编号" align="center" prop="policyId" />
<el-table-column label="保单号" align="center" prop="policyNo" />
<el-table-column label="保单号" align="center" prop="policyNo" width="150" />
<el-table-column label="客户姓名" align="center" prop="customerName" />
<el-table-column label="客户姓名" align="center" prop="customerName" width="100" />
<el-table-column label="状态" align="center" width="150">
<template #default="scope">
......@@ -102,8 +102,7 @@
</span>
</template>
</el-table-column>
<!-- sortable 后端未做 -->
<el-table-column label="创建时间" sortable align="center" prop="createTime">
<el-table-column label="创建时间" sortable align="center" prop="createTime" width="150">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
......@@ -111,13 +110,20 @@
<el-table-column
label="操作"
align="center"
width="200"
width="250"
class-name="small-padding fixed-width"
fixed="right"
>
<template #default="scope">
<el-button link type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button type="primary" @click="handleUpdate(scope.row)">修改</el-button>
<el-button type="success" @click="handleCopy(scope.row)">生成副本</el-button>
<!-- v-if="scope.row.status == 'COMPLETED'" -->
<el-button link type="primary" @click="handleDelete(scope.row)">删除</el-button>
<el-button
v-if="scope.row.status == 'COMPLETED'"
type="danger"
@click="handleDelete(scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
......@@ -137,7 +143,7 @@
<script setup name="FnaList">
import Step from '@/views/components/moduleStep'
import { getFnaList, deleteFna } from '@/api/sign/fna'
import { getFnaList, deleteFna, subProcess } from '@/api/sign/fna'
import useUserStore from '@/store/modules/user'
const stepList = ref([
......@@ -148,11 +154,7 @@ const stepList = ref([
const userStore = useUserStore()
const router = useRouter()
const { proxy } = getCurrentInstance()
const { sys_status, csf_fna_status, sys_no_yes } = proxy.useDict(
'sys_status',
'csf_fna_status',
'sys_no_yes'
)
const { csf_fna_status } = proxy.useDict('csf_fna_status')
const tenantList = ref([])
const loading = ref(true)
......@@ -174,6 +176,25 @@ const data = reactive({
})
const { queryParams, form, rules } = toRefs(data)
const handleCopy = row => {
// { fnaBizId: row.fnaBizId }
subProcess({ fnaBizId: row.fnaBizId }).then(response => {
if (response.code === 200) {
getList()
proxy.$modal.msgSuccess('生成副本成功')
}
})
// proxy.$modal
// .confirm('是否确认复用' + row.customerName + '的FNA流程?')
// .then(function () {
// return subProcess({ fnaBizId: row.fnaBizId })
// })
// .then(() => {
// getList()
// proxy.$modal.msgSuccess('生成副本成功')
// })
// .catch(() => {})
}
const sortChange = ({ prop, order }) => {
if (order == 'ascending') {
queryParams.value.sortOrder = 'ascend'
......@@ -261,20 +282,15 @@ function handleAdd() {
/** 修改按钮操作 */
function handleUpdate(row) {
router.push({ path: '/sign/FnaList/edit', query: { fnaBizId: row.fnaBizId, type: 'edit' } })
router.push({
path: '/sign/FnaList/edit',
query: { fnaBizId: row.fnaBizId, type: 'edit', status: row.status }
})
}
getList()
</script>
<style lang="scss" scoped>
/* ::v-deep .el-step__head.is-success {
color: none !important;
background-color: #e8f3ff !important;
border: none !important;
} */
/* ::v-deep .el-step__icon {
background-color: #e8f3ff !important;
} */
.bottomBtn {
width: 100%;
text-align: right;
......
<template>
<!-- :limit="limit" :on-exceed="handleExceed"-->
<div>
<div class="fileUploadBox">
<el-upload
:action="uploadImgUrl"
:headers="headers"
:show-file-list="false"
:on-success="uploadSuccess"
:before-upload="handleBeforeUpload"
>
<div class="uploadBox">
<el-icon size="20"><DocumentAdd /></el-icon>
<div class="file">选择文件</div>
</div>
</el-upload>
</div>
<div class="tip">(支持Word,Excel,PDF,图片格式)</div>
<el-table v-loading="loading" :data="fileTableList">
<!-- <el-table-column type="selection" width="55" /> -->
<el-table-column label="序号" type="index" width="50" />
<el-table-column label="文件名" align="center" prop="fileName" width="200" />
<!-- sortable -->
<el-table-column label="上传时间" align="center" prop="createTime" width="200">
<template #default="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="上传人" align="center" prop="creatorName" />
<el-table-column label="文件类型" align="center" prop="fileType" />
<el-table-column
label="操作"
align="left"
width="250"
class-name="small-padding fixed-width"
fixed="right"
>
<template #default="scope">
<el-button type="primary" link @click="downloadFile(scope.row)">下载</el-button>
<el-button
v-if="isImageFile(scope.row.fileName)"
type="primary"
link
@click="handleView(scope.row)"
>
查看
</el-button>
<el-button type="primary" link @click="handleUpdate(scope.row)">修改</el-button>
<el-button type="danger" link @click="handleDetele(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total >= 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getFileList"
/>
<!-- 图片查看器 -->
<el-dialog v-model="imageViewerVisible" title="图片预览" width="60%">
<div style="text-align: center">
<el-image :src="imageUrl" fit="contain" />
</div>
</el-dialog>
</div>
</template>
<script setup name="FileUpload">
import { getToken } from '@/utils/auth'
import { ElLoading } from 'element-plus'
import { addFile, getAppointmentFile, delFile } from '@/api/sign/appointment'
const props = defineProps({
activeName: { type: String, default: '' }, //tab名称
formStatus: { type: String, default: '' }, //父组件状态,新增、修改
idsObj: { type: Object, default: () => ({}) }, //父组件传递过来的id对象
apiAppointmentInfoDto: { type: Object, default: () => ({}) }, //父组件传递过来的预约信息的详情
appointmentStatus: { type: Number } //父组件传递过来的预约的状态
})
const { proxy } = getCurrentInstance()
const loading = ref(true)
const total = ref(0)
const fileTableList = ref([])
const limit = ref(2)
const fileSize = ref(10)
const headers = ref({ Authorization: 'Bearer ' + getToken() })
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload') // 上传的服务器地址
// 图片查看相关状态
const imageViewerVisible = ref(false)
const imageUrl = ref('')
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 4,
startTime: '',
endTime: '',
appointmentBizId: props.idsObj.appointmentBizId
}
})
const { queryParams, form } = toRefs(data)
const handleDetele = row => {
proxy.$modal
.confirm('是否确认删除名称为"' + row.fileName + '"的文件?')
.then(function () {
return delFile(row.appointmentFileBizId)
})
.then(res => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功')
getFileList()
}
})
.catch(() => {})
}
const isImageFile = fileName => {
return /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)$/i.test(fileName || '')
}
const handleUpdate = row => {}
const handleView = row => {
imageUrl.value = row.fileUrl
imageViewerVisible.value = true
}
const downloadFile = row => {
// 创建隐藏的下载链接
const link = document.createElement('a')
link.href = row.fileUrl
link.target = '_blank' // 新窗口打开
// 设置下载文件名(重要)
// 从URL中提取文件名,或者使用返回的文件名
const fileName = row.fileName
link.download = fileName
// 触发下载
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
// const downloadFile = async row => {
// try {
// if (!row.fileUrl) {
// proxy.$modal.msgError('文件链接不存在')
// return
// }
// // 显示加载状态
// const loadingInstance = ElLoading.service({
// lock: true,
// text: '文件下载中...',
// background: 'rgba(0, 0, 0, 0.7)'
// })
// try {
// const response = await fetch(row.fileUrl)
// if (!response.ok) {
// throw new Error(`服务器响应错误: ${response.status}`)
// }
// const blob = await response.blob()
// const url = window.URL.createObjectURL(blob)
// const link = document.createElement('a')
// link.href = url
// link.download = row.fileName || extractFileName(row.fileUrl, row.fileName)
// link.style.display = 'none'
// document.body.appendChild(link)
// link.click()
// // 清理资源
// setTimeout(() => {
// document.body.removeChild(link)
// window.URL.revokeObjectURL(url)
// }, 100)
// proxy.$modal.msgSuccess('文件已开始下载')
// } finally {
// loadingInstance.close()
// }
// } catch (error) {
// console.error('下载失败:', error)
// // 如果fetch方式失败,尝试直接链接方式
// try {
// const link = document.createElement('a')
// link.href = row.fileUrl
// link.download = row.fileName || extractFileName(row.fileUrl, row.fileName)
// link.target = '_blank'
// link.style.display = 'none'
// document.body.appendChild(link)
// link.click()
// setTimeout(() => {
// document.body.removeChild(link)
// }, 100)
// proxy.$modal.msgSuccess('已尝试开始下载')
// } catch (fallbackError) {
// proxy.$modal.msgError('下载失败,请检查网络连接')
// }
// }
// }
// // 文件名处理函数
// const extractFileName = (url, fallbackName) => {
// if (fallbackName) return fallbackName
// try {
// const urlObj = new URL(url)
// const pathname = urlObj.pathname
// const filename = pathname.substring(pathname.lastIndexOf('/') + 1)
// return decodeURIComponent(filename) || `file_${Date.now()}`
// } catch {
// const parts = url.split('/')
// const lastPart = parts[parts.length - 1]
// return lastPart.split('?')[0] || `file_${Date.now()}`
// }
// }
// 上传前loading加载
function handleBeforeUpload(file) {
// if (file.name.includes(',')) {
// proxy.$modal.msgError('文件名不正确,不能包含英文逗号!')
// return false
// }
const isLt = file.size / 1024 / 1024 < fileSize.value
if (!isLt) {
proxy.$modal.msgError(`上传文件大小不能超过 ${fileSize.value} MB!`)
return false
}
// proxy.$modal.loading('正在上传文件,请稍候...')
}
// 文件个数超出
function handleExceed() {
proxy.$modal.msgError(`一次只能上传${limit.value}个文件!`)
}
const uploadSuccess = res => {
console.log('上传成功', res)
if (res.code == 200) {
if (props.idsObj.appointmentBizId) {
let submitObj = {
appointmentBizId: props.idsObj.appointmentBizId,
fileUrl: res.data.url,
fileName: res.data.originalName,
ossFileBizId: res.data.fileBizId
}
addFile(submitObj).then(res => {
if (res.code == 200) {
proxy.$message.success('文件上传成功')
queryParams.value.pageNo = 1
getFileList()
// emit('handleSuccessEdit')
}
})
}
}
}
const getFileList = () => {
loading.value = true
try {
getAppointmentFile(queryParams.value).then(response => {
total.value = response.data.total
fileTableList.value = response.data.records
if (fileTableList.value.length > 0) {
for (const item of fileTableList.value) {
item.fileType = '预约文件'
}
}
loading.value = false
})
} catch (error) {
fileTableList.value = []
} finally {
loading.value = false
}
}
watch(
() => props.activeName,
newVal => {
if (newVal === 'accessories') {
queryParams.value.pageNo = 1
getFileList()
}
}
)
</script>
<style lang="scss" scoped>
.fileUploadBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding: 20px 0;
border: 1px dashed #dcdfe6;
}
.uploadBox {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.file {
font-size: 14px;
margin-top: 10px;
color: #409eff;
}
}
.tip {
font-size: 14px;
color: #909399;
margin: 10px 0 20px 0;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment