Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-csf-front
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
yuzhenWang
yd-csf-front
Commits
574b9891
Commit
574b9891
authored
Jan 13, 2026
by
yuzhenWang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
做到了保费对账编辑
parent
d4982277
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2090 additions
and
924 deletions
+2090
-924
src/api/sign/policy.js
+102
-0
src/components/SearchForm/SearchForm.vue
+594
-418
src/utils/2.js
+44
-317
src/views/sign/FnaList/components/fanForm.vue
+9
-1
src/views/sign/appointment/components/productPlan.vue
+4
-1
src/views/sign/policyReceipts/index.vue
+202
-187
src/views/sign/policyReceipts/premiumRecon.vue
+1135
-0
No files found.
src/api/sign/policy.js
0 → 100644
View file @
574b9891
import
request
from
'@/utils/request'
// 分页获取保单业务回执列表
export
function
getPolicyReceiptList
(
data
)
{
return
request
({
url
:
'/csf/api/policyReceipt/page'
,
method
:
'post'
,
data
:
data
})
}
// 添加保单业务回执
export
function
addPolicyReceipt
(
data
)
{
return
request
({
url
:
'/csf/api/policyReceipt/add'
,
method
:
'post'
,
data
:
data
})
}
//编辑保单业务回执
export
function
EditPolicyReceipt
(
data
)
{
return
request
({
url
:
'/csf/api/policyReceipt/edit'
,
method
:
'post'
,
data
:
data
})
}
//获取保单业务回执详情
export
function
getPolicyReceipt
(
policyReceiptBizId
)
{
return
request
({
url
:
'/csf/api/policyReceipt/detail?policyReceiptBizId='
+
policyReceiptBizId
,
method
:
'get'
})
}
//保费对账列表
export
function
premiumReconciliationList
(
data
)
{
return
request
({
url
:
'/csf/api/premiumReconciliation/page'
,
method
:
'post'
,
data
:
data
})
}
//获取保费对账详情
export
function
getPremiumReconciliationInfo
(
premiumReconciliationBizId
)
{
return
request
({
url
:
`/csf/api/premiumReconciliation/detail?premiumReconciliationBizId=
${
premiumReconciliationBizId
}
`
,
method
:
'get'
})
}
//新增保费对账
export
function
addPremiumReconciliation
(
data
)
{
return
request
({
url
:
'/csf/api/premiumReconciliation/add'
,
method
:
'post'
,
data
:
data
})
}
//保费对账编辑单个汇款记录
export
function
editPremiumRemittance
(
data
)
{
return
request
({
url
:
'/csf/api/premiumRemittance/edit'
,
method
:
'put'
,
data
:
data
})
}
//保费对账删除一条汇款记录
export
function
deletePremiumRemittance
(
premiumRemittanceBizId
)
{
return
request
({
url
:
`/csf/api/premiumRemittance/del?premiumRemittanceBizId=
${
premiumRemittanceBizId
}
`
,
method
:
'delete'
})
}
//保费对账删除汇款记录中得其他资料附件
export
function
deletePremiumRemittanceFile
(
premiumRemittanceFileBizId
)
{
return
request
({
url
:
`/csf/api/premiumRemittanceFile/del?premiumRemittanceFileBizId=
${
premiumRemittanceFileBizId
}
`
,
method
:
'delete'
})
}
//保费对账其他资料附件列表
export
function
getPremiumRemittanceFileList
(
data
)
{
return
request
({
url
:
'/csf/api/premiumRemittanceFile/page'
,
method
:
'post'
,
data
:
data
})
}
//编辑单个保费对账汇款记录
export
function
editSiglePremiumRemittance
(
data
)
{
return
request
({
url
:
'/csf/api/premiumRemittance/edit'
,
method
:
'put'
,
data
:
data
})
}
//保费对账汇款记录列表
export
function
getPremiumRemittanceListApi
(
data
)
{
return
request
({
url
:
'/csf/api/premiumRemittance/page'
,
method
:
'post'
,
data
:
data
})
}
src/components/SearchForm/SearchForm.vue
View file @
574b9891
<
template
>
<
template
>
<el-form
ref=
"formRef"
:model=
"localModel"
:rules=
"formRules"
label-width=
"auto"
v-bind=
"$attrs"
<el-form
:validate-on-rule-change=
"false"
>
ref=
"formRef"
<el-row
:gutter=
"20"
>
:model=
"localModel"
<el-col
v-for=
"item in visibleConfig"
:key=
"item.prop"
:span=
"item.span || 6"
>
:rules=
"formRules"
<el-form-item
:label=
"item.label"
:prop=
"item.prop"
:class=
"
{ 'search-form-item': isSearch }"
label-width=
"auto"
:label-position="item.labelPosition || 'top'">
v-bind=
"$attrs"
<!-- Input -->
:validate-on-rule-change=
"false"
<el-input
v-if=
"item.type === 'input'"
v-model=
"localModel[item.prop]"
>
:placeholder=
"item.placeholder || `请输入$
{item.label}`" :clearable="true"
<el-row
:gutter=
"20"
>
@input="(val) => handleNumberInput(val, item)"
<el-col
v-for=
"item in visibleConfig"
:key=
"item.prop"
:span=
"item.span || 6"
>
@change="(val) => handleModelChange(val, item)" />
<el-form-item
:label=
"item.label"
<!-- Select (支持 dictType / api / options) -->
:prop=
"item.prop"
<el-select
v-else-if=
"item.type === 'select'"
v-model=
"localModel[item.prop]"
:class=
"
{ 'search-form-item': isSearch }"
:multiple=
"!!item.multiple"
:placeholder=
"item.placeholder || `请选择$
{item.label}`"
:label-position="item.labelPosition || 'top'"
:clearable="true" filterable :loading="remoteLoading[item.prop] || false"
>
@change="(val) => handleModelChange(val, item)" @focus="() => loadRemoteOptions(item)"
<!-- Input -->
@filter-change="(keyword) => handleFilterChange(keyword, item)">
<el-input
<el-option
v-for=
"opt in getSelectOptions(item)"
:key=
"opt.value"
:label=
"opt.label"
v-if=
"item.type === 'input'"
:value=
"opt.value"
/>
v-model=
"localModel[item.prop]"
</el-select>
:placeholder=
"item.placeholder || `请输入$
{item.label}`"
<!-- Date -->
:clearable="true"
<el-date-picker
v-else-if=
"item.type === 'date'"
v-model=
"localModel[item.prop]"
type=
"date"
:disabled="item.disabled"
:placeholder=
"`选择$
{item.label}`" :value-format="item.valueFormat || 'YYYY-MM-DD'"
@input="val => handleNumberInput(val, item)"
style="width: 100%" :disabled-date="getDisabledDateFn(item)"
@change="val => handleModelChange(val, item)"
@change="(val) => handleModelChange(val, item)" />
/>
<!-- Month -->
<el-date-picker
v-else-if=
"item.type === 'month'"
v-model=
"localModel[item.prop]"
type=
"month"
<!-- Select (支持 dictType / api / options) -->
:placeholder=
"`选择$
{item.label}`" :value-format="item.valueFormat || 'YYYY-MM'"
<el-select
style="width: 100%" :disabled-date="getDisabledDateFn(item)"
v-else-if=
"item.type === 'select'"
@change="(val) => handleModelChange(val, item)" />
v-model=
"localModel[item.prop]"
:multiple=
"!!item.multiple"
:placeholder=
"item.placeholder || `请选择$
{item.label}`"
<!-- Daterange -->
:clearable="true"
<el-date-picker
v-else-if=
"item.type === 'daterange'"
v-model=
"localModel[item.prop]"
filterable
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
:disabled="item.disabled"
:value-format=
"item.valueFormat || 'YYYY-MM-DD'"
:disabled-date=
"getDisabledDateFn(item)"
:loading="remoteLoading[item.prop] || false"
style=
"width: 100%"
@
change=
"(val) => handleModelChange(val, item)"
/>
@change="val => handleModelChange(val, item)"
@focus="() => loadRemoteOptions(item)"
<!-- Checkbox Group -->
@filter-change="keyword => handleFilterChange(keyword, item)"
<el-checkbox-group
v-else-if=
"item.type === 'checkbox-group'"
v-model=
"localModel[item.prop]"
>
@
change=
"(val) => handleModelChange(val, item)"
>
<el-option
<el-checkbox
v-for=
"opt in getSelectOptions(item)"
:key=
"opt.value"
:label=
"opt.value"
>
v-for=
"opt in getSelectOptions(item)"
{{
opt
.
label
}}
:key=
"opt.value"
</el-checkbox>
:label=
"opt.label"
</el-checkbox-group>
:value=
"opt.value"
<!-- textarea -->
/>
<el-input
v-else-if=
"item.type === 'textarea'"
v-model=
"localModel[item.prop]"
style=
"width: 240px"
</el-select>
autosize
type=
"textarea"
placeholder=
"请输入"
:clearable=
"true"
<!-- Date -->
@
change=
"(val) => handleModelChange(val, item)"
/>
<el-date-picker
<span
v-else
>
不支持的类型:
{{
item
.
type
}}
</span>
v-else-if=
"item.type === 'date'"
</el-form-item>
v-model=
"localModel[item.prop]"
</el-col>
type=
"date"
</el-row>
:placeholder=
"`选择$
{item.label}`"
</el-form>
:disabled="item.disabled"
:value-format="item.valueFormat || 'YYYY-MM-DD'"
style="width: 100%"
:disabled-date="getDisabledDateFn(item)"
@change="val => handleModelChange(val, item)"
/>
<!-- Month -->
<el-date-picker
v-else-if=
"item.type === 'month'"
v-model=
"localModel[item.prop]"
type=
"month"
:placeholder=
"`选择$
{item.label}`"
:value-format="item.valueFormat || 'YYYY-MM'"
style="width: 100%"
:disabled="item.disabled"
:disabled-date="getDisabledDateFn(item)"
@change="val => handleModelChange(val, item)"
/>
<!-- Daterange -->
<el-date-picker
v-else-if=
"item.type === 'daterange'"
v-model=
"localModel[item.prop]"
type=
"daterange"
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
:value-format=
"item.valueFormat || 'YYYY-MM-DD'"
:disabled=
"item.disabled"
:disabled-date=
"getDisabledDateFn(item)"
style=
"width: 100%"
@
change=
"val => handleModelChange(val, item)"
/>
<!-- Checkbox Group -->
<el-checkbox-group
v-else-if=
"item.type === 'checkbox-group'"
v-model=
"localModel[item.prop]"
:disabled=
"item.disabled"
@
change=
"val => handleModelChange(val, item)"
>
<el-checkbox
v-for=
"opt in getSelectOptions(item)"
:key=
"opt.value"
:label=
"opt.value"
>
{{
opt
.
label
}}
</el-checkbox>
</el-checkbox-group>
<!-- textarea -->
<el-input
v-else-if=
"item.type === 'textarea'"
v-model=
"localModel[item.prop]"
style=
"width: 240px"
autosize
:disabled=
"item.disabled"
type=
"textarea"
placeholder=
"请输入"
:clearable=
"true"
@
change=
"val => handleModelChange(val, item)"
/>
<!-- Upload 回显值得时候数据格式至少是[
{url: '必须要传', name: 'name不是必须的根据需要传值'}]-->
<el-upload
v-else-if=
"item.type === 'upload'"
v-model:file-list=
"localModel[item.prop]"
:action=
"item.action"
:headers=
"item.headers"
:multiple=
"!!item.multiple"
:limit=
"item.limit || (item.multiple ? 999 : 1)"
:accept=
"item.accept"
:list-type=
"item.listType || 'text'"
:disabled=
"item.disabled"
:auto-upload=
"true"
:show-file-list=
"item.showFileList"
:on-exceed=
"handleExceed"
:before-upload=
"file => beforeUpload(file, item)"
:on-success=
"(res, file, fileList) => handleUploadSuccess(res, file, fileList, item)"
:on-error=
"(err, file, fileList) => handleUploadError(err, file, fileList, item)"
:on-remove=
"(file, fileList) => handleUploadRemove(file, fileList, item)"
>
<el-button
size=
"small"
type=
"primary"
:link=
"item.link"
:disabled=
"item.disabled"
>
{{
item
.
uploadType
===
'image'
?
'点击上传图片'
:
'点击上传文件'
}}
</el-button>
<template
#
tip
v-if=
"item.maxSize || item.accept"
>
<div
class=
"el-upload__tip"
>
<span
v-if=
"item.maxSize"
>
大小不超过
{{
formatFileSize
(
item
.
maxSize
)
}}
</span>
<span
v-if=
"item.accept"
>
支持格式:
{{
item
.
accept
}}
</span>
</div>
</
template
>
</el-upload>
<span
v-else
>
不支持的类型: {{ item.type }}
</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
,
watch
,
onMounted
,
nextTick
,
computed
}
from
'vue'
import
{
ref
,
watch
,
onMounted
,
nextTick
,
computed
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
import
{
ElMessage
}
from
'element-plus'
import
{
Upload
}
from
'@element-plus/icons-vue'
// 🔑 引入你的字典方法
// 🔑 引入你的字典方法
import
useDictStore
from
'@/store/modules/dict'
import
useDictStore
from
'@/store/modules/dict'
import
{
getDicts
}
from
'@/api/system/dict/data'
import
{
getDicts
}
from
'@/api/system/dict/data'
import
request
from
'@/utils/request'
import
request
from
'@/utils/request'
import
dayjs
from
'dayjs'
import
dayjs
from
'dayjs'
// ==================== 上传文件 ====================
// 文件大小格式化
function
formatFileSize
(
bytes
)
{
if
(
bytes
===
0
)
return
'0 Bytes'
const
k
=
1024
const
sizes
=
[
'Bytes'
,
'KB'
,
'MB'
,
'GB'
]
const
i
=
Math
.
floor
(
Math
.
log
(
bytes
)
/
Math
.
log
(
k
))
return
parseFloat
((
bytes
/
Math
.
pow
(
k
,
i
)).
toFixed
(
2
))
+
' '
+
sizes
[
i
]
}
function
beforeUpload
(
file
,
item
)
{
// 检查文件大小
if
(
item
.
maxSize
&&
file
.
size
>
item
.
maxSize
)
{
ElMessage
.
error
(
`文件
${
file
.
name
}
超出大小限制(最大
${
formatFileSize
(
item
.
maxSize
)}
)`
)
return
false
}
// 检查类型(accept 已由浏览器限制,但可二次校验)
if
(
item
.
accept
)
{
const
allowed
=
item
.
accept
.
split
(
','
).
map
(
ext
=>
ext
.
trim
().
toLowerCase
())
const
fileExt
=
'.'
+
file
.
name
.
split
(
'.'
).
pop
().
toLowerCase
()
if
(
!
allowed
.
includes
(
fileExt
))
{
ElMessage
.
error
(
`文件类型不支持,仅支持:
${
item
.
accept
}
`
)
return
false
}
}
return
true
}
function
handleUploadSuccess
(
response
,
file
,
fileList
,
item
)
{
// 假设你的后端返回格式:{ code: 200, data: { url: '...', name: 'xxx.pdf' } }
// 你可以通过 item.responseMap 自定义映射,这里先用通用方式
const
data
=
response
.
data
||
response
const
url
=
data
.
url
||
data
.
fileUrl
||
data
.
path
const
name
=
data
.
name
||
data
.
fileName
||
file
.
name
if
(
!
url
)
{
ElMessage
.
error
(
'上传成功但未返回文件地址'
)
return
}
// 找到刚上传的文件(通过 uid),替换其 url
const
targetFile
=
fileList
.
find
(
f
=>
f
.
uid
===
file
.
uid
)
if
(
targetFile
)
{
targetFile
.
url
=
url
targetFile
.
name
=
name
}
// 触发 model 更新
handleModelChange
([...
fileList
],
item
)
ElMessage
.
success
(
`文件
${
file
.
name
}
上传成功`
)
}
function
handleExceed
(
files
,
fileList
)
{
ElMessage
.
warning
(
'超出文件数量限制'
)
}
function
handleUploadError
(
error
,
file
,
fileList
,
item
)
{
ElMessage
.
error
(
`文件
${
file
.
name
}
上传失败`
)
console
.
error
(
'Upload error:'
,
error
)
}
function
handleUploadRemove
(
file
,
fileList
,
item
)
{
// 用户删除文件时,同步更新 model
handleModelChange
([...
fileList
],
item
)
}
// ==================== 工具函数:深拷贝配置(保留函数) ====================
// ==================== 工具函数:深拷贝配置(保留函数) ====================
function
deepCloneConfig
(
obj
)
{
function
deepCloneConfig
(
obj
)
{
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
if
(
Array
.
isArray
(
obj
))
return
obj
.
map
(
deepCloneConfig
)
if
(
Array
.
isArray
(
obj
))
return
obj
.
map
(
deepCloneConfig
)
const
cloned
=
{}
const
cloned
=
{}
for
(
const
key
in
obj
)
{
for
(
const
key
in
obj
)
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
obj
,
key
))
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
obj
,
key
))
{
const
val
=
obj
[
key
]
const
val
=
obj
[
key
]
cloned
[
key
]
=
typeof
val
===
'function'
?
val
:
deepCloneConfig
(
val
)
cloned
[
key
]
=
typeof
val
===
'function'
?
val
:
deepCloneConfig
(
val
)
}
}
}
return
cloned
}
return
cloned
}
}
function
parseToDate
(
str
)
{
function
parseToDate
(
str
)
{
if
(
!
str
)
return
null
if
(
!
str
)
return
null
if
(
str
===
'today'
)
{
if
(
str
===
'today'
)
{
return
dayjs
().
startOf
(
'day'
)
return
dayjs
().
startOf
(
'day'
)
}
}
if
(
typeof
str
===
'string'
)
{
if
(
typeof
str
===
'string'
)
{
const
d
=
dayjs
(
str
)
const
d
=
dayjs
(
str
)
return
d
.
isValid
()
?
d
.
startOf
(
'day'
)
:
null
return
d
.
isValid
()
?
d
.
startOf
(
'day'
)
:
null
}
}
if
(
str
instanceof
Date
)
{
if
(
str
instanceof
Date
)
{
return
dayjs
(
str
).
startOf
(
'day'
)
return
dayjs
(
str
).
startOf
(
'day'
)
}
}
return
null
return
null
}
}
// ==================== 生成 disabledDate 函数 ====================
// ==================== 生成 disabledDate 函数 ====================
function
getDisabledDateFn
(
item
)
{
function
getDisabledDateFn
(
item
)
{
const
{
minDate
,
maxDate
}
=
item
const
{
minDate
,
maxDate
}
=
item
// 如果都没有限制,返回 null(不禁用任何日期)
// 如果都没有限制,返回 null(不禁用任何日期)
if
(
minDate
==
null
&&
maxDate
==
null
)
{
if
(
minDate
==
null
&&
maxDate
==
null
)
{
return
()
=>
false
return
()
=>
false
}
}
return
(
date
)
=>
{
return
date
=>
{
const
currentDate
=
dayjs
(
date
).
startOf
(
'day'
)
const
currentDate
=
dayjs
(
date
).
startOf
(
'day'
)
let
minD
=
null
let
minD
=
null
let
maxD
=
null
let
maxD
=
null
// 解析最小日期
// 解析最小日期
if
(
minDate
!=
null
)
{
if
(
minDate
!=
null
)
{
if
(
typeof
minDate
===
'function'
)
{
if
(
typeof
minDate
===
'function'
)
{
const
val
=
minDate
(
localModel
.
value
)
const
val
=
minDate
(
localModel
.
value
)
minD
=
parseToDate
(
val
)
minD
=
parseToDate
(
val
)
}
else
{
}
else
{
minD
=
parseToDate
(
minDate
)
minD
=
parseToDate
(
minDate
)
}
}
}
}
// 解析最大日期
// 解析最大日期
if
(
maxDate
!=
null
)
{
if
(
maxDate
!=
null
)
{
if
(
typeof
maxDate
===
'function'
)
{
if
(
typeof
maxDate
===
'function'
)
{
const
val
=
maxDate
(
localModel
.
value
)
const
val
=
maxDate
(
localModel
.
value
)
maxD
=
parseToDate
(
val
)
maxD
=
parseToDate
(
val
)
}
else
{
}
else
{
maxD
=
parseToDate
(
maxDate
)
maxD
=
parseToDate
(
maxDate
)
}
}
}
}
// 判断是否被禁用
// 判断是否被禁用
if
(
minD
&&
currentDate
.
isBefore
(
minD
))
{
if
(
minD
&&
currentDate
.
isBefore
(
minD
))
{
return
true
return
true
}
}
if
(
maxD
&&
currentDate
.
isAfter
(
maxD
))
{
if
(
maxD
&&
currentDate
.
isAfter
(
maxD
))
{
return
true
return
true
}
return
false
}
}
return
false
}
}
}
// ==================== Props & Emits ====================
// ==================== Props & Emits ====================
const
props
=
defineProps
({
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
config
:
{
type
:
Array
,
default
:
()
=>
[]
},
config
:
{
type
:
Array
,
default
:
()
=>
[]
},
isSearch
:
{
type
:
Boolean
,
default
:
false
}
isSearch
:
{
type
:
Boolean
,
default
:
false
}
})
})
const
emit
=
defineEmits
([
'update:modelValue'
,
'update'
])
const
emit
=
defineEmits
([
'update:modelValue'
,
'update'
])
...
@@ -157,61 +312,61 @@ const remoteLoading = ref({}) // { prop: boolean }
...
@@ -157,61 +312,61 @@ const remoteLoading = ref({}) // { prop: boolean }
// ==================== 条件显隐 & 规则 ====================
// ==================== 条件显隐 & 规则 ====================
const
visibleConfig
=
computed
(()
=>
{
const
visibleConfig
=
computed
(()
=>
{
return
internalConfig
.
value
.
filter
(
item
=>
{
return
internalConfig
.
value
.
filter
(
item
=>
{
if
(
typeof
item
.
visible
===
'function'
)
{
if
(
typeof
item
.
visible
===
'function'
)
{
return
item
.
visible
(
localModel
.
value
)
return
item
.
visible
(
localModel
.
value
)
}
}
return
true
return
true
})
})
})
})
const
formRules
=
computed
(()
=>
{
const
formRules
=
computed
(()
=>
{
const
rules
=
{}
const
rules
=
{}
visibleConfig
.
value
.
forEach
(
item
=>
{
visibleConfig
.
value
.
forEach
(
item
=>
{
if
(
item
.
rules
)
rules
[
item
.
prop
]
=
item
.
rules
if
(
item
.
rules
)
rules
[
item
.
prop
]
=
item
.
rules
})
})
return
rules
return
rules
})
})
// 1. 外部 modelValue 变化时,安全同步(仅当内容不同时)
// 1. 外部 modelValue 变化时,安全同步(仅当内容不同时)
// 监听 config 变化(支持动态 config)
// 监听 config 变化(支持动态 config)
watch
(
watch
(
()
=>
props
.
config
,
()
=>
props
.
config
,
(
newConfig
)
=>
{
newConfig
=>
{
if
(
!
newConfig
||
newConfig
.
length
===
0
)
return
if
(
!
newConfig
||
newConfig
.
length
===
0
)
return
internalConfig
.
value
=
deepCloneConfig
(
newConfig
)
// 构建初始模型
const
initialModel
=
{}
for
(
const
item
of
internalConfig
.
value
)
{
const
key
=
item
.
prop
// 优先用父传值,否则用默认值
if
(
props
.
modelValue
?.[
key
]
!==
undefined
)
{
initialModel
[
key
]
=
props
.
modelValue
[
key
]
}
else
if
(
item
.
multiple
||
[
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
))
{
initialModel
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
initialModel
[
key
]
=
item
.
defaultValue
??
''
}
}
// ✅ 在这里同步 modelValue(包括 extra 字段)
internalConfig
.
value
=
deepCloneConfig
(
newConfig
)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
},
// 构建初始模型
{
immediate
:
true
}
const
initialModel
=
{}
for
(
const
item
of
internalConfig
.
value
)
{
const
key
=
item
.
prop
// 优先用父传值,否则用默认值
if
(
props
.
modelValue
?.[
key
]
!==
undefined
)
{
initialModel
[
key
]
=
props
.
modelValue
[
key
]
}
else
if
(
item
.
multiple
||
[
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
))
{
initialModel
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
initialModel
[
key
]
=
item
.
defaultValue
??
''
}
}
// ✅ 在这里同步 modelValue(包括 extra 字段)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
},
{
immediate
:
true
}
)
)
console
.
log
(
'🚀 子组件 props.modelValue 初始值:'
,
props
.
modelValue
)
console
.
log
(
'🚀 子组件 props.modelValue 初始值:'
,
props
.
modelValue
)
// 监听 modelValue(用于后续外部更新)
// 监听 modelValue(用于后续外部更新)
watch
(
watch
(
()
=>
props
.
modelValue
,
()
=>
props
.
modelValue
,
(
newVal
)
=>
{
newVal
=>
{
if
(
!
newVal
||
!
internalConfig
.
value
)
return
if
(
!
newVal
||
!
internalConfig
.
value
)
return
// ✅ 同样使用 sync 函数
// ✅ 同样使用 sync 函数
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
},
},
{
deep
:
true
}
{
deep
:
true
}
)
)
// 提取同步逻辑
// 提取同步逻辑
...
@@ -219,7 +374,8 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -219,7 +374,8 @@ function syncModelFromProps(newModelValue, newConfig) {
if
(
!
newModelValue
||
!
newConfig
)
return
{}
if
(
!
newModelValue
||
!
newConfig
)
return
{}
const
synced
=
{}
const
synced
=
{}
console
.
log
(
'newConfig---------------------'
,
newConfig
)
console
.
log
(
'newModelValue---------------------'
,
newModelValue
)
// 1. 同步主字段
// 1. 同步主字段
for
(
const
item
of
newConfig
)
{
for
(
const
item
of
newConfig
)
{
const
key
=
item
.
prop
const
key
=
item
.
prop
...
@@ -231,16 +387,29 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -231,16 +387,29 @@ function syncModelFromProps(newModelValue, newConfig) {
synced
[
key
]
=
item
.
defaultValue
??
''
synced
[
key
]
=
item
.
defaultValue
??
''
}
}
}
}
// 2. 同步 extra 字段(通过 options 反查)
// 2. 同步 extra 字段(从 newModelValue 中的 sourceField 重新计算)
for
(
const
item
of
newConfig
)
{
for
(
const
item
of
newConfig
)
{
const
sourceField
=
item
.
prop
const
extraMap
=
item
.
onChangeExtraFields
const
extraMap
=
item
.
onChangeExtraFields
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
const
sourceObj
=
newModelValue
[
sourceField
]
const
prop
=
item
.
prop
const
idValue
=
newModelValue
[
prop
]
// e.g. 2
let
sourceObj
=
null
// 情况1: 如果 newModelValue[prop] 本身就是对象 → 直接用(兼容旧逻辑)
if
(
idValue
&&
typeof
idValue
===
'object'
)
{
sourceObj
=
idValue
}
// 情况2: 如果是 primitive(string/number),且有 options → 反查
else
if
(
Array
.
isArray
(
item
.
options
)
&&
idValue
!==
undefined
&&
idValue
!==
null
)
{
// 默认用 option.value 匹配,可配置 valueKey
const
valueKey
=
item
.
valueKey
||
'value'
sourceObj
=
item
.
options
.
find
(
opt
=>
opt
[
valueKey
]
===
idValue
)
}
// 如果找到了 sourceObj,就提取 extra 字段
if
(
sourceObj
&&
typeof
sourceObj
===
'object'
)
{
if
(
sourceObj
&&
typeof
sourceObj
===
'object'
)
{
// newModelValue 中有 sourceField → 重新计算 extra
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
const
val
=
getNestedValue
(
sourceObj
,
subPath
)
const
val
=
getNestedValue
(
sourceObj
,
subPath
)
if
(
val
!==
undefined
)
{
if
(
val
!==
undefined
)
{
...
@@ -267,35 +436,38 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -267,35 +436,38 @@ function syncModelFromProps(newModelValue, newConfig) {
}
}
}
}
// 4. 保留
其他不在 config 中的
字段
// 4. 保留
newModelValue 中已有的 extra 字段和其他额外
字段
for
(
const
key
in
newModelValue
)
{
for
(
const
key
in
newModelValue
)
{
if
(
// 如果已经同步过了(比如主字段或第2步写入的extra),跳过
!
synced
.
hasOwnProperty
(
key
)
&&
if
(
synced
.
hasOwnProperty
(
key
))
continue
!
newConfig
.
some
(
item
=>
item
.
prop
===
key
)
)
{
// 判断是否是某个 extra 目标字段
const
isExtraTarget
=
newConfig
.
some
(
item
=>
const
isExtraTarget
=
newConfig
.
some
(
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
item
=>
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
)
)
if
(
!
isExtraTarget
)
{
// 如果是 extra 字段,且 newModelValue 里有值 → 保留它!
synced
[
key
]
=
newModelValue
[
key
]
if
(
isExtraTarget
)
{
}
synced
[
key
]
=
newModelValue
[
key
]
}
// 如果不是 extra,也不是主字段 → 也保留(兼容 hidden 字段等)
else
if
(
!
newConfig
.
some
(
item
=>
item
.
prop
===
key
))
{
synced
[
key
]
=
newModelValue
[
key
]
}
}
}
}
console
.
log
(
'🚀 子组件 props.modelValue 同步后:'
,
synced
)
return
synced
return
synced
}
}
function
getNestedValue
(
obj
,
path
)
{
function
getNestedValue
(
obj
,
path
)
{
return
path
.
split
(
'.'
).
reduce
((
current
,
key
)
=>
current
?.[
key
],
obj
)
return
path
.
split
(
'.'
).
reduce
((
current
,
key
)
=>
current
?.[
key
],
obj
)
}
}
// 当字典加载完成时,触发同步
// 当字典加载完成时,触发同步
function
markDictLoaded
(
prop
)
{
function
markDictLoaded
(
prop
)
{
dictLoaded
.
value
.
add
(
prop
)
dictLoaded
.
value
.
add
(
prop
)
// 尝试同步该字段
// 尝试同步该字段
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
}
}
}
}
// 2. 用户操作导致 localModel 变化时,emit(防抖可选)
// 2. 用户操作导致 localModel 变化时,emit(防抖可选)
...
@@ -339,294 +511,299 @@ function handleModelChange(value, item) {
...
@@ -339,294 +511,299 @@ function handleModelChange(value, item) {
}
}
// 辅助函数:浅比较两个对象
// 辅助函数:浅比较两个对象
function
isEqualShallow
(
a
,
b
)
{
function
isEqualShallow
(
a
,
b
)
{
const
keysA
=
Object
.
keys
(
a
)
const
keysA
=
Object
.
keys
(
a
)
const
keysB
=
Object
.
keys
(
b
)
const
keysB
=
Object
.
keys
(
b
)
if
(
keysA
.
length
!==
keysB
.
length
)
return
false
if
(
keysA
.
length
!==
keysB
.
length
)
return
false
for
(
let
key
of
keysA
)
{
for
(
let
key
of
keysA
)
{
if
(
a
[
key
]
!==
b
[
key
])
return
false
if
(
a
[
key
]
!==
b
[
key
])
return
false
}
}
return
true
return
true
}
}
// ==================== 加载字典选项 ====================
// ==================== 加载字典选项 ====================
async
function
loadDictOptions
(
dictType
)
{
async
function
loadDictOptions
(
dictType
)
{
const
dictStore
=
useDictStore
()
const
dictStore
=
useDictStore
()
let
options
=
dictStore
.
getDict
(
dictType
)
let
options
=
dictStore
.
getDict
(
dictType
)
if
(
options
&&
options
.
length
>
0
)
{
if
(
options
&&
options
.
length
>
0
)
{
return
options
return
options
}
}
try
{
try
{
const
resp
=
await
getDicts
(
dictType
)
const
resp
=
await
getDicts
(
dictType
)
options
=
resp
.
data
.
map
(
p
=>
({
options
=
resp
.
data
.
map
(
p
=>
({
label
:
p
.
itemLabel
,
label
:
p
.
itemLabel
,
value
:
p
.
itemValue
,
value
:
p
.
itemValue
,
raw
:
p
raw
:
p
}))
}))
dictStore
.
setDict
(
dictType
,
options
)
dictStore
.
setDict
(
dictType
,
options
)
return
options
return
options
}
catch
(
err
)
{
}
catch
(
err
)
{
console
.
error
(
`加载字典
${
dictType
}
失败`
,
err
)
console
.
error
(
`加载字典
${
dictType
}
失败`
,
err
)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
return
[]
return
[]
}
}
}
}
// ==================== 初始化 ====================
// ==================== 初始化 ====================
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
internalConfig
.
value
=
deepCloneConfig
(
props
.
config
)
internalConfig
.
value
=
deepCloneConfig
(
props
.
config
)
const
initialData
=
{}
const
dictTypePromises
=
[]
const
apiPromises
=
[]
// ← 新增:收集 api 加载 promise
for
(
const
item
of
internalConfig
.
value
)
{
const
key
=
item
.
prop
if
(
localModel
.
value
[
key
]
==
null
)
{
if
(
item
.
multiple
)
{
initialData
[
key
]
=
item
.
defaultValue
??
[]
}
else
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
))
{
initialData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
initialData
[
key
]
=
item
.
defaultValue
??
''
}
}
// 预加载 dictType
const
initialData
=
{}
if
(
item
.
type
===
'select'
&&
item
.
dictType
)
{
const
dictTypePromises
=
[]
dictTypePromises
.
push
(
const
apiPromises
=
[]
// ← 新增:收集 api 加载 promise
loadDictOptions
(
item
.
dictType
).
then
(
opts
=>
{
for
(
const
item
of
internalConfig
.
value
)
{
remoteOptions
.
value
[
key
]
=
opts
const
key
=
item
.
prop
markDictLoaded
(
key
)
// ← 立即标记已加载
if
(
localModel
.
value
[
key
]
==
null
)
{
})
if
(
item
.
multiple
)
{
)
initialData
[
key
]
=
item
.
defaultValue
??
[]
}
// 预加载 api(远程接口)← 关键新增!
}
else
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
))
{
else
if
(
item
.
type
===
'select'
&&
item
.
api
)
{
initialData
[
key
]
=
item
.
defaultValue
??
[]
apiPromises
.
push
(
}
else
{
loadRemoteOptionsForInit
(
item
)
// ← 专门用于初始化的加载函数
initialData
[
key
]
=
item
.
defaultValue
??
''
)
}
}
else
if
(
item
.
type
===
'select'
&&
item
.
options
)
{
remoteOptions
.
value
[
key
]
=
[...
item
.
options
]
markDictLoaded
(
key
)
}
// api 类型:延迟加载(focus 时)
}
}
if
(
Object
.
keys
(
initialData
).
length
>
0
)
{
// 预加载 dictType
localModel
.
value
=
{
...
localModel
.
value
,
...
initialData
}
if
(
item
.
type
===
'select'
&&
item
.
dictType
)
{
dictTypePromises
.
push
(
loadDictOptions
(
item
.
dictType
).
then
(
opts
=>
{
remoteOptions
.
value
[
key
]
=
opts
markDictLoaded
(
key
)
// ← 立即标记已加载
})
)
}
// 预加载 api(远程接口)← 关键新增!
else
if
(
item
.
type
===
'select'
&&
item
.
api
)
{
apiPromises
.
push
(
loadRemoteOptionsForInit
(
item
)
// ← 专门用于初始化的加载函数
)
}
else
if
(
item
.
type
===
'select'
&&
item
.
options
)
{
remoteOptions
.
value
[
key
]
=
[...
item
.
options
]
markDictLoaded
(
key
)
}
}
// api 类型:延迟加载(focus 时)
}
if
(
Object
.
keys
(
initialData
).
length
>
0
)
{
localModel
.
value
=
{
...
localModel
.
value
,
...
initialData
}
}
// 等待所有字典加载完成
// 等待所有字典加载完成
await
Promise
.
allSettled
(
dictTypePromises
)
await
Promise
.
allSettled
(
dictTypePromises
)
})
})
// ==================== 获取 select 选项 ====================
// ==================== 获取 select 选项 ====================
function
getSelectOptions
(
item
)
{
function
getSelectOptions
(
item
)
{
const
key
=
item
.
prop
const
key
=
item
.
prop
// 字典选项
if
(
item
.
dictType
||
item
.
api
)
{
// 字典选项
// 字典选项
if
(
item
.
dictType
||
item
.
api
)
{
return
(
remoteOptions
.
value
[
key
]
||
[]).
map
(
opt
=>
({
// 字典选项
value
:
opt
.
value
,
return
(
remoteOptions
.
value
[
key
]
||
[]).
map
(
opt
=>
({
label
:
opt
.
label
,
value
:
opt
.
value
,
raw
:
opt
.
raw
// ← 必须存 raw
label
:
opt
.
label
,
}))
raw
:
opt
.
raw
// ← 必须存 raw
}
else
if
(
item
.
options
)
{
}))
// 静态选项s
}
else
if
(
item
.
options
)
{
return
item
.
options
.
map
(
opt
=>
({
// 静态选项s
value
:
opt
.
value
,
return
item
.
options
.
map
(
opt
=>
({
label
:
opt
.
label
,
value
:
opt
.
value
,
raw
:
opt
.
raw
// 保留原始
label
:
opt
.
label
,
}))
raw
:
opt
.
raw
// 保留原始
}
}))
return
[]
}
return
[]
}
}
async
function
loadRemoteOptionsForInit
(
item
)
{
async
function
loadRemoteOptionsForInit
(
item
)
{
const
{
prop
,
api
,
requestParams
=
{}
}
=
item
const
{
prop
,
api
,
requestParams
=
{}
}
=
item
try
{
try
{
// 构造请求体:只传 requestParams,不传 keyword
// 构造请求体:只传 requestParams,不传 keyword
const
payload
=
{
const
payload
=
{
...(
requestParams
||
{})
...(
requestParams
||
{})
}
}
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
data
:
payload
})
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
res
=
await
request
({
const
newOptions
=
list
.
map
(
i
=>
({
url
:
api
,
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
method
:
'post'
,
label
:
i
[
item
.
labelKey
||
'label'
],
data
:
payload
raw
:
i
})
}))
remoteOptions
.
value
[
prop
]
=
newOptions
const
list
=
markDictLoaded
(
prop
)
// ← 关键:标记已加载
typeof
item
.
transform
===
'function'
}
catch
(
err
)
{
?
item
.
transform
(
res
)
ElMessage
.
error
(
`预加载
${
item
.
label
}
失败`
)
:
res
.
data
?.
records
||
res
.
data
||
[]
remoteOptions
.
value
[
prop
]
=
[]
}
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
newOptions
=
list
.
map
(
i
=>
({
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
}))
remoteOptions
.
value
[
prop
]
=
newOptions
markDictLoaded
(
prop
)
// ← 关键:标记已加载
}
catch
(
err
)
{
ElMessage
.
error
(
`预加载
${
item
.
label
}
失败`
)
remoteOptions
.
value
[
prop
]
=
[]
}
}
}
// ==================== 加载远程 API 选项(首次 focus 时加载,无搜索词) ====================
// ==================== 加载远程 API 选项(首次 focus 时加载,无搜索词) ====================
async
function
loadRemoteOptions
(
item
)
{
async
function
loadRemoteOptions
(
item
)
{
const
{
prop
,
api
,
requestParams
=
{},
keywordField
,
debounceWait
,
...
rest
}
=
item
const
{
prop
,
api
,
requestParams
=
{},
keywordField
,
debounceWait
,
...
rest
}
=
item
if
(
!
api
||
remoteOptions
.
value
[
prop
]?.
length
>
0
)
return
if
(
!
api
||
remoteOptions
.
value
[
prop
]?.
length
>
0
)
return
try
{
try
{
remoteLoading
.
value
[
prop
]
=
true
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:合并 requestParams + 分页(默认第一页)
// 构造请求体:合并 requestParams + 分页(默认第一页)
const
payload
=
{
const
payload
=
{
...(
requestParams
||
{})
...(
requestParams
||
{})
// 注意:此时无 keyword,所以不加 keywordField
// 注意:此时无 keyword,所以不加 keywordField
}
}
const
res
=
await
request
({
const
res
=
await
request
({
url
:
api
,
url
:
api
,
method
:
'post'
,
// ← 改为 POST
method
:
'post'
,
// ← 改为 POST
data
:
payload
// ← 参数放 body
data
:
payload
// ← 参数放 body
})
})
const
list
=
typeof
item
.
transform
===
'function'
const
list
=
?
item
.
transform
(
res
)
typeof
item
.
transform
===
'function'
:
res
.
data
?.
records
||
res
.
data
||
[]
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
newOptions
=
list
.
map
(
i
=>
({
// 建议:统一转成字符串(或根据 item.valueType 判断)
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
const
newOptions
=
list
.
map
(
i
=>
({
label
:
i
[
item
.
labelKey
||
'label'
],
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
raw
:
i
label
:
i
[
item
.
labelKey
||
'label'
],
}))
raw
:
i
remoteOptions
.
value
[
prop
]
=
newOptions
}))
// ✅ 关键:标记该字段字典已加载
remoteOptions
.
value
[
prop
]
=
newOptions
markDictLoaded
(
prop
)
// ✅ 关键:标记该字段字典已加载
}
catch
(
err
)
{
markDictLoaded
(
prop
)
ElMessage
.
error
(
`加载
${
item
.
label
}
失败`
)
}
catch
(
err
)
{
remoteOptions
.
value
[
prop
]
=
[]
ElMessage
.
error
(
`加载
${
item
.
label
}
失败`
)
}
finally
{
remoteOptions
.
value
[
prop
]
=
[]
remoteLoading
.
value
[
prop
]
=
false
}
finally
{
}
remoteLoading
.
value
[
prop
]
=
false
}
}
}
// ==================== 远程搜索(带关键词,防抖) ====================
// ==================== 远程搜索(带关键词,防抖) ====================
let
searchTimeout
=
null
let
searchTimeout
=
null
function
handleFilterChange
(
keyword
,
item
)
{
function
handleFilterChange
(
keyword
,
item
)
{
const
{
prop
,
api
,
requestParams
=
{},
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
const
{
prop
,
api
,
requestParams
=
{},
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
if
(
!
api
)
return
if
(
!
api
)
return
clearTimeout
(
searchTimeout
)
clearTimeout
(
searchTimeout
)
searchTimeout
=
setTimeout
(
async
()
=>
{
searchTimeout
=
setTimeout
(
async
()
=>
{
try
{
try
{
remoteLoading
.
value
[
prop
]
=
true
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:requestParams + 分页 + 动态关键词字段
// 构造请求体:requestParams + 分页 + 动态关键词字段
const
payload
=
{
const
payload
=
{
...(
requestParams
||
{}),
...(
requestParams
||
{}),
[
keywordField
]:
keyword
// ← 动态字段名,如 name / companyName
[
keywordField
]:
keyword
// ← 动态字段名,如 name / companyName
}
}
const
res
=
await
request
({
const
res
=
await
request
({
url
:
api
,
url
:
api
,
method
:
'post'
,
// ← POST 请求
method
:
'post'
,
// ← POST 请求
data
:
payload
// ← body 传参
data
:
payload
// ← body 传参
})
})
const
list
=
typeof
item
.
transform
===
'function'
const
list
=
?
item
.
transform
(
res
)
typeof
item
.
transform
===
'function'
:
res
.
data
?.
records
||
res
.
data
||
[]
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
value
:
i
[
item
.
valueKey
||
'value'
],
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
label
:
i
[
item
.
labelKey
||
'label'
],
value
:
i
[
item
.
valueKey
||
'value'
],
raw
:
i
// ← 保存完整对象
label
:
i
[
item
.
labelKey
||
'label'
],
}))
raw
:
i
// ← 保存完整对象
}
catch
(
err
)
{
}))
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
}
catch
(
err
)
{
}
finally
{
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
remoteLoading
.
value
[
prop
]
=
false
}
finally
{
}
remoteLoading
.
value
[
prop
]
=
false
},
debounceWait
)
}
},
debounceWait
)
}
}
// ==================== 数字输入处理 ====================
// ==================== 数字输入处理 ====================
function
handleNumberInput
(
value
,
item
)
{
function
handleNumberInput
(
value
,
item
)
{
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
if
(
!
prop
)
return
if
(
!
prop
)
return
let
result
=
String
(
value
??
''
).
trim
()
if
(
inputType
===
'integer'
)
{
let
result
=
String
(
value
??
''
).
trim
()
// 只保留数字
result
=
result
.
replace
(
/
[^\d]
/g
,
''
)
}
else
if
(
inputType
===
'decimal'
)
{
// 1. 只保留数字和小数点
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if
(
inputType
===
'integer'
)
{
if
(
result
.
startsWith
(
'.'
))
{
// 只保留数字
result
=
'0.'
+
result
.
slice
(
1
)
result
=
result
.
replace
(
/
[^\d]
/g
,
''
)
}
}
else
if
(
inputType
===
'decimal'
)
{
// 1. 只保留数字和小数点
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
// 3. 保证最多一个小数点
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
const
parts
=
result
.
split
(
'.'
)
if
(
result
.
startsWith
(
'.'
))
{
if
(
parts
.
length
>
2
)
{
result
=
'0.'
+
result
.
slice
(
1
)
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
}
}
// 4. 限制小数位数(但保留结尾的小数点!)
// 3. 保证最多一个小数点
if
(
result
.
includes
(
'.'
))
{
const
parts
=
result
.
split
(
'.'
)
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
if
(
parts
.
length
>
2
)
{
// 如果小数部分超过限制,截断
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
if
(
decPart
.
length
>
decimalDigits
)
{
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
}
// ✅ 不再删除结尾的 '.'
}
}
}
// 防止重复赋值(可选优化)
// 4. 限制小数位数(但保留结尾的小数点!)
if
(
localModel
.
value
[
prop
]
!==
result
)
{
if
(
result
.
includes
(
'.'
))
{
localModel
.
value
=
{
...
localModel
.
value
,
[
prop
]:
result
}
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
// 如果小数部分超过限制,截断
if
(
decPart
.
length
>
decimalDigits
)
{
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
}
// ✅ 不再删除结尾的 '.'
}
}
}
// 防止重复赋值(可选优化)
if
(
localModel
.
value
[
prop
]
!==
result
)
{
localModel
.
value
=
{
...
localModel
.
value
,
[
prop
]:
result
}
}
}
}
// ==================== 暴露方法 ====================
// ==================== 暴露方法 ====================
defineExpose
({
defineExpose
({
getFormData
()
{
getFormData
()
{
return
{
...
localModel
.
value
}
return
{
...
localModel
.
value
}
},
},
async
validate
()
{
async
validate
()
{
return
new
Promise
((
resolve
,
reject
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
formRef
.
value
?.
validate
((
valid
)
=>
{
formRef
.
value
?.
validate
(
valid
=>
{
if
(
valid
)
resolve
(
localModel
.
value
)
if
(
valid
)
resolve
(
localModel
.
value
)
else
reject
(
new
Error
(
'Validation failed'
))
else
reject
(
new
Error
(
'Validation failed'
))
})
})
})
})
},
},
resetForm
()
{
resetForm
()
{
const
resetData
=
{}
const
resetData
=
{}
internalConfig
.
value
.
forEach
(
item
=>
{
internalConfig
.
value
.
forEach
(
item
=>
{
const
key
=
item
.
prop
const
key
=
item
.
prop
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
resetData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
}
else
if
(
item
.
type
===
'upload'
)
{
resetData
[
key
]
=
item
.
defaultValue
??
''
resetData
[
key
]
=
item
.
defaultValue
??
[]
// upload 也是数组
}
}
else
{
})
resetData
[
key
]
=
item
.
defaultValue
??
''
localModel
.
value
=
{
...
resetData
}
}
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
})
}
localModel
.
value
=
{
...
resetData
}
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
}
})
})
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
.search-form-item
{
.search-form-item
{
margin-bottom
:
20px
;
margin-bottom
:
20px
;
}
}
</
style
>
</
style
>
\ No newline at end of file
src/utils/2.js
View file @
574b9891
{
{
"apiAppointmentInfoDto"
:
{
"paymentMethod"
:
"CHECK"
,
"isReferrerAccompany"
:
"1"
,
"paymentAmount"
:
"111"
,
"isUseCar"
:
"1"
,
"paymentCurrency"
:
"HKD"
,
"isOpenAccount"
:
"1"
,
"paymentRel"
:
"SBR"
,
"applyType"
:
"INVESTMENT"
,
"payer"
:
"111"
,
"signDate"
:
"2026-01-26 00:00:00"
,
"payingBank"
:
"bank_1002"
,
"arrivalTime"
:
"2026-01-28 09:40:00"
,
"paymentAccount"
:
"1111"
,
"departureTime"
:
"2026-02-05 09:40:00"
,
"currency"
:
""
,
"meetingPoint"
:
"INSURANCE_COMPANY"
,
"paymentVoucherList"
:
[
"signingAddress"
:
"886"
,
{
"hkMobile"
:
"886"
,
"fileName"
:
"icon5.png"
,
"bankName"
:
"中国工商银行"
,
"fileType"
:
"png"
,
"bankId"
:
"bank_1001"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png"
,
"materials"
:
"个人开户: 带好 身份证 和 实名手机 直接前往网点即可,多数情况十分钟内办结。
\
r
\
n
\
r
\
n对公开户:
\
r
\
n
\
r
\
n第一步: 致电目标银行网点对公业务部,预约并获取最新资料清单。
\
r
\
n
\
r
\
n第二步: 对照清单,准备好所有 原件 和 公章。
\
r
\
n
\
r
\
n第三步: 法定代表人(或授权经办人)携带所有材料,按约定时间前往银行办理。"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png"
"bankBranchName"
:
"886"
,
},
"openAccountStartTime"
:
"2026-01-05 00:00:00"
,
{
"openAccountEndTime"
:
"2026-01-20 00:00:00"
,
"fileName"
:
"cardSix1.png"
,
"openAccountLocation"
:
"886"
,
"fileType"
:
"png"
,
"isBuy"
:
"1"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png"
,
"isTj"
:
"0"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png"
"openAccountNotice"
:
"886"
,
}
"hkMobileCode"
:
"+244"
,
],
"objType"
:
"phone"
,
"accountVerificationList"
:
[
"key"
:
"hkMobile"
,
"phoneString"
:
"+244 886"
,
"referrerDtoList"
:
[
{
"id"
:
1768182062294
,
"span"
:
24
,
"email"
:
"886"
,
"phone"
:
"886"
,
"realName"
:
"赵风"
,
"userBizId"
:
"user_1Y3A18QwDYGMwZHp"
,
"userSaleBizId"
:
"user_sale_expand_6LipZ2uQYqIpv2vK"
}
],
"userSignDtoList"
:
[
{
"id"
:
1768182068586
,
"span"
:
24
,
"name"
:
"SW"
,
"practiceCode"
:
"886"
,
"phone"
:
"886"
,
"cardType"
:
"idCard"
,
"cardNo"
:
"886"
,
"email"
:
"886"
,
"userBizId"
:
"user_HNpzB3A0A72JMLVN"
,
"userSignBizId"
:
"user_sign_rrrrsae445556er"
}
],
"isSecond"
:
1
,
"customerBizId"
:
"customer_OgR6wHpkgvvlpsht"
,
"fnaBizId"
:
"fna_QK3X3ygfsaOKcYQW"
,
"fnaNo"
:
"CSF-B-20260112-0926"
},
"apiProductPlanInfoDto"
:
{
"apiProductPlanMainInfoDto"
:
{
"companyName"
:
"太平洋保险有限公司"
,
"companyId"
:
"insurance_company_MLXRsUzPBhG76P1X"
,
"insuranceTypeName"
:
"储蓄险"
,
"insuranceTypeId"
:
"2aba7e865b9b45deba77e0bcc7d6fa67"
,
"productLaunchMainName"
:
"附加产品001"
,
"productLaunchBizId"
:
"product_launch_1LcCgSXEsf0KfmQM"
,
"issueNumber"
:
"3"
,
"policyCurrency"
:
"GBP"
,
"paymentFrequency"
:
"YEAR"
,
"eachIssuePremium"
:
"886"
,
"policyLevy"
:
"886"
,
"isPrepay"
:
0
},
"apiProductPlanAdditionalInfoDtoList"
:
[]
},
"apiPolicyholderInfoDto"
:
{
"id"
:
23
,
"customerBizId"
:
"customer_OgR6wHpkgvvlpsht"
,
"nameCn"
:
"测试二"
,
"namePyEn"
:
"CESHIER"
,
"documentType"
:
"idCard"
,
"idNumber"
:
"411424199604041622"
,
"gender"
:
"2"
,
"birthday"
:
"1996-04-04 00:00:00"
,
"age"
:
29
,
"nationality"
:
"中国台湾"
,
"birthplace"
:
"886"
,
"isOtherCountry"
:
"1"
,
"apiTaxationDtoList"
:
[
{
"taxCountry"
:
"中国"
,
"taxId"
:
"886"
,
"show"
:
true
}
],
"smokingStatus"
:
"0"
,
"maritalStatus"
:
"SINGLE"
,
"educationLevel"
:
"UNIVERSITY"
,
"isRetirement"
:
"0"
,
"retirementAge"
:
null
,
"height"
:
"160"
,
"weight"
:
"50"
,
"bmi"
:
"19.53"
,
"riskAppetite"
:
"LOW"
,
"dependentsNum"
:
1
,
"mobileCode"
:
"+994"
,
"mobile"
:
"886"
,
"residenceMobileCode"
:
"+675"
,
"residenceMobile"
:
"886"
,
"landlineCode"
:
null
,
"landline"
:
"886"
,
"email"
:
"886"
,
"certificateAddress"
:
"886"
,
"mailingAddressCode"
:
"886"
,
"employmentStatus"
:
"1"
,
"csName"
:
"886"
,
"industry"
:
"886"
,
"currentMonthlyIncome"
:
886
,
"totalWorkingYears"
:
886
,
"currentTenure"
:
886
,
"position"
:
"886"
,
"companyMobileCode"
:
"+359"
,
"companyMobile"
:
"886"
,
"companyAddressCode"
:
"886"
,
"monthIncome"
:
886
,
"monthExpenditure"
:
886
,
"totalCurrentAssets"
:
886
,
"totalDebt"
:
885
,
"travel"
:
"886"
,
"exercise"
:
"VOLLEYBALL"
,
"game"
:
"STIMULATE"
,
"movieDrama"
:
"FANTASY"
,
"delicacy"
:
"886"
,
"addressList"
:
[
{
"type"
:
"mailingAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
},
{
"type"
:
"residentialAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
},
{
"type"
:
"companyAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
}
],
"remark"
:
null
,
"countryName"
:
"中国台湾"
,
"phoneString"
:
"+359 886"
,
"key"
:
"companyMobile"
,
"phoneCode"
:
"companyMobileCode"
,
"code"
:
"+359"
},
"apiInsurantInfoDto"
:
{
"id"
:
23
,
"customerBizId"
:
"customer_OgR6wHpkgvvlpsht"
,
"nameCn"
:
"测试二"
,
"namePyEn"
:
"CESHIER"
,
"documentType"
:
"idCard"
,
"idNumber"
:
"411424199604041622"
,
"gender"
:
"2"
,
"birthday"
:
"1996-04-04 00:00:00"
,
"age"
:
29
,
"nationality"
:
"中国台湾"
,
"birthplace"
:
"886"
,
"isOtherCountry"
:
"1"
,
"apiTaxationDtoList"
:
[
{
"taxCountry"
:
"中国"
,
"taxId"
:
"886"
,
"show"
:
true
}
],
"smokingStatus"
:
"0"
,
"maritalStatus"
:
"SINGLE"
,
"educationLevel"
:
"UNIVERSITY"
,
"isRetirement"
:
"0"
,
"retirementAge"
:
null
,
"height"
:
"160"
,
"weight"
:
"50"
,
"bmi"
:
"19.53"
,
"riskAppetite"
:
"LOW"
,
"dependentsNum"
:
1
,
"mobileCode"
:
"+994"
,
"mobile"
:
"886"
,
"residenceMobileCode"
:
"+675"
,
"residenceMobile"
:
"886"
,
"landlineCode"
:
null
,
"landline"
:
"886"
,
"email"
:
"886"
,
"certificateAddress"
:
"886"
,
"mailingAddressCode"
:
"886"
,
"employmentStatus"
:
"1"
,
"csName"
:
"886"
,
"industry"
:
"886"
,
"currentMonthlyIncome"
:
886
,
"totalWorkingYears"
:
886
,
"currentTenure"
:
886
,
"position"
:
"886"
,
"companyMobileCode"
:
"+359"
,
"companyMobile"
:
"886"
,
"companyAddressCode"
:
"886"
,
"monthIncome"
:
886
,
"monthExpenditure"
:
886
,
"totalCurrentAssets"
:
886
,
"totalDebt"
:
885
,
"travel"
:
"886"
,
"exercise"
:
"VOLLEYBALL"
,
"game"
:
"STIMULATE"
,
"movieDrama"
:
"FANTASY"
,
"delicacy"
:
"886"
,
"addressList"
:
[
{
"type"
:
"mailingAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
},
{
"type"
:
"residentialAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
},
{
"type"
:
"companyAddress"
,
"region"
:
"886"
,
"city"
:
"886"
,
"street"
:
"886"
,
"location"
:
"886"
,
"addressString"
:
"886886886886"
,
"objType"
:
"address"
}
],
"remark"
:
null
,
"policyholderRel"
:
"MYSELF"
,
"countryName"
:
"中国台湾"
,
"phoneString"
:
"+359 886"
,
"key"
:
"companyMobile"
,
"phoneCode"
:
"companyMobileCode"
,
"code"
:
"+359"
},
"apiBeneficiaryInfoFzDto"
:
{
"isLegalBeneficiary"
:
1
},
"apiSecondHolderInfoDto"
:
{
"isSecond"
:
1
,
"insurantRel"
:
"MYSELF"
,
"nameCn"
:
"测试二"
,
"namePyEn"
:
"CESHIER"
,
"documentType"
:
"idCard"
,
"idNumber"
:
"411424199604041622"
,
"gender"
:
"2"
,
"birthday"
:
"1996-04-04 00:00:00"
,
"benefitRatio"
:
""
},
"apiAnswerSaveRequest"
:
{
"questionnaireBizId"
:
"questionnaires_1001"
,
"objectBizId"
:
""
,
"answerSessionsDtoList"
:
[]
},
"materialDtoList"
:
[
{
"materialBizId"
:
"material_rPc0sDZurdDFfwSE"
,
"fileBizIdList"
:
[
"oss_file_jhoezRHKCMf5AHbU"
]
},
{
"materialBizId"
:
"material_NOM56VZmKXa8rwcf"
,
"fileBizIdList"
:
[]
},
{
"materialBizId"
:
"material_Clinlz7VnBLbg151"
,
"fileBizIdList"
:
[]
},
{
"materialBizId"
:
"material_IaSKzxQ73K5qQPUW"
,
"fileBizIdList"
:
[]
},
{
"materialBizId"
:
"material_pOLgtaGwIR5IkUBv"
,
"fileBizIdList"
:
[]
},
{
"materialBizId"
:
"material_h0sIbNWnklHI3dFL"
,
"fileBizIdList"
:
[]
},
{
{
"materialBizId"
:
"material_Txh02cNvT21QPOZH"
,
"fileName"
:
"icon6.png"
,
"fileBizIdList"
:
[]
"fileType"
:
"png"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/ce6f4781b8f3443f92eddbf13ecefe42.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/ce6f4781b8f3443f92eddbf13ecefe42.png"
},
},
{
{
"materialBizId"
:
"material_IeS4kTopn06fGzbe"
,
"fileName"
:
"icon4.png"
,
"fileBizIdList"
:
[]
"fileType"
:
"png"
,
},
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/24e9786aa52d48caba0bfc48e7f4f146.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/24e9786aa52d48caba0bfc48e7f4f146.png"
}
],
"apiPremiumRemittanceFileDtoList"
:
[
{
{
"materialBizId"
:
"material_ykfOPMkoBvxsy1Y8"
,
"fileName"
:
"icon5.png"
,
"fileBizIdList"
:
[]
"fileType"
:
"png"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/bca0400a90ae4c1da2373e3cf4de0fc7.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/bca0400a90ae4c1da2373e3cf4de0fc7.png"
},
},
{
{
"materialBizId"
:
"material_dGGFvKISJBz9OhZg"
,
"fileName"
:
"homeSelect1.png"
,
"fileBizIdList"
:
[]
"fileType"
:
"png"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/138932043c6244cb8a006c2ab81b4bab.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/138932043c6244cb8a006c2ab81b4bab.png"
}
}
]
],
"id"
:
1768282536268
}
}
src/views/sign/FnaList/components/fanForm.vue
View file @
574b9891
...
@@ -430,6 +430,7 @@ const handleRemoteSelectChange = async (row, column, father) => {
...
@@ -430,6 +430,7 @@ const handleRemoteSelectChange = async (row, column, father) => {
row
.
showSumInsured
=
false
row
.
showSumInsured
=
false
}
}
row
.
insuranceType
=
item
.
label
row
.
insuranceType
=
item
.
label
row
.
insuranceCategoryBizId
=
item
.
code
}
}
})
})
}
}
...
@@ -750,9 +751,16 @@ const setFormValue = (obj, formData) => {
...
@@ -750,9 +751,16 @@ const setFormValue = (obj, formData) => {
loading
.
value
=
true
loading
.
value
=
true
let
processedData
=
JSON
.
parse
(
JSON
.
stringify
(
formData
))
let
processedData
=
JSON
.
parse
(
JSON
.
stringify
(
formData
))
// 重疾险要加上row.showSumInsured = true便于控制重疾险保额输入框的显示
// 重疾险要加上row.showSumInsured = true便于控制重疾险保额输入框的显示
for
(
const
section
of
processedData
)
{
for
(
const
section
of
processedData
)
{
if
(
section
.
keyType
==
'Array'
)
{
if
(
section
.
keyType
==
'Array'
)
{
if
(
section
.
key
==
'existingSecurityOwner'
)
{
obj
[
section
.
key
].
forEach
(
item
=>
{
if
(
item
.
insuranceCategoryBizId
==
'CI'
)
{
item
.
showSumInsured
=
true
}
})
}
section
.
data
=
obj
[
section
.
key
]
?
obj
[
section
.
key
]
:
[]
section
.
data
=
obj
[
section
.
key
]
?
obj
[
section
.
key
]
:
[]
}
else
if
(
section
.
keyType
==
'Object'
)
{
}
else
if
(
section
.
keyType
==
'Object'
)
{
obj
[
section
.
key
].
premiumFundingSource
=
obj
[
section
.
key
].
premiumFundingSource
obj
[
section
.
key
].
premiumFundingSource
=
obj
[
section
.
key
].
premiumFundingSource
...
...
src/views/sign/appointment/components/productPlan.vue
View file @
574b9891
...
@@ -516,7 +516,10 @@ const searchSelectList = async (query, fieldKey) => {
...
@@ -516,7 +516,10 @@ const searchSelectList = async (query, fieldKey) => {
projectBizId
:
userStore
.
projectInfo
.
projectBizId
,
projectBizId
:
userStore
.
projectInfo
.
projectBizId
,
tenantBizId
:
userStore
.
projectInfo
.
tenantBizId
,
tenantBizId
:
userStore
.
projectInfo
.
tenantBizId
,
fieldBizId
:
'field_olk1qZe81qHHKXbw'
,
fieldBizId
:
'field_olk1qZe81qHHKXbw'
,
fieldValueBizId
:
'field_value_yXzTigvgUdRMFpoR'
fieldValueBizId
:
fieldKey
===
'productLaunchName'
?
'field_value_yXzTigvgUdRMFpoR'
:
'field_value_uOfJH5ucA2YwJpbn'
}
}
const
response
=
await
secondAdditonalList
(
params
)
const
response
=
await
secondAdditonalList
(
params
)
...
...
src/views/sign/policyReceipts/index.vue
View file @
574b9891
...
@@ -17,8 +17,6 @@
...
@@ -17,8 +17,6 @@
</
template
>
</
template
>
<!-- 列表区域 -->
<!-- 列表区域 -->
<
template
#
table
>
<
template
#
table
>
<!-- 统计信息卡片 -->
<el-table
<el-table
:data=
"tableData"
:data=
"tableData"
@
selection-change=
"handleSelectionChange"
@
selection-change=
"handleSelectionChange"
...
@@ -29,39 +27,31 @@
...
@@ -29,39 +27,31 @@
v-loading=
"loading"
v-loading=
"loading"
>
>
<el-table-column
type=
"selection"
width=
"40"
/>
<el-table-column
type=
"selection"
width=
"40"
/>
<el-table-column
prop=
"fortuneBizType"
label=
"应付单类型"
width=
"120"
sortable
>
<
!--
<
el-table-column
prop=
"fortuneBizType"
label=
"应付单类型"
width=
"120"
sortable
>
<template
#
default=
"
{ row }">
<template
#
default=
"
{ row }">
{{
getFortuneBizTypeLabel
(
row
.
fortuneBizType
)
}}
{{
getFortuneBizTypeLabel
(
row
.
fortuneBizType
)
}}
</
template
>
</
template
>
</el-table-column>
-->
<el-table-column
prop=
"policyNo"
label=
"保单号"
/>
<el-table-column
prop=
"receiptStatus"
label=
"回执状态"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'receipt_status'
,
row
.
receiptStatus
)
}}
</
template
>
</el-table-column>
</el-table-column>
<el-table-column
prop=
"policyNo"
label=
"保单号"
width=
"120"
sortable
/>
<el-table-column
prop=
"insurer"
label=
"保险公司"
/>
<el-table-column
prop=
"insuranceCompany"
label=
"保险公司"
width=
"120"
sortable
/>
<el-table-column
prop=
"policyHolder"
label=
"保单持有人"
/>
<el-table-column
<el-table-column
prop=
"createTime"
label=
"创建时间"
>
prop=
"commissionPaidAmount"
<
template
#
default=
"{ row }"
>
label=
"累积已入账金额"
{{
row
.
createTime
?
formatToDate
(
row
.
createTime
)
:
''
}}
width=
"120"
</
template
>
sortable
</el-table-column>
/>
<el-table-column
prop=
"commissionPaidRatio"
label=
"累积已入账比例"
width=
"120"
sortable
/>
<el-table-column
prop=
"receiptDate"
label=
"回执日期"
>
<el-table-column
prop=
"fortuneName"
label=
"出账项目"
width=
"130"
sortable
/>
<el-table-column
prop=
"fortunePeriod"
label=
"出账期数"
width=
"130"
sortable
/>
<el-table-column
prop=
"fortuneTotalPeriod"
label=
"总期数"
width=
"120"
sortable
/>
<el-table-column
prop=
"broker"
label=
"转介人"
width=
"130"
sortable
/>
<el-table-column
prop=
"team"
label=
"所属团队"
width=
"120"
sortable
/>
<el-table-column
prop=
"amount"
label=
"应出账金额"
width=
"140"
sortable
/>
<el-table-column
prop=
"currency"
label=
"出账币种"
width=
"130"
sortable
/>
<el-table-column
prop=
"fortunePaidAmount"
label=
"已出账金额"
width=
"120"
sortable
/>
<el-table-column
prop=
"fortuneUnpaidAmount"
label=
"剩余出账金额"
width=
"120"
sortable
/>
<el-table-column
prop=
"currentPaymentAmount"
label=
"本期出账金额"
width=
"120"
sortable
/>
<el-table-column
prop=
"fortuneUnpaidRatio"
label=
"剩余出账比例"
width=
"120"
sortable
/>
<el-table-column
prop=
"status"
label=
"出账状态"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_fortune_status'
,
row
.
status
)
}}
{{
row
.
receiptDate
?
formatToDate
(
row
.
receiptDate
)
:
''
}}
</
template
>
</
template
>
</el-table-column>
</el-table-column>
<el-table-column
prop=
"premium"
label=
"期交保费"
width=
"120"
sortable
/>
<el-table-column
prop=
"payoutDate"
label=
"出账日(实)"
width=
"120"
sortable
/>
<el-table-column
prop=
"remark"
label=
"备注"
width=
"120"
sortable
/>
<el-table-column
fixed=
"right"
label=
"操作"
min-width=
"120"
>
<el-table-column
fixed=
"right"
label=
"操作"
min-width=
"120"
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
>
...
@@ -84,15 +74,15 @@
...
@@ -84,15 +74,15 @@
</el-table>
</el-table>
</template>
</template>
</CommonPage>
</CommonPage>
<!-- 新增
出账检核
页面-->
<!-- 新增
保单回执
页面-->
<CommonDialog
<CommonDialog
dialogTitle=
"新增出账检核
"
:dialogTitle=
"receiptsDialogTitle
"
dialogWidth=
"80%"
dialogWidth=
"80%"
:openDialog=
"
addCheckRecordFormDialog
Flag"
:openDialog=
"
receipts
Flag"
:showAction=
"true"
:showAction=
"true"
:showClose=
"true"
:showClose=
"true"
@
close=
"
addCheckRecordFormDialog
Flag = false"
@
close=
"
receipts
Flag = false"
@
confirm=
"add
CheckRecordaddBatchapi
"
@
confirm=
"add
Receipts
"
>
>
<SearchForm
<SearchForm
ref=
"addCheckRecordFormRef"
ref=
"addCheckRecordFormRef"
...
@@ -137,7 +127,7 @@
...
@@ -137,7 +127,7 @@
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
,
reactive
}
from
'vue'
import
{
ref
,
reactive
,
watch
}
from
'vue'
import
CommonPage
from
'@/components/commonPage'
import
CommonPage
from
'@/components/commonPage'
import
CommonDialog
from
'@/components/commonDialog'
import
CommonDialog
from
'@/components/commonDialog'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
...
@@ -145,14 +135,18 @@ import { ElMessage } from 'element-plus'
...
@@ -145,14 +135,18 @@ import { ElMessage } from 'element-plus'
import
{
formatCurrency
}
from
'@/utils/number'
import
{
formatCurrency
}
from
'@/utils/number'
// 接口
// 接口
import
{
import
{
getPolicyFortuneList
,
addCheckRecordaddBatch
,
updatePayoutAmount
,
updatePayoutAmount
,
downloadPolicyFortuneAccount
}
from
'@/api/financial/commission'
}
from
'@/api/financial/commission'
import
{
getPolicyReceiptList
,
addPolicyReceipt
,
getPolicyReceipt
,
EditPolicyReceipt
}
from
'@/api/sign/policy'
import
useUserStore
from
'@/store/modules/user'
import
useUserStore
from
'@/store/modules/user'
import
{
loadDicts
,
getDictLabel
}
from
'@/utils/useDict'
import
{
loadDicts
,
getDictLabel
}
from
'@/utils/useDict'
import
{
getToken
}
from
'@/utils/auth'
const
{
proxy
}
=
getCurrentInstance
()
const
userStore
=
useUserStore
()
const
userStore
=
useUserStore
()
// 分页相关
// 分页相关
const
currentPage
=
ref
(
1
)
const
currentPage
=
ref
(
1
)
...
@@ -162,29 +156,36 @@ const loading = ref(false)
...
@@ -162,29 +156,36 @@ const loading = ref(false)
const
selectedRow
=
ref
(
null
)
const
selectedRow
=
ref
(
null
)
const
searchFormRef
=
ref
(
null
)
const
searchFormRef
=
ref
(
null
)
const
searchParams
=
ref
({})
const
searchParams
=
ref
({})
const
receiptsFlag
=
ref
(
false
)
//回执弹窗
const
receiptsDialogTitle
=
ref
(
'新增保单回执'
)
const
searchConfig
=
ref
([
const
searchConfig
=
ref
([
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'appointmentNo'
,
label
:
'预约编号'
},
{
type
:
'input'
,
prop
:
'policyNo'
,
prop
:
'policyNo'
,
label
:
'保单号'
label
:
'保单号'
},
},
{
{
type
:
'select'
,
type
:
'select'
,
prop
:
'
statusList
'
,
prop
:
'
receiptStatus
'
,
label
:
'
出账
状态'
,
label
:
'
回执
状态'
,
multiple
:
tru
e
,
multiple
:
fals
e
,
dictType
:
'
csf_fortune
_status'
dictType
:
'
receipt
_status'
},
},
{
{
type
:
'select'
,
type
:
'select'
,
prop
:
'insur
anceCompanyBizIdList
'
,
prop
:
'insur
er
'
,
label
:
'保险公司'
,
label
:
'保险公司'
,
api
:
'/insurance/base/api/insuranceCompany/page'
,
api
:
'/insurance/base/api/insuranceCompany/page'
,
keywordField
:
'queryContent'
,
keywordField
:
'queryContent'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入保险公司名称搜索'
,
placeholder
:
'输入保险公司名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
debounceWait
:
500
,
// 自定义防抖时间
multiple
:
tru
e
,
multiple
:
fals
e
,
valueKey
:
'insuranceCompanyBizId'
,
valueKey
:
'insuranceCompanyBizId'
,
labelKey
:
'abbreviation'
,
labelKey
:
'abbreviation'
,
transform
:
res
=>
{
transform
:
res
=>
{
...
@@ -193,47 +194,15 @@ const searchConfig = ref([
...
@@ -193,47 +194,15 @@ const searchConfig = ref([
}
}
},
},
{
{
type
:
'select'
,
type
:
'input'
,
prop
:
'productLaunchBizIdList'
,
prop
:
'policyHolder'
,
label
:
'产品计划'
,
label
:
'保单持有人'
api
:
'/product/api/relProjectProductLaunch/parameter/page'
,
keywordField
:
'productName'
,
requestParams
:
{
tenantBizId
:
userStore
.
projectInfo
.
tenantBizId
||
''
,
projectBizId
:
userStore
.
projectInfo
.
projectBizId
||
''
,
fieldBizId
:
'field_olk1qZe81qHHKXbw'
,
fieldValueBizId
:
'field_value_uOfJH5ucA2YwJpbn'
,
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入产品计划名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
multiple
:
true
,
valueKey
:
'productLaunchBizId'
,
labelKey
:
'productName'
,
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
}
},
{
type
:
'daterange'
,
prop
:
'payoutDate'
,
label
:
'出账日(估)'
,
startPlaceholder
:
'开始时间'
,
endPlaceholder
:
'结束时间'
}
}
// {
// type: 'select',
// prop: 'status',
// label: '入账状态',
// multiple: true,
// dictType: 'csf_expected_commission_status'
// },
])
])
// 表格操作菜单
// 表格操作菜单
const
dropdownItems
=
[
const
dropdownItems
=
[
{
label
:
'设置本期出账金额'
,
value
:
'setPayRoll'
}
// { label: '新增回执', value: 'addReceipts' },
// { label: '更新', value: 'editRecord' },
{
label
:
'更新回执'
,
value
:
'editRecord'
}
// { label: '查看记录', value: 'viewRecord' }
// { label: '查看记录', value: 'viewRecord' }
]
]
// 应收单类型
// 应收单类型
...
@@ -250,85 +219,106 @@ const getFortuneBizTypeLabel = value => {
...
@@ -250,85 +219,106 @@ const getFortuneBizTypeLabel = value => {
const
addCheckRecordFormModel
=
ref
({})
const
addCheckRecordFormModel
=
ref
({})
const
addCheckRecordFormRef
=
ref
(
null
)
const
addCheckRecordFormRef
=
ref
(
null
)
const
addCheckRecordFormDialogFlag
=
ref
(
false
)
const
addCheckRecordFormDialogFlag
=
ref
(
false
)
const
addCheckRecordConfig
=
[
const
addCheckRecordConfig
=
[
{
{
type
:
'select'
,
type
:
'select'
,
prop
:
'fortuneBizType'
,
prop
:
'policyNo'
,
label
:
'应付单类型'
,
label
:
'保单号码'
,
options
:
fortuneBizTypeOptions
api
:
'/csf/api/policy/list/page/vo'
,
keywordField
:
'policyNo'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入转介人名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
valueKey
:
'policyNo'
,
labelKey
:
'policyNo'
,
onChangeExtraFields
:
{
insured
:
'insured'
,
// 选择了保单号码,自动填充保单受保人
policyHolder
:
'policyHolder'
,
insuranceCompany
:
'insuranceCompany'
,
productName
:
'productName'
,
currency
:
'currency'
,
paymentPremium
:
'paymentPremium'
,
paymentTerm
:
'paymentTerm'
},
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
},
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
},
{
{
type
:
'
selec
t'
,
type
:
'
inpu
t'
,
prop
:
'
status
'
,
prop
:
'
insured
'
,
label
:
'
出账状态
'
,
label
:
'
保单受保人
'
,
di
ctType
:
'csf_expected_fortune_status'
di
sabled
:
true
},
},
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'policy
No
'
,
prop
:
'policy
Holder
'
,
label
:
'
关联保单号
'
,
label
:
'
保单持有人姓名
'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
disabled
:
true
},
},
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'fortunePeriod'
,
prop
:
'insuranceCompany'
,
label
:
'佣金期数'
,
label
:
'保险公司名称'
,
inputType
:
'decimal'
,
disabled
:
true
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
},
},
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'fortuneTotalPeriod'
,
prop
:
'productName'
,
label
:
'总期数'
,
label
:
'保单产品名称'
,
inputType
:
'decimal'
,
disabled
:
true
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
},
},
{
{
type
:
'date'
,
type
:
'input'
,
prop
:
'actualPayoutDate'
,
prop
:
'paymentTerm'
,
label
:
'出账日(实)'
,
label
:
'缴费年限'
,
placeholder
:
'请选择'
,
disabled
:
true
maxDate
:
'today'
},
},
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'amount'
,
prop
:
'paymentPremium'
,
label
:
'出账金额'
,
label
:
'保单年缴保费'
,
inputType
:
'decimal'
,
disabled
:
true
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
},
},
{
{
type
:
'select'
,
type
:
'select'
,
prop
:
'currency'
,
prop
:
'currency'
,
label
:
'出账币种'
,
label
:
'保单货币'
,
dictType
:
'bx_currency_type'
dictType
:
'bx_currency_type'
,
disabled
:
true
},
},
{
{
type
:
'select'
,
type
:
'select'
,
prop
:
'fortuneType'
,
prop
:
'receiptStatus'
,
label
:
'出账项目'
,
label
:
'回执状态'
,
dictType
:
'csf_fortune_type'
dictType
:
'receipt_status'
,
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
},
{
{
type
:
'select'
,
type
:
'date'
,
prop
:
'brokerBizId'
,
prop
:
'receiptDate'
,
label
:
'转介人'
,
label
:
'回执日期'
,
api
:
'/insurance/base/api/userSaleExpand/page'
,
placeholder
:
'请选择'
,
keywordField
:
'realName'
,
maxDate
:
'today'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
placeholder
:
'输入转介人名称搜索'
,
},
debounceWait
:
500
,
// 自定义防抖时间
{
valueKey
:
'userSaleBizId'
,
type
:
'upload'
,
labelKey
:
'realName'
,
prop
:
'policyHolderSignatureList'
,
onChangeExtraFields
:
{
label
:
'签名照片'
,
broker
:
'realName'
,
// 自动同步 raw.name 到 reconciliationCompany
uploadType
:
'image'
,
reconciliationCompanyCode
:
'code'
multiple
:
true
,
},
limit
:
3
,
transform
:
res
=>
{
maxSize
:
5
*
1024
*
1024
,
// 5MB
return
res
?.
data
.
records
||
[]
accept
:
'.png,.jpg,.jpeg,.webp'
,
// ✅ 改成扩展名!
}
action
:
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
,
headers
:
{
Authorization
:
'Bearer '
+
getToken
()
},
listType
:
'picture-card'
,
defaultValue
:
[],
span
:
24
}
}
]
]
...
@@ -362,10 +352,28 @@ const setPayoutAmountConfig = [
...
@@ -362,10 +352,28 @@ const setPayoutAmountConfig = [
const
statisticsData
=
ref
({})
const
statisticsData
=
ref
({})
// 弹窗相关
// 弹窗相关
const
dialogFlag
=
ref
(
false
)
const
dialogFlag
=
ref
(
false
)
// 加载表格数据
const
getReceiptsDetail
=
async
row
=>
{
try
{
const
res
=
await
getPolicyReceipt
(
row
.
policyReceiptBizId
)
addCheckRecordFormModel
.
value
=
{
...
res
.
data
,
receiptDate
:
proxy
.
formatToDate
(
row
.
receiptDate
),
policyHolderSignatureList
:
res
.
data
.
policyHolderSignatureList
.
map
(
item
=>
({
url
:
item
}))
}
receiptsFlag
.
value
=
true
receiptsDialogTitle
.
value
=
'编辑保单回执'
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
receiptsFlag
.
value
=
false
}
}
// 按钮事件处理
// 按钮事件处理
const
handleAdd
=
()
=>
{
const
handleAdd
=
()
=>
{
addCheckRecordFormDialogFlag
.
value
=
true
console
.
log
(
addCheckRecordFormModel
.
value
)
receiptsFlag
.
value
=
true
}
}
const
handleExport
=
()
=>
{
const
handleExport
=
()
=>
{
...
@@ -375,26 +383,30 @@ const handleReset = () => {
...
@@ -375,26 +383,30 @@ const handleReset = () => {
// 重置搜索表单
// 重置搜索表单
searchFormRef
.
value
.
resetForm
()
searchFormRef
.
value
.
resetForm
()
console
.
log
(
'表单已重置'
)
console
.
log
(
'表单已重置'
)
currentPage
.
value
=
1
loadTableData
()
}
}
const
handleQuery
=
async
()
=>
{
const
handleQuery
=
async
()
=>
{
const
params
=
searchFormRef
.
value
.
getFormData
()
const
params
=
searchFormRef
.
value
.
getFormData
()
console
.
log
(
'params'
,
params
)
loadTableData
(
params
)
loadTableData
(
params
)
}
}
const
visibleDefaultButtons
=
ref
([
'add'
,
'
import'
,
'export'
,
'
reset'
,
'query'
])
const
visibleDefaultButtons
=
ref
([
'add'
,
'reset'
,
'query'
])
// 按钮配置
// 按钮配置
const
operationBtnList
=
ref
([
const
operationBtnList
=
ref
([
{
{
key
:
'add'
,
key
:
'add'
,
direction
:
'left'
,
direction
:
'left'
,
label
:
'新增
出账
'
,
label
:
'新增
回执
'
,
click
:
handleAdd
click
:
handleAdd
},
},
{
//
{
key
:
'export'
,
//
key: 'export',
direction
:
'right'
,
//
direction: 'right',
click
:
handleExport
//
click: handleExport
},
//
},
{
{
key
:
'reset'
,
key
:
'reset'
,
direction
:
'right'
,
direction
:
'right'
,
...
@@ -414,17 +426,13 @@ const loadTableData = async (searchParams = {}) => {
...
@@ -414,17 +426,13 @@ const loadTableData = async (searchParams = {}) => {
const
params
=
{
const
params
=
{
pageNo
:
currentPage
.
value
,
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
pageSize
:
pageSize
.
value
,
...
searchParams
,
...
searchParams
payoutDateStart
:
searchParams
.
payoutDate
?.[
0
]
||
undefined
,
payoutDateEnd
:
searchParams
.
payoutDate
?.[
1
]
||
undefined
,
payoutDate
:
undefined
}
}
const
res
=
await
getPolicyFortuneList
(
params
)
const
res
=
await
getPolicyReceiptList
(
params
)
tableData
.
value
=
res
.
data
.
page
.
records
||
[]
tableData
.
value
=
res
.
data
.
records
||
[]
pageTotal
.
value
=
res
.
data
.
page
.
total
||
0
pageSize
.
value
=
res
.
data
.
page
.
size
||
0
pageTotal
.
value
=
res
.
data
.
total
||
0
// 统计信息
pageSize
.
value
=
res
.
data
.
size
||
0
statisticsData
.
value
=
res
.
data
.
statisticsVO
||
{}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
...
@@ -448,27 +456,51 @@ const tableData = ref([])
...
@@ -448,27 +456,51 @@ const tableData = ref([])
const
handleSelect
=
(
e
,
row
)
=>
{
const
handleSelect
=
(
e
,
row
)
=>
{
console
.
log
(
'选中行:'
,
e
,
row
)
console
.
log
(
'选中行:'
,
e
,
row
)
selectedRow
.
value
=
row
selectedRow
.
value
=
row
if
(
e
===
'setPayRoll'
)
{
if
(
e
===
'addReceipts'
)
{
setPayoutAmountDialogFlag
.
value
=
true
receiptsFlag
.
value
=
true
receiptsDialogTitle
.
value
=
'新增保单回执'
}
else
if
(
e
==
'editRecord'
)
{
getReceiptsDetail
(
row
)
}
}
}
}
const
addCheckRecordaddBatchapi
=
async
data
=>
{
const
addReceipts
=
async
()
=>
{
const
formData
=
addCheckRecordFormRef
.
value
.
getFormData
()
console
.
log
(
'新增出账检核记录:'
,
formData
)
const
params
=
[{
...
formData
}]
try
{
try
{
const
res
=
await
addCheckRecordaddBatch
(
params
)
// ✅ 正确:await validate(),成功时返回表单数据
const
formData
=
await
addCheckRecordFormRef
.
value
.
validate
()
console
.
log
(
'新增保单回执:'
,
formData
)
const
params
=
{
policyNo
:
formData
.
policyNo
,
receiptStatus
:
formData
.
receiptStatus
,
receiptDate
:
proxy
.
formatToDateTime
(
formData
.
receiptDate
),
policyHolderSignatureList
:
formData
.
policyHolderSignatureList
.
length
>
0
?
formData
.
policyHolderSignatureList
.
map
(
item
=>
item
.
url
)
:
[]
}
let
res
=
{}
if
(
formData
.
policyReceiptBizId
)
{
params
.
policyReceiptBizId
=
formData
.
policyReceiptBizId
res
=
await
EditPolicyReceipt
(
params
)
}
else
{
res
=
await
addPolicyReceipt
(
params
)
}
if
(
res
.
code
===
200
)
{
if
(
res
.
code
===
200
)
{
ElMessage
.
success
(
'新增出账检核记录成功'
)
ElMessage
.
success
(
`
${
receiptsDialogTitle
.
value
}
成功`
)
addCheckRecordFormDialogFlag
.
value
=
false
receiptsFlag
.
value
=
false
// ✅ 校验 & 提交成功后才关闭
addCheckRecordFormRef
.
value
.
resetForm
()
addCheckRecordFormRef
.
value
.
resetForm
()
loadTableData
()
loadTableData
()
}
else
{
}
else
{
ElMessage
.
error
(
res
.
msg
||
'新增出账检核记录失败'
)
ElMessage
.
error
(
res
.
msg
||
`
${
receiptsDialogTitle
.
value
}
失败`
)
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'新增出账检核记录失败:'
,
error
)
// ❌ 校验失败或提交异常都会进入这里
ElMessage
.
error
(
error
.
message
||
'新增出账检核记录失败'
)
console
.
error
(
'操作失败:'
,
error
)
// ⚠️ 不设置 receiptsFlag = false,弹窗保持打开
ElMessage
.
warning
(
'请检查表单填写是否正确'
)
// 注意:Element Plus 的 validate 失败时,错误信息已自动显示在表单项下方
}
}
}
}
...
@@ -501,32 +533,9 @@ const handleSelectionChange = val => {
...
@@ -501,32 +533,9 @@ const handleSelectionChange = val => {
updatePayRollStatusDisable
.
value
=
val
.
length
===
0
updatePayRollStatusDisable
.
value
=
val
.
length
===
0
}
}
const
downloadPolicyFortuneAccountapi
=
async
data
=>
{
console
.
log
(
'生成出账清单:'
,
data
)
try
{
const
res
=
await
downloadPolicyFortuneAccount
({
fortuneBizIdList
:
multipleSelection
.
value
.
map
(
item
=>
item
.
fortuneBizId
)
})
if
(
res
.
code
===
200
)
{
ElMessage
.
success
(
'完成检核,等待关账'
)
loadTableData
()
}
}
catch
(
error
)
{
console
.
error
(
'检核失败:'
,
error
)
ElMessage
.
error
(
error
.
response
?.
data
?.
msg
||
'检核失败'
)
}
}
import
FileUploadPreview
from
'@/components/fileUploadPreview/fileUploadPreview.vue'
import
FileUploadPreview
from
'@/components/fileUploadPreview/fileUploadPreview.vue'
const
importCheckRecordFlag
=
ref
(
false
)
const
importCheckRecordFlag
=
ref
(
false
)
// 如果后端接收的就是当前格式,可直接透传
const
formatForBackend
=
rows
=>
{
return
rows
.
map
(
row
=>
({
...
row
,
amount
:
Number
(
row
.
amount
)
||
0
,
exchangeRate
:
Number
(
row
.
exchangeRate
)
||
1
}))
}
const
onSubmit
=
data
=>
{
const
onSubmit
=
data
=>
{
console
.
log
(
'提交给后端的数据:'
,
data
)
console
.
log
(
'提交给后端的数据:'
,
data
)
...
@@ -536,13 +545,19 @@ const onSubmit = data => {
...
@@ -536,13 +545,19 @@ const onSubmit = data => {
// 获取入账状态,字典值转化方法
// 获取入账状态,字典值转化方法
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
try
{
try
{
await
loadDicts
([
'
csf_fortune
_status'
])
await
loadDicts
([
'
receipt
_status'
])
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'字典加载失败'
,
error
)
console
.
error
(
'字典加载失败'
,
error
)
}
finally
{
}
finally
{
loading
.
value
=
false
loading
.
value
=
false
}
}
})
})
watch
(
receiptsFlag
,
newVal
=>
{
if
(
!
newVal
)
{
addCheckRecordFormModel
.
value
=
{}
receiptsDialogTitle
.
value
=
''
}
})
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
...
...
src/views/sign/policyReceipts/premiumRecon.vue
0 → 100644
View file @
574b9891
<
template
>
<div
class=
"container"
>
<CommonPage
:operationBtnList=
"operationBtnList"
:visibleDefaultButtons=
"visibleDefaultButtons"
:showSearchForm=
"true"
:show-pagination=
"true"
:total=
"pageTotal"
:current-page=
"currentPage"
:page-size=
"pageSize"
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
>
<!-- 搜索区域 -->
<template
#
searchForm
>
<SearchForm
ref=
"searchFormRef"
:config=
"searchConfig"
/>
</
template
>
<!-- 列表区域 -->
<
template
#
table
>
<el-table
:data=
"tableData"
@
selection-change=
"handleSelectionChange"
height=
"400"
border
highlight-current-row
style=
"width: 100%"
v-loading=
"loading"
>
<el-table-column
type=
"selection"
width=
"40"
/>
<el-table-column
prop=
"policyNo"
label=
"保单号"
width=
"150"
fixed=
"left"
/>
<el-table-column
prop=
"insuranceCompany"
label=
"保险公司"
width=
"150"
/>
<el-table-column
prop=
"policyFollowStatus"
label=
"新单状态"
width=
"150"
>
<template
#
default=
"
{ row }">
{{
getDictLabel
(
'csf_policy_follow_status_new'
,
row
.
policyFollowStatus
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"reconciliationStatus"
label=
"对账状态"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'reconciliation_status'
,
row
.
reconciliationStatus
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"policyStatus"
label=
"保单状态"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_policy_status_new'
,
row
.
policyStatus
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"remainingUnpaidAmount"
label=
"待付款金额"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
row
.
remainingUnpaidAmount
?
formatCurrency
(
row
.
remainingUnpaidAmount
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"remainingUnpaidCurrency"
label=
"待付款币种"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'bx_currency_type'
,
row
.
remainingUnpaidCurrency
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paymentAmount"
label=
"付款金额"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
row
.
paymentAmount
?
formatCurrency
(
row
.
paymentAmount
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paymentCurrency"
label=
"付款币种"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'bx_currency_type'
,
row
.
paymentCurrency
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paymentMethod"
label=
"缴费方式"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_ap_first_issue'
,
row
.
paymentMethod
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"recognizedAmount"
label=
"保司认定金额"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
row
.
recognizedAmount
?
formatCurrency
(
row
.
recognizedAmount
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"recognizedCurrency"
label=
"保司对账币种"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'bx_currency_type'
,
row
.
recognizedCurrency
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"payingBank"
label=
"付款银行"
width=
"150"
/>
<el-table-column
prop=
"payer"
label=
"付款人"
width=
"150"
/>
<el-table-column
prop=
"policyHolder"
label=
"投保人"
width=
"150"
/>
<el-table-column
prop=
"insured"
label=
"受保人"
width=
"150"
/>
<el-table-column
prop=
"brokerName"
label=
"转介人"
width=
"150"
/>
<el-table-column
prop=
"createTime"
label=
"创建时间"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
row
.
createTime
?
formatToDate
(
row
.
createTime
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"updateTime"
label=
"更新时间"
width=
"150"
>
<
template
#
default=
"{ row }"
>
{{
row
.
updateTime
?
formatToDate
(
row
.
updateTime
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"reconciliationCompany"
label=
"对账公司"
width=
"150"
>
</el-table-column>
<el-table-column
fixed=
"right"
label=
"操作"
min-width=
"120"
>
<
template
#
default=
"{ row }"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
>
<template
#
reference
>
<el-icon>
<MoreFilled
/>
</el-icon>
</
template
>
<el-menu
@
select=
"handleSelect($event, row)"
popper-class=
"custom-menu"
>
<el-menu-item
:index=
"item.value"
v-for=
"item in dropdownItems"
:key=
"item.value"
>
{{ item.label }}
</el-menu-item
>
</el-menu>
</el-popover>
</template>
</el-table-column>
</el-table>
</template>
</CommonPage>
<!-- 新增保单对账-->
<CommonDialog
:dialogTitle=
"receiptsDialogTitle"
dialogWidth=
"80%"
:openDialog=
"receiptsFlag"
:showAction=
"true"
:showClose=
"true"
@
close=
"receiptsFlag = false"
@
confirm=
"addReceipts"
>
<SearchForm
ref=
"addCheckRecordFormRef"
:config=
"addCheckRecordConfig"
v-model=
"addCheckRecordFormModel"
/>
<div>
<el-button
type=
"primary"
icon=
"plus"
@
click=
"addRemittance"
style=
"margin-bottom: 10px"
>
新增汇款记录
</el-button
>
<div
v-if=
"
addCheckRecordFormModel.apiPremiumRemittanceDtoList &&
addCheckRecordFormModel.apiPremiumRemittanceDtoList.length > 0
"
class=
"otherFileBox"
>
<el-table
border
:data=
"addCheckRecordFormModel.apiPremiumRemittanceDtoList"
size=
"small"
>
<el-table-column
label=
"缴费方式"
prop=
"paymentMethod"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_ap_first_issue'
,
row
.
paymentMethod
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"付款金额"
prop=
"paymentAmount"
/>
<el-table-column
label=
"付款币种"
prop=
"paymentCurrency"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'bx_currency_type'
,
row
.
paymentCurrency
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"付款人与保单关系"
prop=
"paymentRel"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'rel_pay_policy'
,
row
.
paymentRel
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"付款人"
prop=
"payer"
/>
<el-table-column
label=
"付款银行"
prop=
"payingBank"
/>
<el-table-column
label=
"付款账号"
prop=
"paymentAccount"
/>
<el-table-column
label=
"操作"
width=
"100"
>
<
template
#
default=
"{ row, $index }"
>
<el-button
type=
"primary"
link
@
click=
"editRemittance(row, $index)"
>
修改
</el-button>
<el-button
type=
"danger"
link
@
click=
"removeRemittance(row, $index)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</CommonDialog>
<!-- 设置认定结果 -->
<CommonDialog
dialogTitle=
"设置认定结果"
dialogWidth=
"80%"
:openDialog=
"showAffirm"
:showAction=
"true"
:showClose=
"true"
@
close=
"showAffirm = false"
>
<SearchForm
ref=
"affirmFormRef"
:config=
"affirmConfig"
v-model=
"affirmFormModel"
/>
</CommonDialog>
<!-- 汇款弹窗 -->
<CommonDialog
:dialogTitle=
"remittanceDialogTitle"
dialogWidth=
"80%"
:openDialog=
"showRemittance"
:showAction=
"true"
:showClose=
"true"
@
close=
"showRemittance = false"
@
confirm=
"confirmRemittance"
>
<el-scrollbar
max-height=
"500px"
>
<div
:style=
"{
marginBottom: remittanceFormModel.apiPremiumRemittanceFileDtoList?.length
? '0px'
: '100px'
}"
>
<SearchForm
ref=
"remittanceFormRef"
:config=
"remittanceConfig"
v-model=
"remittanceFormModel"
/>
<div
v-if=
"remittanceFormModel.apiPremiumRemittanceFileDtoList?.length"
class=
"otherFileBox"
>
<el-table
border
:data=
"tempOtherFileList"
size=
"small"
>
<el-table-column
prop=
"fileName"
label=
"文件名"
/>
<el-table-column
prop=
"fileType"
label=
"文件类型"
/>
<el-table-column
prop=
"creatorName"
label=
"上传人"
/>
<el-table-column
prop=
"createTime"
label=
"上传时间"
>
<
template
#
default=
"{ row }"
>
{{
row
.
createTime
?
formatToDate
(
row
.
createTime
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"100"
>
<
template
#
default=
"{ row, $index }"
>
<el-button
type=
"danger"
link
@
click=
"removeOtherFile(row, $index)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</el-scrollbar>
</CommonDialog>
</div>
</template>
<
script
setup
>
import
{
ref
,
reactive
,
watch
}
from
'vue'
import
CommonPage
from
'@/components/commonPage'
import
CommonDialog
from
'@/components/commonDialog'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
import
{
ElMessage
}
from
'element-plus'
import
{
formatCurrency
}
from
'@/utils/number'
// 接口
import
{
updatePayoutAmount
}
from
'@/api/financial/commission'
import
{
premiumReconciliationList
,
EditPolicyReceipt
,
editPremiumRemittance
,
addPremiumReconciliation
,
getPremiumReconciliationInfo
,
deletePremiumRemittanceFile
,
getPremiumRemittanceFileList
,
editSiglePremiumRemittance
,
getPremiumRemittanceListApi
}
from
'@/api/sign/policy'
import
useUserStore
from
'@/store/modules/user'
import
{
loadDicts
,
getDictLabel
}
from
'@/utils/useDict'
import
{
getToken
}
from
'@/utils/auth'
const
{
proxy
}
=
getCurrentInstance
()
const
userStore
=
useUserStore
()
// 分页相关
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
10
)
const
pageTotal
=
ref
(
0
)
const
loading
=
ref
(
false
)
const
selectedRow
=
ref
(
null
)
const
searchFormRef
=
ref
(
null
)
const
searchParams
=
ref
({})
const
receiptsFlag
=
ref
(
false
)
//回执弹窗
const
receiptsDialogTitle
=
ref
(
'新增保单回执'
)
const
remittanceDialogTitle
=
ref
(
'新增汇款'
)
const
showRemittance
=
ref
(
false
)
const
remittanceFormRef
=
ref
(
null
)
const
searchConfig
=
ref
([
{
type
:
'select'
,
prop
:
'insurer'
,
label
:
'保险公司'
,
api
:
'/insurance/base/api/insuranceCompany/page'
,
keywordField
:
'queryContent'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入保险公司名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
multiple
:
false
,
valueKey
:
'insuranceCompanyBizId'
,
labelKey
:
'abbreviation'
,
transform
:
res
=>
{
console
.
log
(
res
)
return
res
?.
data
.
records
||
[]
}
},
{
type
:
'select'
,
prop
:
'insurer'
,
label
:
'付款银行'
,
api
:
'/insurance/base/api/insuranceCompany/page'
,
keywordField
:
'queryContent'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入保险公司名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
multiple
:
false
,
valueKey
:
'insuranceCompanyBizId'
,
labelKey
:
'abbreviation'
,
transform
:
res
=>
{
console
.
log
(
res
)
return
res
?.
data
.
records
||
[]
}
},
{
type
:
'input'
,
prop
:
'appointmentNo'
,
label
:
'付款人'
},
{
type
:
'input'
,
prop
:
'policyNo'
,
label
:
'保单号'
},
{
type
:
'input'
,
prop
:
'policyNo'
,
label
:
'预约编号'
},
{
type
:
'input'
,
prop
:
'policyNo'
,
label
:
'投保人'
},
{
type
:
'select'
,
prop
:
'receiptStatus'
,
label
:
'缴费方式'
,
multiple
:
false
,
dictType
:
'receipt_status'
},
{
type
:
'select'
,
prop
:
'receiptStatus'
,
label
:
'新单状态'
,
multiple
:
false
,
dictType
:
'receipt_status'
},
{
type
:
'select'
,
prop
:
'receiptStatus'
,
label
:
'保单状态'
,
multiple
:
false
,
dictType
:
'receipt_status'
},
{
type
:
'select'
,
prop
:
'receiptStatus'
,
label
:
'对账类型'
,
multiple
:
false
,
dictType
:
'receipt_status'
},
{
type
:
'date'
,
prop
:
'receiptDate'
,
label
:
'付款日期'
,
placeholder
:
'请选择'
,
maxDate
:
'today'
}
])
// 表格操作菜单
const
dropdownItems
=
[
{
label
:
'设置认定结果'
,
value
:
'settingResult'
},
{
label
:
'编辑'
,
value
:
'editRecord'
}
// { label: '查看记录', value: 'viewRecord' }
]
// 应收单类型
const
fortuneBizTypeOptions
=
[
{
value
:
'R'
,
label
:
'关联保单应付单'
},
{
value
:
'U'
,
label
:
'非关联保单应付单'
}
]
// ==============设置认定结果开始============
const
showAffirm
=
ref
(
false
)
const
affirmFormRef
=
ref
(
null
)
const
affirmFormModel
=
ref
({})
const
affirmConfig
=
[
{
type
:
'input'
,
prop
:
'paymentAmount'
,
label
:
'保司认定金额'
,
inputType
:
'decimal'
,
rules
:
[
{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
},
{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}
]
},
{
type
:
'select'
,
prop
:
'paymentCurrency'
,
label
:
'保司认定币种'
,
dictType
:
'bx_currency_type'
,
rules
:
[{
required
:
true
,
message
:
'请输入认定币种'
,
trigger
:
'blur'
}]
},
{
type
:
'input'
,
prop
:
'insured'
,
label
:
'此保单剩余待付金额'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'insurede'
,
label
:
'此保单剩余待付币种'
,
disabled
:
true
}
]
// ==============设置认定结果结束============
// ==============新增对账开始============
const
addCheckRecordFormModel
=
ref
({
apiPremiumRemittanceDtoList
:
[]
})
const
addCheckRecordFormRef
=
ref
(
null
)
// 设置汇款
const
remittanceFormModel
=
ref
({
apiPremiumRemittanceFileDtoList
:
[]
})
const
tempOtherFileList
=
ref
([])
//临时存储其他资料数据
const
deleteObjkeys
=
{
reconciliationType
:
'reconciliationType'
,
applicant
:
'applicant'
,
policyNo
:
'policyNo'
,
insured
:
'insured'
,
insuranceCompany
:
'insuranceCompany'
}
const
remittanceConfig
=
[
{
type
:
'select'
,
prop
:
'paymentMethod'
,
label
:
'缴费方式'
,
dictType
:
'csf_ap_first_issue'
,
rules
:
[{
required
:
true
,
message
:
'请输入缴费方式'
,
trigger
:
'blur'
}]
},
{
type
:
'input'
,
prop
:
'paymentAmount'
,
label
:
'付款金额'
,
inputType
:
'decimal'
,
rules
:
[
{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
},
{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}
]
},
{
type
:
'select'
,
prop
:
'paymentCurrency'
,
label
:
'付款币种'
,
dictType
:
'bx_currency_type'
,
rules
:
[{
required
:
true
,
message
:
'请输入付款币种'
,
trigger
:
'blur'
}]
},
{
type
:
'select'
,
prop
:
'paymentRel'
,
label
:
'付款人与保单关系'
,
dictType
:
'rel_pay_policy'
,
rules
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}]
},
{
type
:
'input'
,
prop
:
'payer'
,
label
:
'付款人'
,
rules
:
[{
required
:
true
,
message
:
'请输入'
,
trigger
:
'blur'
}]
},
{
type
:
'select'
,
prop
:
'payingBank'
,
label
:
'付款银行'
,
api
:
'/base/api/bank/page'
,
keywordField
:
'policyNo'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入转介人名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
valueKey
:
'bankBizId'
,
labelKey
:
'bankName'
,
// onChangeExtraFields: {
// insured: 'insured', // 选择了保单号码,自动填充保单受保人
// policyHolder: 'policyHolder',
// insuranceCompany: 'insuranceCompany',
// productName: 'productName',
// currency: 'currency',
// paymentPremium: 'paymentPremium',
// paymentTerm: 'paymentTerm'
// },
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
}
},
{
type
:
'input'
,
prop
:
'paymentAccount'
,
label
:
'付款账号'
},
{
type
:
'input'
,
prop
:
'applicant'
,
label
:
'申请人'
,
disabled
:
true
},
{
type
:
'select'
,
prop
:
'policyNo'
,
label
:
'保单号码'
,
api
:
'/csf/api/policy/list/page/vo'
,
keywordField
:
'policyNo'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入转介人名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
valueKey
:
'policyNo'
,
labelKey
:
'policyNo'
,
onChangeExtraFields
:
{
insured
:
'insured'
,
// 选择了保单号码,自动填充保单受保人
policyHolder
:
'policyHolder'
,
insuranceCompany
:
'insuranceCompany'
,
productName
:
'productName'
,
currency
:
'currency'
,
paymentPremium
:
'paymentPremium'
,
paymentTerm
:
'paymentTerm'
},
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
},
disabled
:
true
},
{
type
:
'input'
,
prop
:
'insured'
,
label
:
'被保人'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
disabled
:
true
},
{
type
:
'select'
,
prop
:
'reconciliationType'
,
label
:
'对账类型'
,
disabled
:
true
,
dictType
:
'reconciliation_type'
},
{
type
:
'upload'
,
prop
:
'paymentVoucherList'
,
label
:
'支付凭证'
,
uploadType
:
'image'
,
multiple
:
true
,
limit
:
5
,
maxSize
:
5
*
1024
*
1024
,
// 5MB
accept
:
'.png,.jpg,.jpeg,.webp'
,
// ✅ 改成扩展名!
action
:
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
,
headers
:
{
Authorization
:
'Bearer '
+
getToken
()
},
listType
:
'picture-card'
,
defaultValue
:
[],
span
:
24
,
rules
:
[{
required
:
true
,
message
:
'请上传支付凭证'
,
trigger
:
'blur'
}]
},
{
type
:
'upload'
,
prop
:
'accountVerificationList'
,
label
:
'账户证明'
,
uploadType
:
'image'
,
multiple
:
true
,
limit
:
5
,
maxSize
:
5
*
1024
*
1024
,
// 5MB
accept
:
'.png,.jpg,.jpeg,.webp'
,
// ✅ 改成扩展名!
action
:
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
,
headers
:
{
Authorization
:
'Bearer '
+
getToken
()
},
listType
:
'picture-card'
,
defaultValue
:
[],
span
:
24
,
rules
:
[{
required
:
true
,
message
:
'请上传账户证明'
,
trigger
:
'blur'
}]
},
{
type
:
'upload'
,
prop
:
'apiPremiumRemittanceFileDtoList'
,
label
:
'其他资料'
,
uploadType
:
'file'
,
multiple
:
true
,
limit
:
5
,
maxSize
:
10
*
1024
*
1024
,
// 5MB
accept
:
'.png,.jpg,.jpeg,.webp,.pdf,.doc,.docx,.xls,.xlsx,.txt'
,
// ✅ 支持多种格式
action
:
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/oss/api/oss/upload'
,
headers
:
{
Authorization
:
'Bearer '
+
getToken
()
},
listType
:
'text'
,
// ← 按钮样式(非图片卡片)
defaultValue
:
[],
span
:
24
,
link
:
false
,
showFileList
:
false
}
]
// 新增汇款
const
addRemittance
=
async
()
=>
{
try
{
// 汇款记录详情也需要回显保费对账得一些字段
const
formData
=
await
addCheckRecordFormRef
.
value
.
validate
()
remittanceFormModel
.
value
=
{
...
formData
}
remittanceFormModel
.
value
.
apiPremiumRemittanceFileDtoList
=
[]
tempOtherFileList
.
value
=
[]
remittanceDialogTitle
.
value
=
'新增汇款'
showRemittance
.
value
=
true
}
catch
(
error
)
{
// ❌ 校验失败或提交异常都会进入这里
console
.
error
(
'操作失败:'
,
error
)
ElMessage
.
warning
(
'必填项不能为空'
)
}
}
// 删除其他资料附件函数
function
removeOtherFile
(
row
,
index
)
{
const
list
=
[...
remittanceFormModel
.
value
.
apiPremiumRemittanceFileDtoList
]
proxy
.
$modal
.
confirm
(
'是否确认删除这个附件?'
)
.
then
(
function
()
{
if
(
row
.
premiumRemittanceFileBizId
)
{
console
.
log
(
'远程删除'
)
return
deletePremiumRemittanceFile
(
row
.
premiumRemittanceFileBizId
)
}
else
{
// 本地临时数据,直接从列表中移除
list
.
splice
(
index
,
1
)
// ⚠️ 关键:直接赋值触发响应式更新(SearchForm 会收到新值)
remittanceFormModel
.
value
.
apiPremiumRemittanceFileDtoList
=
tempOtherFileList
.
value
=
list
// 注意:这里不需要 return Promise,因为是同步操作
// 但为了统一 then 链,可以 return 一个成功标识
return
{
code
:
200
}
// 模拟成功
}
})
.
then
(
res
=>
{
if
(
res
&&
res
.
code
==
200
)
{
// 调用附件接口,更新附件列表
otherFileList
()
}
})
.
catch
(()
=>
{})
}
// 获取汇款记录列表
const
getPremiumRemittanceList
=
async
()
=>
{
try
{
const
params
=
{
premiumReconciliationBizId
:
addCheckRecordFormModel
.
value
.
premiumReconciliationBizId
,
pageNo
:
1
,
pageSize
:
9999
}
const
res
=
await
getPremiumRemittanceListApi
(
params
)
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
=
res
.
data
.
records
ElMessage
.
success
(
`
${
remittanceDialogTitle
.
value
}
成功`
)
showRemittance
.
value
=
false
// ✅ 校验 & 提交成功后才关闭
remittanceFormRef
.
value
.
resetForm
()
proxy
.
$modal
.
msgSuccess
(
'汇款记录修改成功'
)
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
}
}
// 获取其他资料得附件列表
const
otherFileList
=
async
()
=>
{
try
{
const
params
=
{
premiumRemittanceBizId
:
remittanceFormModel
.
value
.
premiumRemittanceBizId
,
pageNo
:
1
,
pageSize
:
9999
}
const
res
=
await
getPremiumRemittanceFileList
(
params
)
remittanceFormModel
.
value
.
apiPremiumRemittanceFileDtoList
=
tempOtherFileList
.
value
=
res
.
data
.
records
proxy
.
$modal
.
msgSuccess
(
'附件删除成功'
)
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
}
}
// 确认汇款记录
const
confirmRemittance
=
async
data
=>
{
try
{
// ✅ 正确:await validate(),成功时返回表单数据
const
formData
=
await
remittanceFormRef
.
value
.
validate
()
let
newFormData
=
JSON
.
parse
(
JSON
.
stringify
(
formData
))
// 因为汇款记录回显了保费对账的一些字段,但提交汇款记录的时候这些回显的字段是不能提交的,所以在这里做删除
for
(
const
key
in
newFormData
)
{
if
(
key
==
deleteObjkeys
[
key
])
delete
newFormData
[
key
]
// 文件上传的数据格式在这里统一处理
if
(
key
==
'apiPremiumRemittanceFileDtoList'
&&
newFormData
[
key
].
length
>
0
)
{
newFormData
[
key
]
=
newFormData
[
key
].
map
(
item
=>
{
// 有response说明是新上传得,要处理返回格式和字段
if
(
item
.
response
)
{
let
newObj
=
item
.
response
.
data
return
{
fileName
:
newObj
.
originalName
,
//文件名
fileType
:
newObj
.
fileType
,
//文件类型
fileUrl
:
newObj
.
url
,
//文件URL,
url
:
newObj
.
url
//文件URL
}
}
else
{
return
item
}
})
}
else
if
(
(
key
==
'paymentVoucherList'
||
key
==
'accountVerificationList'
)
&&
newFormData
[
key
].
length
>
0
)
{
newFormData
[
key
]
=
newFormData
[
key
].
map
(
item
=>
item
.
url
)
}
}
let
res
=
{}
// 编辑状态下修改汇款记录
if
(
formData
.
premiumRemittanceBizId
)
{
//编辑单个对账的先写到这里,等会在对接
const
params
=
{
...
newFormData
}
res
=
await
editSiglePremiumRemittance
(
params
)
if
(
res
.
code
===
200
)
{
// 更新保费对账详情
getPremiumReconciliationDetail
(
addCheckRecordFormModel
.
value
)
// getPremiumRemittanceList()
}
else
{
ElMessage
.
error
(
res
.
msg
||
`
${
remittanceDialogTitle
.
value
}
失败`
)
}
}
else
{
// 新增汇款记录状态修改或新增
if
(
!
newFormData
.
addRemittanceId
)
{
// 新增汇款记录自己家id,便于新增情况下做修改
newFormData
.
addRemittanceId
=
new
Date
().
getTime
()
}
let
index
=
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
.
findIndex
(
item
=>
newFormData
.
addRemittanceId
==
item
.
addRemittanceId
)
if
(
index
==
-
1
)
{
// 添加新得汇款记录
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
.
push
(
newFormData
)
}
else
{
// 已经新增过汇款记录只是做修改
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
[
index
]
=
newFormData
}
}
showRemittance
.
value
=
false
}
catch
(
error
)
{
// ❌ 校验失败或提交异常都会进入这里
console
.
error
(
'操作失败:'
,
error
)
// ⚠️ 不设置 receiptsFlag = false,弹窗保持打开
ElMessage
.
warning
(
'请检查表单填写是否正确'
)
}
}
// 修改汇款记录
const
editRemittance
=
row
=>
{
let
newObj
=
JSON
.
parse
(
JSON
.
stringify
(
row
))
// 添加保费对账弹窗得form表单便于汇款回显
newObj
.
reconciliationType
=
addCheckRecordFormModel
.
value
.
reconciliationType
newObj
.
applicant
=
addCheckRecordFormModel
.
value
.
applicant
newObj
.
policyNo
=
addCheckRecordFormModel
.
value
.
policyNo
newObj
.
insured
=
addCheckRecordFormModel
.
value
.
insured
newObj
.
insuranceCompany
=
addCheckRecordFormModel
.
value
.
insuranceCompany
// 上传回显得格式是[{url:''}]所以要处理一下
for
(
const
key
in
newObj
)
{
if
(
(
key
==
'paymentVoucherList'
||
key
==
'accountVerificationList'
||
key
==
'apiPremiumRemittanceFileDtoList'
)
&&
newObj
[
key
]
&&
newObj
[
key
].
length
>
0
)
{
newObj
[
key
]
=
newObj
[
key
].
map
(
item
=>
{
if
(
key
==
'apiPremiumRemittanceFileDtoList'
)
{
return
{
...
item
,
url
:
item
.
fileUrl
}
}
else
{
return
{
url
:
item
}
}
})
}
}
console
.
log
(
'===================================='
)
console
.
log
(
'汇款修改'
,
newObj
)
console
.
log
(
'===================================='
)
remittanceFormModel
.
value
=
newObj
showRemittance
.
value
=
true
}
// 删除汇款记录
function
removeRemittance
(
row
,
index
)
{
const
list
=
[...
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
]
proxy
.
$modal
.
confirm
(
'是否确认删除此行数据?'
)
.
then
(
function
()
{
if
(
row
.
premiumRemittanceBizId
)
{
console
.
log
(
'远程删除'
)
return
deletePremiumRemittance
(
row
.
premiumRemittanceBizId
)
}
else
{
// 本地临时数据,直接从列表中移除
const
index
=
list
.
findIndex
(
item
=>
item
.
id
===
row
.
id
)
if
(
index
!==
-
1
)
{
list
.
splice
(
index
,
1
)
}
addCheckRecordFormModel
.
value
.
apiPremiumRemittanceDtoList
=
list
console
.
log
(
'addCheckRecordFormModel.value.apiPremiumRemittanceDtoList'
,
addCheckRecordFormModel
.
value
)
// 注意:这里不需要 return Promise,因为是同步操作
// 但为了统一 then 链,可以 return 一个成功标识
return
{
code
:
200
}
// 模拟成功
}
})
.
then
(
res
=>
{
if
(
res
&&
res
.
code
==
200
)
{
proxy
.
$modal
.
msgSuccess
(
'汇款删除成功'
)
// 删除成功要调用保费对账的详情接口,重新汇款记录数据,这里还没做等会做
}
})
.
catch
(()
=>
{})
}
// ==============新增对账结束============
// 应付单类型通过value转成label
const
getFortuneBizTypeLabel
=
value
=>
{
const
item
=
fortuneBizTypeOptions
.
find
(
item
=>
item
.
value
===
value
)
return
item
?.
label
||
''
}
const
addCheckRecordConfig
=
[
{
type
:
'select'
,
prop
:
'reconciliationType'
,
label
:
'对账类型'
,
dictType
:
'reconciliation_type'
,
rules
:
[{
required
:
true
,
message
:
'请输入对账类型'
,
trigger
:
'blur'
}]
},
{
type
:
'input'
,
prop
:
'applicant'
,
label
:
'申请人'
,
rules
:
[{
required
:
true
,
message
:
'请输入申请人'
,
trigger
:
'blur'
}]
},
{
type
:
'select'
,
prop
:
'policyNo'
,
label
:
'保单号码'
,
api
:
'/csf/api/policy/list/page/vo'
,
keywordField
:
'policyNo'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入转介人名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
valueKey
:
'policyNo'
,
labelKey
:
'policyNo'
,
onChangeExtraFields
:
{
insured
:
'insured'
,
// 选择了保单号码,自动填充保单被保人
insuranceCompany
:
'insuranceCompany'
},
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
},
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
{
type
:
'input'
,
prop
:
'insured'
,
label
:
'被保人'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
disabled
:
true
}
]
const
apiPremiumReconciliationDto
=
ref
({})
// 获取对账详情
const
getPremiumReconciliationDetail
=
async
row
=>
{
try
{
const
res
=
await
getPremiumReconciliationInfo
(
row
.
premiumReconciliationBizId
)
apiPremiumReconciliationDto
.
value
=
res
.
data
.
apiPremiumReconciliationDto
addCheckRecordFormModel
.
value
=
{
...
res
.
data
.
apiPremiumReconciliationDto
,
apiPremiumRemittanceDtoList
:
res
.
data
.
apiPremiumRemittanceDtoList
}
receiptsFlag
.
value
=
true
receiptsDialogTitle
.
value
=
'编辑保单对账'
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
receiptsFlag
.
value
=
false
}
}
// 按钮事件处理
const
handleAdd
=
()
=>
{
console
.
log
(
addCheckRecordFormModel
.
value
)
receiptsDialogTitle
.
value
=
'新增保单对账'
receiptsFlag
.
value
=
true
}
const
handleExport
=
()
=>
{
ElMessage
.
info
(
'点击导出按钮'
)
}
const
handleReset
=
()
=>
{
// 重置搜索表单
searchFormRef
.
value
.
resetForm
()
console
.
log
(
'表单已重置'
)
currentPage
.
value
=
1
loadTableData
()
}
const
handleQuery
=
async
()
=>
{
const
params
=
searchFormRef
.
value
.
getFormData
()
console
.
log
(
'params'
,
params
)
loadTableData
(
params
)
}
const
visibleDefaultButtons
=
ref
([
'add'
,
'reset'
,
'query'
])
// 按钮配置
const
operationBtnList
=
ref
([
{
key
:
'add'
,
direction
:
'left'
,
label
:
'新增保费对账'
,
click
:
handleAdd
},
// {
// key: 'export',
// direction: 'right',
// click: handleExport
// },
{
key
:
'reset'
,
direction
:
'right'
,
click
:
handleReset
},
{
key
:
'query'
,
direction
:
'right'
,
click
:
handleQuery
}
])
// 加载表格数据
const
loadTableData
=
async
(
searchParams
=
{})
=>
{
loading
.
value
=
true
try
{
const
params
=
{
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
...
searchParams
}
const
res
=
await
premiumReconciliationList
(
params
)
tableData
.
value
=
res
.
data
.
records
||
[]
pageTotal
.
value
=
res
.
data
.
total
||
0
pageSize
.
value
=
res
.
data
.
size
||
0
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
error
.
message
||
'加载数据失败'
)
}
finally
{
loading
.
value
=
false
}
}
// 分页事件
const
handleSizeChange
=
val
=>
{
pageSize
.
value
=
val
loadTableData
()
}
const
handleCurrentChange
=
val
=>
{
currentPage
.
value
=
val
loadTableData
()
}
// 表格数据
const
tableData
=
ref
([])
const
handleSelect
=
(
e
,
row
)
=>
{
console
.
log
(
'选中行:'
,
e
,
row
)
selectedRow
.
value
=
row
if
(
e
==
'editRecord'
)
{
getPremiumReconciliationDetail
(
row
)
}
else
if
(
e
==
'settingResult'
)
{
affirmFormModel
.
value
=
{}
showAffirm
.
value
=
true
}
}
const
addReceipts
=
async
()
=>
{
try
{
// ✅ 正确:await validate(),成功时返回表单数据
const
formData
=
await
addCheckRecordFormRef
.
value
.
validate
()
let
apiPremiumReconciliationDto
=
{}
let
apiPremiumRemittanceDtoList
=
JSON
.
parse
(
JSON
.
stringify
(
formData
.
apiPremiumRemittanceDtoList
)
)
for
(
const
key
in
formData
)
{
if
(
!
Array
.
isArray
(
formData
[
key
]))
{
apiPremiumReconciliationDto
[
key
]
=
formData
[
key
]
}
}
// if (apiPremiumRemittanceDtoList.length > 0) {
// apiPremiumRemittanceDtoList.forEach(item => {
// if (item.paymentVoucherList && item.paymentVoucherList.length > 0) {
// item.paymentVoucherList = item.paymentVoucherList.map(item1 => item1.url)
// }
// if (item.accountVerificationList && item.accountVerificationList.length > 0) {
// item.accountVerificationList = item.accountVerificationList.map(item1 => item1.url)
// }
// })
// }
const
params
=
{
apiPremiumReconciliationDto
:
apiPremiumReconciliationDto
,
apiPremiumRemittanceDtoList
:
apiPremiumRemittanceDtoList
}
// console.log('新增保单对账:', params)
// return
let
res
=
{}
if
(
formData
.
policyReceiptBizId
)
{
params
.
policyReceiptBizId
=
formData
.
policyReceiptBizId
res
=
await
EditPolicyReceipt
(
params
)
}
else
{
res
=
await
addPremiumReconciliation
(
params
)
}
if
(
res
.
code
===
200
)
{
ElMessage
.
success
(
`
${
receiptsDialogTitle
.
value
}
成功`
)
receiptsFlag
.
value
=
false
// ✅ 校验 & 提交成功后才关闭
addCheckRecordFormRef
.
value
.
resetForm
()
loadTableData
()
}
else
{
ElMessage
.
error
(
res
.
msg
||
`
${
receiptsDialogTitle
.
value
}
失败`
)
}
}
catch
(
error
)
{
// ❌ 校验失败或提交异常都会进入这里
console
.
error
(
'操作失败:'
,
error
)
// ⚠️ 不设置 receiptsFlag = false,弹窗保持打开
ElMessage
.
warning
(
'请检查表单填写是否正确'
)
// 注意:Element Plus 的 validate 失败时,错误信息已自动显示在表单项下方
}
}
const
updatePayRollStatusDisable
=
ref
(
true
)
const
multipleSelection
=
ref
([])
const
handleSelectionChange
=
val
=>
{
multipleSelection
.
value
=
val
console
.
log
(
'全选:'
,
val
)
// 完成检核按钮是否禁用
updatePayRollStatusDisable
.
value
=
val
.
length
===
0
}
import
FileUploadPreview
from
'@/components/fileUploadPreview/fileUploadPreview.vue'
const
onSubmit
=
data
=>
{
console
.
log
(
'提交给后端的数据:'
,
data
)
// 调用保存 API
}
// 获取入账状态,字典值转化方法
onMounted
(
async
()
=>
{
try
{
await
loadDicts
([
'csf_ap_first_issue'
,
'csf_policy_follow_status_new'
,
'csf_policy_status_new'
,
'reconciliation_status'
,
'bx_currency_type'
,
'reconciliation_type'
,
'rel_pay_policy'
])
loadTableData
()
}
catch
(
error
)
{
console
.
error
(
'字典加载失败'
,
error
)
}
finally
{
loading
.
value
=
false
}
})
watch
(
()
=>
remittanceFormModel
.
value
.
apiPremiumRemittanceFileDtoList
,
newVal
=>
{
console
.
log
(
'newVal'
,
newVal
)
if
(
newVal
&&
newVal
.
length
>
0
)
{
newVal
.
forEach
(
item
=>
{
// 代表是从新增保单对账开始
if
(
item
.
response
)
{
let
newObj
=
JSON
.
parse
(
JSON
.
stringify
(
item
.
response
.
data
))
// tempOtherFileList这个变量便于其他资料文件表格得展示使用
tempOtherFileList
.
value
.
push
({
fileName
:
newObj
.
originalName
,
//文件名
fileType
:
newObj
.
fileType
,
//文件类型
fileUrl
:
newObj
.
url
,
//文件URL,
url
:
newObj
.
url
//文件URL
})
}
else
{
// 从编辑对账或已经新增过汇款要修改汇款
tempOtherFileList
.
value
=
JSON
.
parse
(
JSON
.
stringify
(
newVal
))
}
})
}
},
{
deep
:
true
}
)
watch
(
receiptsFlag
,
newVal
=>
{
if
(
!
newVal
)
{
addCheckRecordFormModel
.
value
=
{}
receiptsDialogTitle
.
value
=
''
}
})
</
script
>
<
style
scoped
>
.tableOptionContainer
{
display
:
flex
;
justify-content
:
flex-end
;
margin-top
:
10px
;
}
.otherFileBox
{
margin-bottom
:
50px
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment