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
223a37bd
Commit
223a37bd
authored
Jan 13, 2026
by
Sweet Zhang
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'test' into sw
parents
23d2b1d4
a22e899f
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
2514 additions
and
1110 deletions
+2514
-1110
src/api/financial/commission.js
+0
-9
src/api/sign/policy.js
+102
-0
src/components/SearchForm/SearchForm.vue
+594
-418
src/utils/2.js
+44
-317
src/views/financialCenter/payables.vue
+221
-101
src/views/financialCenter/receivables.vue
+203
-76
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/financial/commission.js
View file @
223a37bd
...
...
@@ -412,12 +412,3 @@ export function exportPayRecord(data) {
responseType
:
'blob'
})
}
// 入账检核重新比对
export
function
compareCommissionEntry
(
data
){
return
request
({
url
:
'/csf/api/commission/compare'
,
method
:
'post'
,
data
:
data
})
}
src/api/sign/policy.js
0 → 100644
View file @
223a37bd
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 @
223a37bd
<
template
>
<el-form
ref=
"formRef"
:model=
"localModel"
:rules=
"formRules"
label-width=
"auto"
v-bind=
"$attrs"
:validate-on-rule-change=
"false"
>
<el-row
:gutter=
"20"
>
<el-col
v-for=
"item in visibleConfig"
:key=
"item.prop"
:span=
"item.span || 6"
>
<el-form-item
:label=
"item.label"
:prop=
"item.prop"
:class=
"
{ 'search-form-item': isSearch }"
:label-position="item.labelPosition || 'top'">
<!-- Input -->
<el-input
v-if=
"item.type === 'input'"
v-model=
"localModel[item.prop]"
:placeholder=
"item.placeholder || `请输入$
{item.label}`" :clearable="true"
@input="(val) => handleNumberInput(val, item)"
@change="(val) => handleModelChange(val, item)" />
<!-- Select (支持 dictType / api / options) -->
<el-select
v-else-if=
"item.type === 'select'"
v-model=
"localModel[item.prop]"
:multiple=
"!!item.multiple"
:placeholder=
"item.placeholder || `请选择$
{item.label}`"
:clearable="true" filterable :loading="remoteLoading[item.prop] || false"
@change="(val) => handleModelChange(val, item)" @focus="() => loadRemoteOptions(item)"
@filter-change="(keyword) => handleFilterChange(keyword, item)">
<el-option
v-for=
"opt in getSelectOptions(item)"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
<!-- Date -->
<el-date-picker
v-else-if=
"item.type === 'date'"
v-model=
"localModel[item.prop]"
type=
"date"
:placeholder=
"`选择$
{item.label}`" :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-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-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]"
@
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
type=
"textarea"
placeholder=
"请输入"
:clearable=
"true"
@
change=
"(val) => handleModelChange(val, item)"
/>
<span
v-else
>
不支持的类型:
{{
item
.
type
}}
</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-form
ref=
"formRef"
:model=
"localModel"
:rules=
"formRules"
label-width=
"auto"
v-bind=
"$attrs"
:validate-on-rule-change=
"false"
>
<el-row
:gutter=
"20"
>
<el-col
v-for=
"item in visibleConfig"
:key=
"item.prop"
:span=
"item.span || 6"
>
<el-form-item
:label=
"item.label"
:prop=
"item.prop"
:class=
"
{ 'search-form-item': isSearch }"
:label-position="item.labelPosition || 'top'"
>
<!-- Input -->
<el-input
v-if=
"item.type === 'input'"
v-model=
"localModel[item.prop]"
:placeholder=
"item.placeholder || `请输入$
{item.label}`"
:clearable="true"
:disabled="item.disabled"
@input="val => handleNumberInput(val, item)"
@change="val => handleModelChange(val, item)"
/>
<!-- Select (支持 dictType / api / options) -->
<el-select
v-else-if=
"item.type === 'select'"
v-model=
"localModel[item.prop]"
:multiple=
"!!item.multiple"
:placeholder=
"item.placeholder || `请选择$
{item.label}`"
:clearable="true"
filterable
:disabled="item.disabled"
:loading="remoteLoading[item.prop] || false"
@change="val => handleModelChange(val, item)"
@focus="() => loadRemoteOptions(item)"
@filter-change="keyword => handleFilterChange(keyword, item)"
>
<el-option
v-for=
"opt in getSelectOptions(item)"
:key=
"opt.value"
:label=
"opt.label"
:value=
"opt.value"
/>
</el-select>
<!-- Date -->
<el-date-picker
v-else-if=
"item.type === 'date'"
v-model=
"localModel[item.prop]"
type=
"date"
:placeholder=
"`选择$
{item.label}`"
: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>
<
script
setup
>
import
{
ref
,
watch
,
onMounted
,
nextTick
,
computed
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
import
{
Upload
}
from
'@element-plus/icons-vue'
// 🔑 引入你的字典方法
import
useDictStore
from
'@/store/modules/dict'
import
{
getDicts
}
from
'@/api/system/dict/data'
import
request
from
'@/utils/request'
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
)
{
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
if
(
Array
.
isArray
(
obj
))
return
obj
.
map
(
deepCloneConfig
)
const
cloned
=
{}
for
(
const
key
in
obj
)
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
obj
,
key
))
{
const
val
=
obj
[
key
]
cloned
[
key
]
=
typeof
val
===
'function'
?
val
:
deepCloneConfig
(
val
)
}
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
if
(
Array
.
isArray
(
obj
))
return
obj
.
map
(
deepCloneConfig
)
const
cloned
=
{}
for
(
const
key
in
obj
)
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
obj
,
key
))
{
const
val
=
obj
[
key
]
cloned
[
key
]
=
typeof
val
===
'function'
?
val
:
deepCloneConfig
(
val
)
}
return
cloned
}
return
cloned
}
function
parseToDate
(
str
)
{
if
(
!
str
)
return
null
if
(
str
===
'today'
)
{
return
dayjs
().
startOf
(
'day'
)
}
if
(
typeof
str
===
'string'
)
{
const
d
=
dayjs
(
str
)
return
d
.
isValid
()
?
d
.
startOf
(
'day'
)
:
null
}
if
(
str
instanceof
Date
)
{
return
dayjs
(
str
).
startOf
(
'day'
)
}
return
null
if
(
!
str
)
return
null
if
(
str
===
'today'
)
{
return
dayjs
().
startOf
(
'day'
)
}
if
(
typeof
str
===
'string'
)
{
const
d
=
dayjs
(
str
)
return
d
.
isValid
()
?
d
.
startOf
(
'day'
)
:
null
}
if
(
str
instanceof
Date
)
{
return
dayjs
(
str
).
startOf
(
'day'
)
}
return
null
}
// ==================== 生成 disabledDate 函数 ====================
function
getDisabledDateFn
(
item
)
{
const
{
minDate
,
maxDate
}
=
item
const
{
minDate
,
maxDate
}
=
item
// 如果都没有限制,返回 null(不禁用任何日期)
if
(
minDate
==
null
&&
maxDate
==
null
)
{
return
()
=>
false
}
// 如果都没有限制,返回 null(不禁用任何日期)
if
(
minDate
==
null
&&
maxDate
==
null
)
{
return
()
=>
false
}
return
(
date
)
=>
{
const
currentDate
=
dayjs
(
date
).
startOf
(
'day'
)
let
minD
=
null
let
maxD
=
null
// 解析最小日期
if
(
minDate
!=
null
)
{
if
(
typeof
minDate
===
'function'
)
{
const
val
=
minDate
(
localModel
.
value
)
minD
=
parseToDate
(
val
)
}
else
{
minD
=
parseToDate
(
minDate
)
}
}
return
date
=>
{
const
currentDate
=
dayjs
(
date
).
startOf
(
'day'
)
let
minD
=
null
let
maxD
=
null
// 解析最小日期
if
(
minDate
!=
null
)
{
if
(
typeof
minDate
===
'function'
)
{
const
val
=
minDate
(
localModel
.
value
)
minD
=
parseToDate
(
val
)
}
else
{
minD
=
parseToDate
(
minDate
)
}
}
// 解析最大日期
if
(
maxDate
!=
null
)
{
if
(
typeof
maxDate
===
'function'
)
{
const
val
=
maxDate
(
localModel
.
value
)
maxD
=
parseToDate
(
val
)
}
else
{
maxD
=
parseToDate
(
maxDate
)
}
}
// 解析最大日期
if
(
maxDate
!=
null
)
{
if
(
typeof
maxDate
===
'function'
)
{
const
val
=
maxDate
(
localModel
.
value
)
maxD
=
parseToDate
(
val
)
}
else
{
maxD
=
parseToDate
(
maxDate
)
}
}
// 判断是否被禁用
if
(
minD
&&
currentDate
.
isBefore
(
minD
))
{
return
true
}
if
(
maxD
&&
currentDate
.
isAfter
(
maxD
))
{
return
true
}
return
false
// 判断是否被禁用
if
(
minD
&&
currentDate
.
isBefore
(
minD
))
{
return
true
}
if
(
maxD
&&
currentDate
.
isAfter
(
maxD
))
{
return
true
}
return
false
}
}
// ==================== Props & Emits ====================
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
config
:
{
type
:
Array
,
default
:
()
=>
[]
},
isSearch
:
{
type
:
Boolean
,
default
:
false
}
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
config
:
{
type
:
Array
,
default
:
()
=>
[]
},
isSearch
:
{
type
:
Boolean
,
default
:
false
}
})
const
emit
=
defineEmits
([
'update:modelValue'
,
'update'
])
...
...
@@ -157,61 +312,61 @@ const remoteLoading = ref({}) // { prop: boolean }
// ==================== 条件显隐 & 规则 ====================
const
visibleConfig
=
computed
(()
=>
{
return
internalConfig
.
value
.
filter
(
item
=>
{
if
(
typeof
item
.
visible
===
'function'
)
{
return
item
.
visible
(
localModel
.
value
)
}
return
true
})
return
internalConfig
.
value
.
filter
(
item
=>
{
if
(
typeof
item
.
visible
===
'function'
)
{
return
item
.
visible
(
localModel
.
value
)
}
return
true
})
})
const
formRules
=
computed
(()
=>
{
const
rules
=
{}
visibleConfig
.
value
.
forEach
(
item
=>
{
if
(
item
.
rules
)
rules
[
item
.
prop
]
=
item
.
rules
})
return
rules
const
rules
=
{}
visibleConfig
.
value
.
forEach
(
item
=>
{
if
(
item
.
rules
)
rules
[
item
.
prop
]
=
item
.
rules
})
return
rules
})
// 1. 外部 modelValue 变化时,安全同步(仅当内容不同时)
// 监听 config 变化(支持动态 config)
watch
(
()
=>
props
.
config
,
(
newConfig
)
=>
{
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
??
''
}
}
()
=>
props
.
config
,
newConfig
=>
{
if
(
!
newConfig
||
newConfig
.
length
===
0
)
return
// ✅ 在这里同步 modelValue(包括 extra 字段)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
},
{
immediate
:
true
}
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 字段)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
},
{
immediate
:
true
}
)
console
.
log
(
'🚀 子组件 props.modelValue 初始值:'
,
props
.
modelValue
)
// 监听 modelValue(用于后续外部更新)
watch
(
()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
!
newVal
||
!
internalConfig
.
value
)
return
// ✅ 同样使用 sync 函数
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
},
{
deep
:
true
}
()
=>
props
.
modelValue
,
newVal
=>
{
if
(
!
newVal
||
!
internalConfig
.
value
)
return
// ✅ 同样使用 sync 函数
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
},
{
deep
:
true
}
)
// 提取同步逻辑
...
...
@@ -219,7 +374,8 @@ function syncModelFromProps(newModelValue, newConfig) {
if
(
!
newModelValue
||
!
newConfig
)
return
{}
const
synced
=
{}
console
.
log
(
'newConfig---------------------'
,
newConfig
)
console
.
log
(
'newModelValue---------------------'
,
newModelValue
)
// 1. 同步主字段
for
(
const
item
of
newConfig
)
{
const
key
=
item
.
prop
...
...
@@ -231,16 +387,29 @@ function syncModelFromProps(newModelValue, newConfig) {
synced
[
key
]
=
item
.
defaultValue
??
''
}
}
// 2. 同步 extra 字段(从 newModelValue 中的 sourceField 重新计算)
// 2. 同步 extra 字段(通过 options 反查)
for
(
const
item
of
newConfig
)
{
const
sourceField
=
item
.
prop
const
extraMap
=
item
.
onChangeExtraFields
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'
)
{
// newModelValue 中有 sourceField → 重新计算 extra
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
const
val
=
getNestedValue
(
sourceObj
,
subPath
)
if
(
val
!==
undefined
)
{
...
...
@@ -267,35 +436,38 @@ function syncModelFromProps(newModelValue, newConfig) {
}
}
// 4. 保留
其他不在 config 中的
字段
// 4. 保留
newModelValue 中已有的 extra 字段和其他额外
字段
for
(
const
key
in
newModelValue
)
{
if
(
!
synced
.
hasOwnProperty
(
key
)
&&
!
newConfig
.
some
(
item
=>
item
.
prop
===
key
)
)
{
const
isExtraTarget
=
newConfig
.
some
(
item
=>
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
)
if
(
!
isExtraTarget
)
{
synced
[
key
]
=
newModelValue
[
key
]
}
// 如果已经同步过了(比如主字段或第2步写入的extra),跳过
if
(
synced
.
hasOwnProperty
(
key
))
continue
// 判断是否是某个 extra 目标字段
const
isExtraTarget
=
newConfig
.
some
(
item
=>
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
)
// 如果是 extra 字段,且 newModelValue 里有值 → 保留它!
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
}
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
)
{
dictLoaded
.
value
.
add
(
prop
)
// 尝试同步该字段
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
}
dictLoaded
.
value
.
add
(
prop
)
// 尝试同步该字段
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
}
}
// 2. 用户操作导致 localModel 变化时,emit(防抖可选)
...
...
@@ -339,294 +511,299 @@ function handleModelChange(value, item) {
}
// 辅助函数:浅比较两个对象
function
isEqualShallow
(
a
,
b
)
{
const
keysA
=
Object
.
keys
(
a
)
const
keysB
=
Object
.
keys
(
b
)
if
(
keysA
.
length
!==
keysB
.
length
)
return
false
for
(
let
key
of
keysA
)
{
if
(
a
[
key
]
!==
b
[
key
])
return
false
}
return
true
const
keysA
=
Object
.
keys
(
a
)
const
keysB
=
Object
.
keys
(
b
)
if
(
keysA
.
length
!==
keysB
.
length
)
return
false
for
(
let
key
of
keysA
)
{
if
(
a
[
key
]
!==
b
[
key
])
return
false
}
return
true
}
// ==================== 加载字典选项 ====================
async
function
loadDictOptions
(
dictType
)
{
const
dictStore
=
useDictStore
()
let
options
=
dictStore
.
getDict
(
dictType
)
const
dictStore
=
useDictStore
()
let
options
=
dictStore
.
getDict
(
dictType
)
if
(
options
&&
options
.
length
>
0
)
{
return
options
}
if
(
options
&&
options
.
length
>
0
)
{
return
options
}
try
{
const
resp
=
await
getDicts
(
dictType
)
options
=
resp
.
data
.
map
(
p
=>
({
label
:
p
.
itemLabel
,
value
:
p
.
itemValue
,
raw
:
p
}))
dictStore
.
setDict
(
dictType
,
options
)
return
options
}
catch
(
err
)
{
console
.
error
(
`加载字典
${
dictType
}
失败`
,
err
)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
return
[]
}
try
{
const
resp
=
await
getDicts
(
dictType
)
options
=
resp
.
data
.
map
(
p
=>
({
label
:
p
.
itemLabel
,
value
:
p
.
itemValue
,
raw
:
p
}))
dictStore
.
setDict
(
dictType
,
options
)
return
options
}
catch
(
err
)
{
console
.
error
(
`加载字典
${
dictType
}
失败`
,
err
)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
return
[]
}
}
// ==================== 初始化 ====================
onMounted
(
async
()
=>
{
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
??
''
}
}
internalConfig
.
value
=
deepCloneConfig
(
props
.
config
)
// 预加载 dictType
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 时)
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
??
''
}
}
if
(
Object
.
keys
(
initialData
).
length
>
0
)
{
localModel
.
value
=
{
...
localModel
.
value
,
...
initialData
}
// 预加载 dictType
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 选项 ====================
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
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// ← 必须存 raw
}))
}
else
if
(
item
.
options
)
{
// 静态选项s
return
item
.
options
.
map
(
opt
=>
({
value
:
opt
.
value
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// 保留原始
}))
}
return
[]
return
(
remoteOptions
.
value
[
key
]
||
[]).
map
(
opt
=>
({
value
:
opt
.
value
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// ← 必须存 raw
}))
}
else
if
(
item
.
options
)
{
// 静态选项s
return
item
.
options
.
map
(
opt
=>
({
value
:
opt
.
value
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// 保留原始
}))
}
return
[]
}
async
function
loadRemoteOptionsForInit
(
item
)
{
const
{
prop
,
api
,
requestParams
=
{}
}
=
item
try
{
// 构造请求体:只传 requestParams,不传 keyword
const
payload
=
{
...(
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
||
[]
const
{
prop
,
api
,
requestParams
=
{}
}
=
item
try
{
// 构造请求体:只传 requestParams,不传 keyword
const
payload
=
{
...(
requestParams
||
{})
}
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
newOptions
=
list
.
map
(
i
=>
({
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
}))
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
data
:
payload
})
remoteOptions
.
value
[
prop
]
=
newOptions
markDictLoaded
(
prop
)
// ← 关键:标记已加载
}
catch
(
err
)
{
ElMessage
.
error
(
`预加载
${
item
.
label
}
失败`
)
remoteOptions
.
value
[
prop
]
=
[]
}
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 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 时加载,无搜索词) ====================
async
function
loadRemoteOptions
(
item
)
{
const
{
prop
,
api
,
requestParams
=
{},
keywordField
,
debounceWait
,
...
rest
}
=
item
if
(
!
api
||
remoteOptions
.
value
[
prop
]?.
length
>
0
)
return
const
{
prop
,
api
,
requestParams
=
{},
keywordField
,
debounceWait
,
...
rest
}
=
item
if
(
!
api
||
remoteOptions
.
value
[
prop
]?.
length
>
0
)
return
try
{
remoteLoading
.
value
[
prop
]
=
true
try
{
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:合并 requestParams + 分页(默认第一页)
const
payload
=
{
...(
requestParams
||
{})
// 注意:此时无 keyword,所以不加 keywordField
}
// 构造请求体:合并 requestParams + 分页(默认第一页)
const
payload
=
{
...(
requestParams
||
{})
// 注意:此时无 keyword,所以不加 keywordField
}
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
// ← 改为 POST
data
:
payload
// ← 参数放 body
})
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
// ← 改为 POST
data
:
payload
// ← 参数放 body
})
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 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
]
=
[]
}
finally
{
remoteLoading
.
value
[
prop
]
=
false
}
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 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
]
=
[]
}
finally
{
remoteLoading
.
value
[
prop
]
=
false
}
}
// ==================== 远程搜索(带关键词,防抖) ====================
let
searchTimeout
=
null
function
handleFilterChange
(
keyword
,
item
)
{
const
{
prop
,
api
,
requestParams
=
{},
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
if
(
!
api
)
return
clearTimeout
(
searchTimeout
)
searchTimeout
=
setTimeout
(
async
()
=>
{
try
{
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:requestParams + 分页 + 动态关键词字段
const
payload
=
{
...(
requestParams
||
{}),
[
keywordField
]:
keyword
// ← 动态字段名,如 name / companyName
}
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
// ← POST 请求
data
:
payload
// ← body 传参
})
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
value
:
i
[
item
.
valueKey
||
'value'
],
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
// ← 保存完整对象
}))
}
catch
(
err
)
{
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
}
finally
{
remoteLoading
.
value
[
prop
]
=
false
}
},
debounceWait
)
const
{
prop
,
api
,
requestParams
=
{},
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
if
(
!
api
)
return
clearTimeout
(
searchTimeout
)
searchTimeout
=
setTimeout
(
async
()
=>
{
try
{
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:requestParams + 分页 + 动态关键词字段
const
payload
=
{
...(
requestParams
||
{}),
[
keywordField
]:
keyword
// ← 动态字段名,如 name / companyName
}
const
res
=
await
request
({
url
:
api
,
method
:
'post'
,
// ← POST 请求
data
:
payload
// ← body 传参
})
const
list
=
typeof
item
.
transform
===
'function'
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
value
:
i
[
item
.
valueKey
||
'value'
],
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
// ← 保存完整对象
}))
}
catch
(
err
)
{
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
}
finally
{
remoteLoading
.
value
[
prop
]
=
false
}
},
debounceWait
)
}
// ==================== 数字输入处理 ====================
function
handleNumberInput
(
value
,
item
)
{
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
if
(
!
prop
)
return
let
result
=
String
(
value
??
''
).
trim
()
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
if
(
!
prop
)
return
if
(
inputType
===
'integer'
)
{
// 只保留数字
result
=
result
.
replace
(
/
[^\d]
/g
,
''
)
}
else
if
(
inputType
===
'decimal'
)
{
// 1. 只保留数字和小数点
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
let
result
=
String
(
value
??
''
).
trim
()
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if
(
result
.
startsWith
(
'.'
))
{
result
=
'0.'
+
result
.
slice
(
1
)
}
if
(
inputType
===
'integer'
)
{
// 只保留数字
result
=
result
.
replace
(
/
[^\d]
/g
,
''
)
}
else
if
(
inputType
===
'decimal'
)
{
// 1. 只保留数字和小数点
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
// 3. 保证最多一个小数点
const
parts
=
result
.
split
(
'.'
)
if
(
parts
.
length
>
2
)
{
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
}
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if
(
result
.
startsWith
(
'.'
))
{
result
=
'0.'
+
result
.
slice
(
1
)
}
// 4. 限制小数位数(但保留结尾的小数点!)
if
(
result
.
includes
(
'.'
))
{
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
// 如果小数部分超过限制,截断
if
(
decPart
.
length
>
decimalDigits
)
{
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
}
// ✅ 不再删除结尾的 '.'
}
// 3. 保证最多一个小数点
const
parts
=
result
.
split
(
'.'
)
if
(
parts
.
length
>
2
)
{
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
}
// 防止重复赋值(可选优化)
if
(
localModel
.
value
[
prop
]
!==
result
)
{
localModel
.
value
=
{
...
localModel
.
value
,
[
prop
]:
result
}
// 4. 限制小数位数(但保留结尾的小数点!)
if
(
result
.
includes
(
'.'
))
{
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
({
getFormData
()
{
return
{
...
localModel
.
value
}
},
async
validate
()
{
return
new
Promise
((
resolve
,
reject
)
=>
{
formRef
.
value
?.
validate
((
valid
)
=>
{
if
(
valid
)
resolve
(
localModel
.
value
)
else
reject
(
new
Error
(
'Validation failed'
))
})
})
},
resetForm
()
{
const
resetData
=
{}
internalConfig
.
value
.
forEach
(
item
=>
{
const
key
=
item
.
prop
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
resetData
[
key
]
=
item
.
defaultValue
??
''
}
})
localModel
.
value
=
{
...
resetData
}
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
}
getFormData
()
{
return
{
...
localModel
.
value
}
},
async
validate
()
{
return
new
Promise
((
resolve
,
reject
)
=>
{
formRef
.
value
?.
validate
(
valid
=>
{
if
(
valid
)
resolve
(
localModel
.
value
)
else
reject
(
new
Error
(
'Validation failed'
))
})
})
},
resetForm
()
{
const
resetData
=
{}
internalConfig
.
value
.
forEach
(
item
=>
{
const
key
=
item
.
prop
if
([
'checkbox-group'
,
'daterange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
}
else
if
(
item
.
type
===
'upload'
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
// upload 也是数组
}
else
{
resetData
[
key
]
=
item
.
defaultValue
??
''
}
})
localModel
.
value
=
{
...
resetData
}
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
}
})
</
script
>
<
style
scoped
>
.search-form-item
{
margin-bottom
:
20px
;
margin-bottom
:
20px
;
}
</
style
>
\ No newline at end of file
</
style
>
src/utils/2.js
View file @
223a37bd
{
"apiAppointmentInfoDto"
:
{
"isReferrerAccompany"
:
"1"
,
"isUseCar"
:
"1"
,
"isOpenAccount"
:
"1"
,
"applyType"
:
"INVESTMENT"
,
"signDate"
:
"2026-01-26 00:00:00"
,
"arrivalTime"
:
"2026-01-28 09:40:00"
,
"departureTime"
:
"2026-02-05 09:40:00"
,
"meetingPoint"
:
"INSURANCE_COMPANY"
,
"signingAddress"
:
"886"
,
"hkMobile"
:
"886"
,
"bankName"
:
"中国工商银行"
,
"bankId"
:
"bank_1001"
,
"materials"
:
"个人开户: 带好 身份证 和 实名手机 直接前往网点即可,多数情况十分钟内办结。
\
r
\
n
\
r
\
n对公开户:
\
r
\
n
\
r
\
n第一步: 致电目标银行网点对公业务部,预约并获取最新资料清单。
\
r
\
n
\
r
\
n第二步: 对照清单,准备好所有 原件 和 公章。
\
r
\
n
\
r
\
n第三步: 法定代表人(或授权经办人)携带所有材料,按约定时间前往银行办理。"
,
"bankBranchName"
:
"886"
,
"openAccountStartTime"
:
"2026-01-05 00:00:00"
,
"openAccountEndTime"
:
"2026-01-20 00:00:00"
,
"openAccountLocation"
:
"886"
,
"isBuy"
:
"1"
,
"isTj"
:
"0"
,
"openAccountNotice"
:
"886"
,
"hkMobileCode"
:
"+244"
,
"objType"
:
"phone"
,
"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"
:
[]
},
"paymentMethod"
:
"CHECK"
,
"paymentAmount"
:
"111"
,
"paymentCurrency"
:
"HKD"
,
"paymentRel"
:
"SBR"
,
"payer"
:
"111"
,
"payingBank"
:
"bank_1002"
,
"paymentAccount"
:
"1111"
,
"currency"
:
""
,
"paymentVoucherList"
:
[
{
"fileName"
:
"icon5.png"
,
"fileType"
:
"png"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/b4241a95a39d4655a79c706d7ec37f85.png"
},
{
"fileName"
:
"cardSix1.png"
,
"fileType"
:
"png"
,
"fileUrl"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png"
,
"url"
:
"https://yd-ali-oss.oss-cn-shanghai-finance-1-pub.aliyuncs.com/png/2026/01/13/41bd22390f6a4a69a5ed8d9c8758ef94.png"
}
],
"accountVerificationList"
:
[
{
"materialBizId"
:
"material_Txh02cNvT21QPOZH"
,
"fileBizIdList"
:
[]
"fileName"
:
"icon6.png"
,
"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"
,
"fileBizIdList"
:
[]
},
"fileName"
:
"icon4.png"
,
"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"
,
"fileBizIdList"
:
[]
"fileName"
:
"icon5.png"
,
"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"
,
"fileBizIdList"
:
[]
"fileName"
:
"homeSelect1.png"
,
"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/financialCenter/payables.vue
View file @
223a37bd
<
template
>
<div>
<CommonPage
:operationBtnList=
"operationBtnList"
:visibleDefaultButtons=
"visibleDefaultButtons"
:showSearchForm=
"true"
:show-pagination=
"true"
:total=
"pageTotal
"
:
current-page=
"currentPage"
:page-size=
"pageSize"
@
size-change=
"handleSizeChan
ge"
@
current-change=
"handleCurrentChange"
>
<CommonPage
:operationBtnList=
"operationBtnList"
:visibleDefaultButtons=
"visibleDefaultButtons
"
:
showSearchForm=
"true"
:show-pagination=
"true"
:total=
"pageTotal"
:current-page=
"currentPa
ge"
:page-size=
"pageSize"
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
>
<!-- 搜索区域 -->
<template
#
searchForm
>
<SearchForm
ref=
"searchFormRef"
:config=
"searchConfig"
/>
...
...
@@ -40,7 +40,7 @@
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
已出账比例
</div>
<div
class=
"card-value"
>
{{
statisticsData
.
paidAmountRatio
}}
</div>
<div
class=
"card-value"
>
{{
statisticsData
.
paidAmountRatio
}}
%
</div>
</div>
</el-card>
</el-col>
...
...
@@ -64,88 +64,98 @@
</div>
<!-- 应付款管理列表 -->
<el-table
:data=
"tableData"
height=
"400"
border
highlight-current-row
style=
"width: 100%"
v-loading=
"loading"
>
<el-table-column
prop=
"fortuneBizType"
label=
"应付款类型"
width=
"120"
fixed=
"left"
sortable
>
<template
#
default=
"
{ row }">
{{
getFortuneBizTypeLabel
(
row
.
fortuneBizType
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"payableNo"
label=
"应付款编号"
width=
"120"
sortable
/>
<el-table-column
prop=
"policyNo"
label=
"保单号"
width=
"120"
sortable
/>
<el-table-column
prop=
"broker"
label=
"转介人"
width=
"120"
sortable
/>
<el-table-column
prop=
"status"
label=
"出账状态"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_expected_fortune_status'
,
row
.
status
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"currency"
label=
"出账币种"
width=
"120"
sortable
/>
<el-table-column
prop=
"fortunePeriod"
label=
"出账期数"
width=
"120"
sortable
/>
<el-table-column
prop=
"fortuneTotalPeriod"
label=
"出账总期数"
width=
"120"
sortable
/>
<el-table-column
prop=
"fortuneName"
label=
"出账项目"
width=
"120"
sortable
/>
<el-table-column
prop=
"payoutDate"
label=
"出账日(估)"
width=
"120"
sortable
/>
<el-table-column
prop=
"actualPayoutDate"
label=
"出账日(实)"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionRatio"
label=
"出账比例(估)"
width=
"140"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
commissionRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"amount"
label=
"出账金额(估)"
width=
"140"
sortable
>
<
template
#
default=
"{ row }"
>
{{
formatCurrency
(
row
.
amount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paidRatio"
label=
"已出账比例"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
paidRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paidAmount"
label=
"已出账金额"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
formatCurrency
(
row
.
paidAmount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"pendingRatio"
label=
"待出账比例"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
pendingRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"unpaidAmount"
label=
"待出账金额(估)"
width=
"160"
sortable
>
<
template
#
default=
"{ row }"
>
{{
formatCurrency
(
row
.
unpaidAmount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"brokerRatio"
label=
"持有比例"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
brokerRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"insuranceCompany"
label=
"保险公司"
width=
"120"
sortable
/>
<el-table-column
prop=
"productName"
label=
"产品计划"
width=
"120"
sortable
/>
<el-table-column
prop=
"premium"
label=
"期交保费"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
formatCurrency
(
row
.
premium
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"statusDesc"
label=
"修改理由"
width=
"150"
/>
<el-table-column
prop=
"remark"
label=
"备注"
width=
"150"
/>
<el-table-column
v-for=
"(column, index) in payableReportTableColumns"
:key=
"index"
:prop=
"column.prop"
:label=
"column.label"
:width=
"column.width"
:sortable=
"column.sortable"
:formatter=
"column.formatter"
/>
<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
#
default=
"scope"
>
<el-button
link
type=
"primary"
size=
"small"
@
click=
"viewDetail(scope.row)"
>
查看明细
</el-button>
</
template
>
</el-table-column>
</el-table>
</template>
</CommonPage>
<!-- 查看明细列表 -->
<CommonDialog
dialogTitle=
"应付明细"
dialogWidth=
"80%"
:openDialog=
"detailDialogVisible"
:showAction=
"true"
:showClose=
"true"
@
close=
"detailDialogVisible = false"
>
<div
class=
"statistics-container"
v-if=
"detailRecordStatistics.totalPolicyCount > 0"
>
<el-row
:gutter=
"20"
>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
应出账总金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
已出账金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
待出账金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
已出账比例
</div>
<div
class=
"card-value"
>
{{ detailRecordStatistics.paidAmountRatio }}%
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
总保单数
</div>
<div
class=
"card-value"
>
{{ detailRecordStatistics.totalPolicyCount }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
总保费
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalPremiumAmount) }}
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<el-table
:data=
"payableReportTableData"
border
style=
"width: 100%;margin-bottom: 10px;"
>
<el-table-column
v-for=
"item in payableReportListTableColumns"
:key=
"item.property"
:property=
"item.property"
:label=
"item.label"
:width=
"item.width"
:formatter=
"item.formatter"
/>
<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>
<el-pagination
v-model:current-page=
"detailPageInfo.currentPage"
v-model:page-size=
"detailPageInfo.pageSize"
:page-sizes=
"[50, 100, 200, 300]"
size=
"default"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"detailPageInfo.total"
@
size-change=
"handleSizeChangeDetailRecord"
@
current-change=
"handleCurrentChangeDetailRecord"
/>
</CommonDialog>
<!-- 出账记录表格弹窗-->
<CommonDialog
dialogTitle=
"出账记录"
dialogWidth=
"80%"
:openDialog=
"payRecordDialogTableVisible"
:showAction=
"true"
:showClose=
"true"
@
close=
"payRecordDialogTableVisible = false"
>
...
...
@@ -156,13 +166,13 @@
</CommonDialog>
<!-- 新增出账记录 -->
<CommonDialog
:dialogTitle=
"editStatus === 'add' ? '新增出账记录' : '修改出账记录'"
dialogWidth=
"80%"
:openDialog=
"addPayRecordDialogVisible"
:showAction=
"true"
:showClose=
"true"
@
c
lose=
"resetAddPayRecordForm"
@
c
onfirm=
"handleConfirmAddPayRecord"
>
:openDialog=
"addPayRecordDialogVisible"
:showAction=
"true"
:showClose=
"true"
@
close=
"resetAddPayRecordForm"
@
confirm=
"handleConfirmAddPayRecord"
>
<SearchForm
ref=
"addPayRecordFormRef"
:config=
"addPayRecordFormConfig"
v-model=
"addPayRecordFormModel"
/>
</CommonDialog>
<!-- 设置出账状态 -->
<CommonDialog
dialogTitle=
"设置出账状态"
dialogWidth=
"80%"
:openDialog=
"setPayRecordStatusDialogVisible"
:showAction=
"
fals
e"
:showClose=
"true"
@
close=
"setPayRecordStatusDialogVisible = false"
:showAction=
"
tru
e"
:showClose=
"true"
@
close=
"setPayRecordStatusDialogVisible = false"
@
confirm=
"handleConfirmSetPayRecordStatus"
>
<SearchForm
ref=
"setPayRecordStatusFormRef"
:config=
"setPayRecordStatusFormConfig"
/>
</CommonDialog>
...
...
@@ -175,12 +185,63 @@ import CommonPage from '@/components/commonPage'
import
{
ref
,
reactive
}
from
'vue'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
formatCurrency
}
from
'@/utils/number'
import
{
expectedFortuneList
,
payRecordList
,
addPayRecord
,
updatePayRecord
,
exportPayRecord
}
from
'@/api/financial/commission'
import
{
expectedFortuneList
,
payRecordList
,
addPayRecord
,
updatePayRecord
,
exportPayRecord
,
payableReport
}
from
'@/api/financial/commission'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
import
CommonDialog
from
'@/components/commonDialog'
import
{
loadDicts
,
getDictLabel
}
from
'@/utils/useDict'
import
useUserStore
from
'@/store/modules/user'
import
{
safeDownload
}
from
'@/utils/safeDownload'
const
payableReportTableData
=
ref
([])
const
payableReportTableColumns
=
ref
([
{
prop
:
'policyNo'
,
label
:
'保单号'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
policyNo
||
'-'
},
{
prop
:
'broker'
,
label
:
'转介人(主)'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
broker
||
'-'
},
{
prop
:
'fortunePeriod'
,
label
:
'出账期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
fortunePeriod
||
'-'
},
{
prop
:
'fortuneTotalPeriod'
,
label
:
'出账总期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
fortuneTotalPeriod
||
'-'
},
{
prop
:
'payoutDate'
,
label
:
'出账日(估)'
,
sortable
:
true
,
width
:
'130'
,
},
{
prop
:
'paidRatio'
,
label
:
'已出账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
paidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'unpaidRatio'
,
label
:
'待出账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
unpaidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'paidAmount'
,
label
:
'已出账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
paidAmount
||
0
)
},
{
prop
:
'unpaidAmount'
,
label
:
'待出账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
unpaidAmount
||
0
)
},
{
prop
:
'amount'
,
label
:
'应出账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
amount
||
0
)
},
{
prop
:
'currency'
,
label
:
'出账币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
currency
||
'-'
},
{
prop
:
'premium'
,
label
:
'期交保费'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
premium
||
0
)
},
{
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
insuranceCompany
||
'-'
},
{
prop
:
'productName'
,
label
:
'产品计划'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
productName
||
'-'
},
{
prop
:
'policyCurrency'
,
label
:
'保单币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
policyCurrency
||
'-'
}
])
const
detailDialogVisible
=
ref
(
false
)
// 应付明细列表
const
payableReportListTableColumns
=
ref
([
{
prop
:
'fortuneBizType'
,
label
:
'应付款类型'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
getFortuneBizTypeLabel
(
row
.
fortuneBizType
)
||
'-'
},
{
prop
:
'payableNo'
,
label
:
'应付账款编号'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
payableNo
||
'-'
},
{
prop
:
'policyNo'
,
label
:
'保单号'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
policyNo
||
'-'
},
{
prop
:
'broker'
,
label
:
'转介人'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
broker
||
'-'
},
{
prop
:
'status'
,
label
:
'出账状态'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
getDictLabel
(
'csf_expected_fortune_status'
,
row
.
status
)
||
'-'
},
{
prop
:
'currency'
,
label
:
'出账币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
currency
||
'-'
},
{
prop
:
'fortunePeriod'
,
label
:
'出账期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
fortunePeriod
||
'-'
},
{
prop
:
'fortuneTotalPeriod'
,
label
:
'出账总期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
fortuneTotalPeriod
||
'-'
},
{
prop
:
'payoutDate'
,
label
:
'出账日(估)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
payoutDate
||
'-'
},
{
prop
:
'actualPayoutDate'
,
label
:
'出账日(实)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
actualPayoutDate
||
'-'
},
{
prop
:
'commissionRatio'
,
label
:
'出账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
commissionRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'amount'
,
label
:
'应出账金额(估)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
amount
||
0
)
},
{
prop
:
'paidRatio'
,
label
:
'已出账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
paidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'paidAmount'
,
label
:
'已出账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
paidAmount
||
0
)
},
{
prop
:
'unpaidRatio'
,
label
:
'待出账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
unpaidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'unpaidAmount'
,
label
:
'待出账金额(估)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
unpaidAmount
||
0
)
},
{
prop
:
'brokerRatio'
,
label
:
'持有比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
brokerRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'premium'
,
label
:
'期交保费'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
premium
||
0
)
},
{
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
insuranceCompany
||
'-'
},
{
prop
:
'productName'
,
label
:
'产品计划'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
productName
||
'-'
},
{
prop
:
'statusDesc'
,
label
:
'修改理由'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
statusDesc
||
'-'
},
{
prop
:
'remark'
,
label
:
'备注'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
remark
||
'-'
},
])
// 设置出账状态
const
setPayRecordStatusDialogVisible
=
ref
(
false
)
const
selectedRow
=
ref
(
null
)
...
...
@@ -198,7 +259,7 @@ const setPayRecordStatusFormConfig = ref([
},
])
const
visibleDefaultButtons
=
ref
([
'add'
,
'export'
,
'reset'
,
'query'
'add'
,
'export'
,
'reset'
,
'query'
])
const
userStore
=
useUserStore
()
// 应收单类型
...
...
@@ -430,9 +491,9 @@ const handleConfirmAddPayRecord = async () => {
}
// 弹窗表单重置
const
resetAddPayRecordForm
=
()
=>
{
addPayRecordFormRef
.
value
.
resetForm
()
editStatus
.
value
=
'add'
addPayRecordDialogVisible
.
value
=
false
addPayRecordFormRef
.
value
.
resetForm
()
editStatus
.
value
=
'add'
addPayRecordDialogVisible
.
value
=
false
}
...
...
@@ -450,8 +511,8 @@ const handleSelect = async (e, row) => {
{
property
:
'fortuneName'
,
label
:
'出账项目'
,
width
:
'150'
},
{
property
:
'currentPaymentAmount'
,
label
:
'出账金额'
,
width
:
'150'
},
{
property
:
'currency'
,
label
:
'出账币种'
,
width
:
'150'
},
{
property
:
'currentPaymentRatio'
,
label
:
'出账比例'
,
width
:
'150'
,
formatter
:
(
row
)
=>
`
${
row
.
currentPaymentRatio
}
%`
},
{
property
:
'fortuneUnpaidRatio'
,
label
:
'待出账比例'
,
width
:
'150'
,
formatter
:
(
row
)
=>
`
${
row
.
fortuneUnpaidRatio
}
%`
},
{
property
:
'currentPaymentRatio'
,
label
:
'出账比例'
,
width
:
'150'
,
formatter
:
(
row
)
=>
`
${
row
.
currentPaymentRatio
}
%`
},
{
property
:
'fortuneUnpaidRatio'
,
label
:
'待出账比例'
,
width
:
'150'
,
formatter
:
(
row
)
=>
`
${
row
.
fortuneUnpaidRatio
}
%`
},
{
property
:
'fortunePeriod'
,
label
:
'佣金期数'
,
width
:
'150'
},
{
property
:
'fortuneTotalPeriod'
,
label
:
'总期数'
,
width
:
'150'
},
{
property
:
'reconciliationOperator'
,
label
:
'操作人'
,
width
:
'150'
},
...
...
@@ -517,16 +578,16 @@ const handleImport = () => {
}
const
handleExport
=
async
()
=>
{
// 获取搜索参数
const
params
=
searchFormRef
.
value
?.
getFormData
()
||
{}
const
response
=
await
exportPayRecord
(
params
)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const
fileName
=
`应付款导出_
${
new
Date
().
toLocaleString
().
replace
(
/
\/
/g
,
''
).
replace
(
/:/g
,
''
).
replace
(
/
\s
/g
,
''
)}
.xlsx`
await
safeDownload
(
response
,
fileName
,
'application/vnd.ms-excel;charset=utf-8'
)
// 获取搜索参数
const
params
=
searchFormRef
.
value
?.
getFormData
()
||
{}
const
response
=
await
exportPayRecord
(
params
)
// 文件名设置为应收款导出_yyyy-MM-dd hh:mm:ss.xlsx,不需要-,用字符串
const
fileName
=
`应付款导出_
${
new
Date
().
toLocaleString
().
replace
(
/
\/
/g
,
''
).
replace
(
/:/g
,
''
).
replace
(
/
\s
/g
,
''
)}
.xlsx`
await
safeDownload
(
response
,
fileName
,
'application/vnd.ms-excel;charset=utf-8'
)
}
const
handleReset
=
()
=>
{
...
...
@@ -595,7 +656,7 @@ const loadTableData = async () => {
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
}
const
response
=
await
expectedFortuneLis
t
(
params
)
const
response
=
await
payableRepor
t
(
params
)
tableData
.
value
=
response
.
data
.
page
.
records
pageTotal
.
value
=
response
.
data
.
page
.
total
pageSize
.
value
=
response
.
data
.
page
.
size
...
...
@@ -675,6 +736,63 @@ onMounted(async () => {
const
formatStatus
=
(
row
,
column
)
=>
{
return
getDictLabel
(
'csf_expected_fortune_status'
,
row
.
status
)
// 实时查缓存
}
const
detailRecordStatistics
=
ref
({})
const
detailPageInfo
=
ref
({
pageNo
:
1
,
pageSize
:
10
,
currentPage
:
1
,
total
:
0
})
const
selectedDetailRecordRow
=
ref
({})
// 应付款管理列表查询
const
expectedFortuneListData
=
async
()
=>
{
loading
.
value
=
true
try
{
const
params
=
{
policyNo
:
selectedDetailRecordRow
.
value
.
policyNo
,
fortunePeriod
:
selectedDetailRecordRow
.
value
.
fortunePeriod
,
fortuneBizType
:
selectedDetailRecordRow
.
value
.
fortuneBizType
,
payableNo
:
selectedDetailRecordRow
.
value
.
payableNo
,
pageNo
:
detailPageInfo
.
value
.
currentPage
,
pageSize
:
detailPageInfo
.
value
.
pageSize
}
const
response
=
await
expectedFortuneList
(
params
)
payableReportTableData
.
value
=
response
.
data
.
page
.
records
detailPageInfo
.
value
.
total
=
response
.
data
.
page
.
total
detailPageInfo
.
value
.
pageSize
=
response
.
data
.
page
.
size
// 统计信息
detailRecordStatistics
.
value
=
{
totalExpectedAmount
:
response
.
data
.
statisticsVO
.
totalExpectedAmount
,
totalPaidAmount
:
response
.
data
.
statisticsVO
.
totalPaidAmount
,
totalUnpaidAmount
:
response
.
data
.
statisticsVO
.
totalUnpaidAmount
,
paidAmountRatio
:
response
.
data
.
statisticsVO
.
paidAmountRatio
,
totalPolicyCount
:
response
.
data
.
statisticsVO
.
totalPolicyCount
,
totalPremiumAmount
:
response
.
data
.
statisticsVO
.
totalPremiumAmount
}
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
// ElMessage.error('加载数据失败')
}
finally
{
loading
.
value
=
false
}
}
const
viewDetail
=
(
row
)
=>
{
selectedDetailRecordRow
.
value
=
row
detailDialogVisible
.
value
=
true
expectedFortuneListData
()
}
// 分页事件
const
handleSizeChangeDetailRecord
=
(
val
)
=>
{
detailPageInfo
.
value
.
pageSize
=
val
expectedFortuneListData
()
}
// 分页事件
const
handleCurrentChangeDetailRecord
=
(
val
)
=>
{
detailPageInfo
.
value
.
currentPage
=
val
expectedFortuneListData
()
}
</
script
>
...
...
@@ -692,4 +810,5 @@ const formatStatus = (row, column) => {
border-top
:
1px
solid
#ebeef5
;
text-align
:
right
;
}
</
style
>
\ No newline at end of file
src/views/financialCenter/receivables.vue
View file @
223a37bd
...
...
@@ -29,83 +29,106 @@
</el-col>
</el-row>
</div>
<!-- 应收款管理列表 -->
<el-table
:data=
"tableData"
height=
"400"
border
highlight-current-row
style=
"width: 100%"
v-loading=
"loading"
>
<el-table-column
prop=
"commissionBizType"
label=
"应收单类型"
width=
"130"
sortable
>
<template
#
default=
"
{ row }">
{{
getCommissionBizTypeLabel
(
row
.
commissionBizType
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"receivableNo"
label=
"应收款编号"
width=
"150"
sortable
/>
<el-table-column
prop=
"policyNo"
label=
"保单号"
width=
"120"
fixed=
"left"
sortable
/>
<el-table-column
prop=
"reconciliationCompany"
label=
"对账公司"
width=
"120"
sortable
/>
<el-table-column
prop=
"status"
label=
"入账状态"
width=
"120"
sortable
:formatter=
"formatStatus"
/>
<el-table-column
prop=
"commissionPeriod"
label=
"入账期数"
width=
"120"
sortable
/>
<el-table-column
prop=
"totalPeriod"
label=
"入账总期数"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionName"
label=
"入账项目"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionDate"
label=
"入账日(估)"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionRatio"
label=
"入账比例(估)"
width=
"140"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
commissionRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"expectedAmount"
label=
"入账金额(估)"
width=
"140"
sortable
>
<
template
#
default=
"{ row }"
>
{{
numberWithCommas
(
row
.
expectedAmount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paidRatio"
label=
"已入账比例"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
paidRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"paidAmount"
label=
"已入账金额"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
numberWithCommas
(
row
.
paidAmount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"pendingRatio"
label=
"待入账比例"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
(
row
.
pendingRatio
||
0
)
+
'%'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"pendingAmount"
label=
"待入账金额(估)"
width=
"160"
sortable
>
<
template
#
default=
"{ row }"
>
{{
numberWithCommas
(
row
.
pendingAmount
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"currency"
label=
"入账币种"
width=
"100"
/>
<el-table-column
prop=
"defaultExchangeRate"
label=
"结算汇率(估)"
width=
"120"
/>
<el-table-column
prop=
"insuranceCompany"
label=
"保险公司"
width=
"120"
sortable
/>
<el-table-column
prop=
"productName"
label=
"产品计划"
width=
"120"
sortable
/>
<el-table-column
prop=
"premium"
label=
"期交保费"
width=
"120"
sortable
>
<
template
#
default=
"{ row }"
>
{{
numberWithCommas
(
row
.
premium
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"statusDesc"
label=
"入账状态修改理由"
width=
"150"
/>
<el-table-column
prop=
"remark"
label=
"备注"
width=
"150"
/>
<el-table-column
v-for=
"(column, index) in receivableReportTableColumns"
:key=
"index"
:prop=
"column.prop"
:label=
"column.label"
:width=
"column.width"
:sortable=
"column.sortable"
:formatter=
"column.formatter"
/>
<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
#
default=
"scope"
>
<el-button
link
type=
"primary"
size=
"small"
@
click=
"viewDetail(scope.row)"
>
查看明细
</el-button>
</
template
>
</el-table-column>
</el-table>
</template>
</CommonPage>
<!-- 原有弹窗(不变) -->
<CommonDialog
dialogTitle=
"应收明细"
dialogWidth=
"80%"
:openDialog=
"detailDialogVisible"
:showAction=
"false"
:showClose=
"true"
@
close=
"detailDialogVisible = false"
>
<div
class=
"statistics-container"
v-if=
"detailRecordStatistics.totalPolicyCount > 0"
>
<el-row
:gutter=
"20"
>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
应出账总金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalExpectedAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
已出账金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalPaidAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
待出账金额
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalUnpaidAmount) }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
已出账比例
</div>
<div
class=
"card-value"
>
{{ detailRecordStatistics.paidAmountRatio }}%
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
总保单数
</div>
<div
class=
"card-value"
>
{{ detailRecordStatistics.totalPolicyCount }}
</div>
</div>
</el-card>
</el-col>
<el-col
:xs=
"24"
:sm=
"12"
:md=
"4"
:lg=
"4"
>
<el-card
shadow=
"hover"
class=
"statistics-card"
>
<div
class=
"card-content"
>
<div
class=
"card-label"
>
总保费
</div>
<div
class=
"card-value"
>
{{ formatCurrency(detailRecordStatistics.totalPremiumAmount) }}
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<el-table
:data=
"receivableReportTableData"
border
style=
"width: 100%;margin-bottom: 10px;min-height: 300px;"
>
<el-table-column
v-for=
"item in receivableReportItemTableColumns"
:key=
"item.property"
:prop=
"item.prop"
:label=
"item.label"
:width=
"item.width"
/>
<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>
<el-pagination
v-model:current-page=
"detailPageInfo.currentPage"
v-model:page-size=
"detailPageInfo.pageSize"
:page-sizes=
"[50, 100, 200, 300]"
size=
"default"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"detailPageInfo.total"
@
size-change=
"handleSizeChangeDetailRecord"
@
current-change=
"handleCurrentChangeDetailRecord"
/>
</CommonDialog>
<CommonDialog
dialogTitle=
"入账记录"
dialogWidth=
"80%"
:openDialog=
"entryRecordDialogTableVisible"
:showAction=
"false"
:showClose=
"true"
@
close=
"entryRecordDialogTableVisible = false"
>
<el-table
:data=
"entryRecordDialogTableData"
border
style=
"width: 100%"
>
...
...
@@ -150,12 +173,16 @@ import CommonDialog from '@/components/commonDialog'
import
{
ref
,
reactive
,
onMounted
,
computed
}
from
'vue'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
MoreFilled
}
from
'@element-plus/icons-vue'
import
{
receivedFortuneList
,
updateCommissionExpected
,
commissionEntryEditRecords
,
exportReceivedFortune
,
commissionExpectedRecord
,
addReceivedFortune
}
from
'@/api/financial/commission'
import
{
receivedFortuneList
,
updateCommissionExpected
,
commissionEntryEditRecords
,
exportReceivedFortune
,
commissionExpectedRecord
,
addReceivedFortune
,
receivableReport
}
from
'@/api/financial/commission'
import
{
numberWithCommas
}
from
'@/utils/index'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
import
{
loadDicts
,
getDictLabel
}
from
'@/utils/useDict'
import
{
safeDownload
}
from
'@/utils/safeDownload'
import
useUserStore
from
'@/store/modules/user'
import
{
formatCurrency
}
from
'@/utils/number'
const
userStore
=
useUserStore
()
// 应收单类型
...
...
@@ -551,18 +578,18 @@ const loadTableData = async () => {
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
}
const
response
=
await
receiv
edFortuneLis
t
(
params
)
const
response
=
await
receiv
ableRepor
t
(
params
)
tableData
.
value
=
response
.
data
.
page
.
records
||
[]
pageTotal
.
value
=
response
.
data
.
page
.
total
||
0
pageSize
.
value
=
response
.
data
.
page
.
size
||
1
0
pageSize
.
value
=
response
.
data
.
page
.
size
||
5
0
// 统计信息
statisticsData
.
value
=
{
totalAmount
:
response
.
data
.
expectedS
tatisticsVO
.
totalAmount
,
totalPaidAmount
:
response
.
data
.
expectedS
tatisticsVO
.
totalPaidAmount
,
pendingPaidAmount
:
response
.
data
.
expectedS
tatisticsVO
.
pendingPaidAmount
,
paidAmountRatio
:
response
.
data
.
expectedS
tatisticsVO
.
paidAmountRatio
,
totalPolicyCount
:
response
.
data
.
expectedS
tatisticsVO
.
totalPolicyCount
totalAmount
:
response
.
data
.
s
tatisticsVO
.
totalAmount
,
totalPaidAmount
:
response
.
data
.
s
tatisticsVO
.
totalPaidAmount
,
pendingPaidAmount
:
response
.
data
.
s
tatisticsVO
.
pendingPaidAmount
,
paidAmountRatio
:
response
.
data
.
s
tatisticsVO
.
paidAmountRatio
,
totalPolicyCount
:
response
.
data
.
s
tatisticsVO
.
totalPolicyCount
}
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
...
...
@@ -719,7 +746,106 @@ onMounted(async () => {
const
formatStatus
=
(
row
,
column
)
=>
{
return
getDictLabel
(
'csf_expected_commission_status'
,
row
.
status
)
// 实时查缓存
}
const
detailDialogVisible
=
ref
(
false
)
const
receivableReportTableData
=
ref
([])
const
receivableReportTableColumns
=
ref
([
{
prop
:
'policyNo'
,
label
:
'保单号'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
policyNo
||
'-'
},
{
prop
:
'reconciliationCompany'
,
label
:
'对账公司'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
reconciliationCompany
||
'-'
},
{
prop
:
'commissionPeriod'
,
label
:
'入账期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
commissionPeriod
||
'-'
},
{
prop
:
'totalPeriod'
,
label
:
'入账总期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
totalPeriod
||
'-'
},
{
prop
:
'commissionDate'
,
label
:
'入账日(估)'
,
sortable
:
true
,
width
:
'130'
,
},
{
prop
:
'commissionRatio'
,
label
:
'预估入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
commissionRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'hkdAmount'
,
label
:
'预估入账金额HKD'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
hkdAmount
||
0
)
},
{
prop
:
'paidRatio'
,
label
:
'已入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
paidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'paidAmount'
,
label
:
'已入账金额HKD'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
paidAmount
||
0
)
},
{
prop
:
'unpaidRatio'
,
label
:
'待入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
unpaidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'unpaidAmount'
,
label
:
'待入账金额HKD'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
unpaidAmount
||
0
)
},
{
prop
:
'exchangeRate'
,
label
:
'结算汇率(估)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
exchangeRate
||
0
)
},
{
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
insuranceCompany
||
'-'
},
{
prop
:
'productName'
,
label
:
'产品计划'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
productName
||
'-'
},
{
prop
:
'premium'
,
label
:
'期交保费'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
productName
||
'-'
},
{
prop
:
'policyCurrency'
,
label
:
'保单币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
policyCurrency
||
'-'
},
])
const
receivableReportItemTableColumns
=
ref
([
{
prop
:
'commissionBizType'
,
label
:
'应收单类型'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
getCommissionBizTypeLabel
(
row
.
commissionBizType
)
||
'-'
},
{
prop
:
'receivableNo'
,
label
:
'应收款编号'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
receivableNo
||
'-'
},
{
prop
:
'policyNo'
,
label
:
'保单号'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
policyNo
||
'-'
},
{
prop
:
'reconciliationCompany'
,
label
:
'对账公司'
,
sortable
:
true
,
width
:
'150'
,
formatter
:
(
row
)
=>
row
.
reconciliationCompany
||
'-'
},
{
prop
:
'status'
,
label
:
'入账状态'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatStatus
(
row
.
status
)
||
'-'
},
{
prop
:
'commissionPeriod'
,
label
:
'入账期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
commissionPeriod
||
'-'
},
{
prop
:
'totalPeriod'
,
label
:
'入账总期数'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
totalPeriod
||
'-'
},
{
prop
:
'commissionName'
,
label
:
'入账项目'
,
sortable
:
true
,
width
:
'130'
,
formatter
:
(
row
)
=>
row
.
commissionName
||
'-'
},
{
prop
:
'commissionDate'
,
label
:
'入账日(估)'
,
sortable
:
true
,
width
:
'130'
,
formatter
:
(
row
)
=>
row
.
commissionDate
||
'-'
},
{
prop
:
'commissionRatio'
,
label
:
'预估入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
commissionRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'expectedAmount'
,
label
:
'预估入账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
expectedAmount
||
0
)
},
{
prop
:
'paidRatio'
,
label
:
'已入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
paidRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'paidAmount'
,
label
:
'已入账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
paidAmount
||
0
)
},
{
prop
:
'pendingRatio'
,
label
:
'待入账比例'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
(
row
.
pendingRatio
||
0
)
+
'%'
||
'-'
},
{
prop
:
'pendingAmount'
,
label
:
'待入账金额'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
pendingAmount
||
0
)
},
{
prop
:
'defaultExchangeRate'
,
label
:
'结算汇率(估)'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
defaultExchangeRate
||
0
)
},
{
prop
:
'insuranceCompany'
,
label
:
'保险公司'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
insuranceCompany
||
'-'
},
{
prop
:
'productName'
,
label
:
'产品计划'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
productName
||
'-'
},
{
prop
:
'premium'
,
label
:
'期交保费'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
formatCurrency
(
row
.
premium
||
0
)
},
{
prop
:
'policyCurrency'
,
label
:
'保单币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
policyCurrency
||
'-'
},
{
prop
:
'currency'
,
label
:
'入账币种'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
currency
||
'-'
},
{
prop
:
'statusDesc'
,
label
:
'入账状态修改理由'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
statusDesc
||
'-'
},
{
prop
:
'remark'
,
label
:
'备注'
,
sortable
:
true
,
width
:
'120'
,
formatter
:
(
row
)
=>
row
.
remark
||
'-'
},
])
const
detailRecordStatistics
=
ref
({})
const
detailPageInfo
=
ref
({
pageNo
:
1
,
pageSize
:
10
,
currentPage
:
1
,
total
:
0
})
// 获取入账报告
const
receivedFortuneListData
=
async
()
=>
{
loading
.
value
=
true
try
{
const
params
=
{
policyNo
:
selectedDetailRecordRow
.
value
.
policyNo
,
commissionPeriod
:
selectedDetailRecordRow
.
value
.
commissionPeriod
,
receivableNo
:
selectedDetailRecordRow
.
value
.
receivableNo
,
pageNo
:
detailPageInfo
.
value
.
currentPage
,
pageSize
:
detailPageInfo
.
value
.
pageSize
}
const
response
=
await
receivedFortuneList
(
params
)
receivableReportTableData
.
value
=
response
.
data
.
page
.
records
||
[]
detailPageInfo
.
value
.
total
=
response
.
data
.
page
.
total
||
0
detailPageInfo
.
value
.
pageSize
=
response
.
data
.
page
.
size
||
50
// 统计信息
detailRecordStatistics
.
value
=
{
totalAmount
:
response
.
data
.
expectedStatisticsVO
.
totalAmount
,
totalPaidAmount
:
response
.
data
.
expectedStatisticsVO
.
totalPaidAmount
,
pendingPaidAmount
:
response
.
data
.
expectedStatisticsVO
.
pendingPaidAmount
,
paidAmountRatio
:
response
.
data
.
expectedStatisticsVO
.
paidAmountRatio
,
totalPolicyCount
:
response
.
data
.
expectedStatisticsVO
.
totalPolicyCount
}
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
ElMessage
.
error
(
'加载数据失败'
)
}
finally
{
loading
.
value
=
false
}
}
const
selectedDetailRecordRow
=
ref
({})
const
viewDetail
=
(
row
)
=>
{
selectedDetailRecordRow
.
value
=
row
detailDialogVisible
.
value
=
true
receivedFortuneListData
()
}
// 分页事件
const
handleSizeChangeDetailRecord
=
(
val
)
=>
{
detailPageInfo
.
value
.
pageSize
=
val
receivedFortuneListData
()
}
// 分页事件
const
handleCurrentChangeDetailRecord
=
(
val
)
=>
{
detailPageInfo
.
value
.
currentPage
=
val
receivedFortuneListData
()
}
</
script
>
<
style
scoped
lang=
"scss"
></
style
>
\ No newline at end of file
src/views/sign/FnaList/components/fanForm.vue
View file @
223a37bd
...
...
@@ -430,6 +430,7 @@ const handleRemoteSelectChange = async (row, column, father) => {
row
.
showSumInsured
=
false
}
row
.
insuranceType
=
item
.
label
row
.
insuranceCategoryBizId
=
item
.
code
}
})
}
...
...
@@ -750,9 +751,16 @@ const setFormValue = (obj, formData) => {
loading
.
value
=
true
let
processedData
=
JSON
.
parse
(
JSON
.
stringify
(
formData
))
// 重疾险要加上row.showSumInsured = true便于控制重疾险保额输入框的显示
// 重疾险要加上row.showSumInsured = true便于控制重疾险保额输入框的显示
for
(
const
section
of
processedData
)
{
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
]
:
[]
}
else
if
(
section
.
keyType
==
'Object'
)
{
obj
[
section
.
key
].
premiumFundingSource
=
obj
[
section
.
key
].
premiumFundingSource
...
...
src/views/sign/appointment/components/productPlan.vue
View file @
223a37bd
...
...
@@ -516,7 +516,10 @@ const searchSelectList = async (query, fieldKey) => {
projectBizId
:
userStore
.
projectInfo
.
projectBizId
,
tenantBizId
:
userStore
.
projectInfo
.
tenantBizId
,
fieldBizId
:
'field_olk1qZe81qHHKXbw'
,
fieldValueBizId
:
'field_value_yXzTigvgUdRMFpoR'
fieldValueBizId
:
fieldKey
===
'productLaunchName'
?
'field_value_yXzTigvgUdRMFpoR'
:
'field_value_uOfJH5ucA2YwJpbn'
}
const
response
=
await
secondAdditonalList
(
params
)
...
...
src/views/sign/policyReceipts/index.vue
View file @
223a37bd
...
...
@@ -17,8 +17,6 @@
</
template
>
<!-- 列表区域 -->
<
template
#
table
>
<!-- 统计信息卡片 -->
<el-table
:data=
"tableData"
@
selection-change=
"handleSelectionChange"
...
...
@@ -29,39 +27,31 @@
v-loading=
"loading"
>
<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 }">
{{
getFortuneBizTypeLabel
(
row
.
fortuneBizType
)
}}
</
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
prop=
"policyNo"
label=
"保单号"
width=
"120"
sortable
/>
<el-table-column
prop=
"insuranceCompany"
label=
"保险公司"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionPaidAmount"
label=
"累积已入账金额"
width=
"120"
sortable
/>
<el-table-column
prop=
"commissionPaidRatio"
label=
"累积已入账比例"
width=
"120"
sortable
/>
<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
>
<el-table-column
prop=
"insurer"
label=
"保险公司"
/>
<el-table-column
prop=
"policyHolder"
label=
"保单持有人"
/>
<el-table-column
prop=
"createTime"
label=
"创建时间"
>
<
template
#
default=
"{ row }"
>
{{
row
.
createTime
?
formatToDate
(
row
.
createTime
)
:
''
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"receiptDate"
label=
"回执日期"
>
<
template
#
default=
"{ row }"
>
{{
getDictLabel
(
'csf_fortune_status'
,
row
.
status
)
}}
{{
row
.
receiptDate
?
formatToDate
(
row
.
receiptDate
)
:
''
}}
</
template
>
</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"
>
<
template
#
default=
"{ row }"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
>
...
...
@@ -84,15 +74,15 @@
</el-table>
</template>
</CommonPage>
<!-- 新增
出账检核
页面-->
<!-- 新增
保单回执
页面-->
<CommonDialog
dialogTitle=
"新增出账检核
"
:dialogTitle=
"receiptsDialogTitle
"
dialogWidth=
"80%"
:openDialog=
"
addCheckRecordFormDialog
Flag"
:openDialog=
"
receipts
Flag"
:showAction=
"true"
:showClose=
"true"
@
close=
"
addCheckRecordFormDialog
Flag = false"
@
confirm=
"add
CheckRecordaddBatchapi
"
@
close=
"
receipts
Flag = false"
@
confirm=
"add
Receipts
"
>
<SearchForm
ref=
"addCheckRecordFormRef"
...
...
@@ -137,7 +127,7 @@
</template>
<
script
setup
>
import
{
ref
,
reactive
}
from
'vue'
import
{
ref
,
reactive
,
watch
}
from
'vue'
import
CommonPage
from
'@/components/commonPage'
import
CommonDialog
from
'@/components/commonDialog'
import
SearchForm
from
'@/components/SearchForm/SearchForm.vue'
...
...
@@ -145,14 +135,18 @@ import { ElMessage } from 'element-plus'
import
{
formatCurrency
}
from
'@/utils/number'
// 接口
import
{
getPolicyFortuneList
,
addCheckRecordaddBatch
,
updatePayoutAmount
,
downloadPolicyFortuneAccount
}
from
'@/api/financial/commission'
import
{
getPolicyReceiptList
,
addPolicyReceipt
,
getPolicyReceipt
,
EditPolicyReceipt
}
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
)
...
...
@@ -162,29 +156,36 @@ const loading = ref(false)
const
selectedRow
=
ref
(
null
)
const
searchFormRef
=
ref
(
null
)
const
searchParams
=
ref
({})
const
receiptsFlag
=
ref
(
false
)
//回执弹窗
const
receiptsDialogTitle
=
ref
(
'新增保单回执'
)
const
searchConfig
=
ref
([
{
type
:
'input'
,
prop
:
'appointmentNo'
,
label
:
'预约编号'
},
{
type
:
'input'
,
prop
:
'policyNo'
,
label
:
'保单号'
},
{
type
:
'select'
,
prop
:
'
statusList
'
,
label
:
'
出账
状态'
,
multiple
:
tru
e
,
dictType
:
'
csf_fortune
_status'
prop
:
'
receiptStatus
'
,
label
:
'
回执
状态'
,
multiple
:
fals
e
,
dictType
:
'
receipt
_status'
},
{
type
:
'select'
,
prop
:
'insur
anceCompanyBizIdList
'
,
prop
:
'insur
er
'
,
label
:
'保险公司'
,
api
:
'/insurance/base/api/insuranceCompany/page'
,
keywordField
:
'queryContent'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入保险公司名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
multiple
:
tru
e
,
multiple
:
fals
e
,
valueKey
:
'insuranceCompanyBizId'
,
labelKey
:
'abbreviation'
,
transform
:
res
=>
{
...
...
@@ -193,47 +194,15 @@ const searchConfig = ref([
}
},
{
type
:
'select'
,
prop
:
'productLaunchBizIdList'
,
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
:
'input'
,
prop
:
'policyHolder'
,
label
:
'保单持有人'
}
// {
// type: 'select',
// prop: 'status',
// label: '入账状态',
// multiple: true,
// dictType: 'csf_expected_commission_status'
// },
])
// 表格操作菜单
const
dropdownItems
=
[
{
label
:
'设置本期出账金额'
,
value
:
'setPayRoll'
}
// { label: '更新', value: 'editRecord' },
// { label: '新增回执', value: 'addReceipts' },
{
label
:
'更新回执'
,
value
:
'editRecord'
}
// { label: '查看记录', value: 'viewRecord' }
]
// 应收单类型
...
...
@@ -250,85 +219,106 @@ const getFortuneBizTypeLabel = value => {
const
addCheckRecordFormModel
=
ref
({})
const
addCheckRecordFormRef
=
ref
(
null
)
const
addCheckRecordFormDialogFlag
=
ref
(
false
)
const
addCheckRecordConfig
=
[
{
type
:
'select'
,
prop
:
'fortuneBizType'
,
label
:
'应付单类型'
,
options
:
fortuneBizTypeOptions
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
||
[]
},
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
{
type
:
'
selec
t'
,
prop
:
'
status
'
,
label
:
'
出账状态
'
,
di
ctType
:
'csf_expected_fortune_status'
type
:
'
inpu
t'
,
prop
:
'
insured
'
,
label
:
'
保单受保人
'
,
di
sabled
:
true
},
{
type
:
'input'
,
prop
:
'policy
No
'
,
label
:
'
关联保单号
'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
prop
:
'policy
Holder
'
,
label
:
'
保单持有人姓名
'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'fortunePeriod'
,
label
:
'佣金期数'
,
inputType
:
'decimal'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
prop
:
'insuranceCompany'
,
label
:
'保险公司名称'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'fortuneTotalPeriod'
,
label
:
'总期数'
,
inputType
:
'decimal'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
prop
:
'productName'
,
label
:
'保单产品名称'
,
disabled
:
true
},
{
type
:
'date'
,
prop
:
'actualPayoutDate'
,
label
:
'出账日(实)'
,
placeholder
:
'请选择'
,
maxDate
:
'today'
type
:
'input'
,
prop
:
'paymentTerm'
,
label
:
'缴费年限'
,
disabled
:
true
},
{
type
:
'input'
,
prop
:
'amount'
,
label
:
'出账金额'
,
inputType
:
'decimal'
,
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
prop
:
'paymentPremium'
,
label
:
'保单年缴保费'
,
disabled
:
true
},
{
type
:
'select'
,
prop
:
'currency'
,
label
:
'出账币种'
,
dictType
:
'bx_currency_type'
label
:
'保单货币'
,
dictType
:
'bx_currency_type'
,
disabled
:
true
},
{
type
:
'select'
,
prop
:
'fortuneType'
,
label
:
'出账项目'
,
dictType
:
'csf_fortune_type'
prop
:
'receiptStatus'
,
label
:
'回执状态'
,
dictType
:
'receipt_status'
,
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
{
type
:
'select'
,
prop
:
'brokerBizId'
,
label
:
'转介人'
,
api
:
'/insurance/base/api/userSaleExpand/page'
,
keywordField
:
'realName'
,
requestParams
:
{
pageNo
:
1
,
pageSize
:
20
},
placeholder
:
'输入转介人名称搜索'
,
debounceWait
:
500
,
// 自定义防抖时间
valueKey
:
'userSaleBizId'
,
labelKey
:
'realName'
,
onChangeExtraFields
:
{
broker
:
'realName'
,
// 自动同步 raw.name 到 reconciliationCompany
reconciliationCompanyCode
:
'code'
},
transform
:
res
=>
{
return
res
?.
data
.
records
||
[]
}
type
:
'date'
,
prop
:
'receiptDate'
,
label
:
'回执日期'
,
placeholder
:
'请选择'
,
maxDate
:
'today'
,
rules
:
[{
required
:
true
,
message
:
'请输入保单号码'
,
trigger
:
'blur'
}]
},
{
type
:
'upload'
,
prop
:
'policyHolderSignatureList'
,
label
:
'签名照片'
,
uploadType
:
'image'
,
multiple
:
true
,
limit
:
3
,
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
}
]
...
...
@@ -362,10 +352,28 @@ const setPayoutAmountConfig = [
const
statisticsData
=
ref
({})
// 弹窗相关
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
=
()
=>
{
addCheckRecordFormDialogFlag
.
value
=
true
console
.
log
(
addCheckRecordFormModel
.
value
)
receiptsFlag
.
value
=
true
}
const
handleExport
=
()
=>
{
...
...
@@ -375,26 +383,30 @@ 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'
,
'
import'
,
'export'
,
'
reset'
,
'query'
])
const
visibleDefaultButtons
=
ref
([
'add'
,
'reset'
,
'query'
])
// 按钮配置
const
operationBtnList
=
ref
([
{
key
:
'add'
,
direction
:
'left'
,
label
:
'新增
出账
'
,
label
:
'新增
回执
'
,
click
:
handleAdd
},
{
key
:
'export'
,
direction
:
'right'
,
click
:
handleExport
},
//
{
//
key: 'export',
//
direction: 'right',
//
click: handleExport
//
},
{
key
:
'reset'
,
direction
:
'right'
,
...
...
@@ -414,17 +426,13 @@ const loadTableData = async (searchParams = {}) => {
const
params
=
{
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
...
searchParams
,
payoutDateStart
:
searchParams
.
payoutDate
?.[
0
]
||
undefined
,
payoutDateEnd
:
searchParams
.
payoutDate
?.[
1
]
||
undefined
,
payoutDate
:
undefined
...
searchParams
}
const
res
=
await
getPolicyFortuneList
(
params
)
tableData
.
value
=
res
.
data
.
page
.
records
||
[]
pageTotal
.
value
=
res
.
data
.
page
.
total
||
0
pageSize
.
value
=
res
.
data
.
page
.
size
||
0
// 统计信息
statisticsData
.
value
=
res
.
data
.
statisticsVO
||
{}
const
res
=
await
getPolicyReceiptList
(
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
||
'加载数据失败'
)
...
...
@@ -448,27 +456,51 @@ const tableData = ref([])
const
handleSelect
=
(
e
,
row
)
=>
{
console
.
log
(
'选中行:'
,
e
,
row
)
selectedRow
.
value
=
row
if
(
e
===
'setPayRoll'
)
{
setPayoutAmountDialogFlag
.
value
=
true
if
(
e
===
'addReceipts'
)
{
receiptsFlag
.
value
=
true
receiptsDialogTitle
.
value
=
'新增保单回执'
}
else
if
(
e
==
'editRecord'
)
{
getReceiptsDetail
(
row
)
}
}
const
addCheckRecordaddBatchapi
=
async
data
=>
{
const
formData
=
addCheckRecordFormRef
.
value
.
getFormData
()
console
.
log
(
'新增出账检核记录:'
,
formData
)
const
params
=
[{
...
formData
}]
const
addReceipts
=
async
()
=>
{
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
)
{
ElMessage
.
success
(
'新增出账检核记录成功'
)
addCheckRecordFormDialogFlag
.
value
=
false
ElMessage
.
success
(
`
${
receiptsDialogTitle
.
value
}
成功`
)
receiptsFlag
.
value
=
false
// ✅ 校验 & 提交成功后才关闭
addCheckRecordFormRef
.
value
.
resetForm
()
loadTableData
()
}
else
{
ElMessage
.
error
(
res
.
msg
||
'新增出账检核记录失败'
)
ElMessage
.
error
(
res
.
msg
||
`
${
receiptsDialogTitle
.
value
}
失败`
)
}
}
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 => {
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'
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
=>
{
console
.
log
(
'提交给后端的数据:'
,
data
)
...
...
@@ -536,13 +545,19 @@ const onSubmit = data => {
// 获取入账状态,字典值转化方法
onMounted
(
async
()
=>
{
try
{
await
loadDicts
([
'
csf_fortune
_status'
])
await
loadDicts
([
'
receipt
_status'
])
}
catch
(
error
)
{
console
.
error
(
'字典加载失败'
,
error
)
}
finally
{
loading
.
value
=
false
}
})
watch
(
receiptsFlag
,
newVal
=>
{
if
(
!
newVal
)
{
addCheckRecordFormModel
.
value
=
{}
receiptsDialogTitle
.
value
=
''
}
})
</
script
>
<
style
scoped
>
...
...
src/views/sign/policyReceipts/premiumRecon.vue
0 → 100644
View file @
223a37bd
<
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