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
a0e22287
Commit
a0e22287
authored
May 13, 2026
by
yuzhenWang
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'wyz' into 'test'
发布测试 See merge request
!141
parents
1d9c426b
8e482ad9
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
164 additions
and
279 deletions
+164
-279
src/components/SearchForm/SearchForm.vue
+134
-266
src/views/financialCenter/financialBilling.vue
+14
-6
src/views/financialCenter/payables.vue
+16
-7
No files found.
src/components/SearchForm/SearchForm.vue
View file @
a0e22287
...
@@ -146,44 +146,6 @@
...
@@ -146,44 +146,6 @@
@
change=
"val => handleModelChange(val, item)"
@
change=
"val => handleModelChange(val, item)"
/>
/>
<!-- Upload 回显值得时候数据格式至少是[
{url: '必须要传', name: 'name不是必须的根据需要传值'}]-->
<!-- 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-icon
class=
"iconStyle"
:size=
"20"
v-if=
"item.uploadType === 'image'"
>
<Upload
/>
</el-icon>
<el-button
v-else
size=
"small"
type=
"primary"
:link=
"item.link"
:disabled=
"item.disabled"
>
{{
'点击上传文件'
}}
</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>
-->
<!-- Upload 回显值时数据格式至少是 [{url: '必须', name: '可选'}] -->
<template
v-else-if=
"item.type === 'upload'"
>
<template
v-else-if=
"item.type === 'upload'"
>
<!-- 🔽 默认模式:完整使用 el-upload(含自带文件列表) -->
<!-- 🔽 默认模式:完整使用 el-upload(含自带文件列表) -->
<el-upload
<el-upload
...
@@ -325,14 +287,12 @@ import useDictStore from '@/store/modules/dict'
...
@@ -325,14 +287,12 @@ import useDictStore from '@/store/modules/dict'
import
{
getDicts
}
from
'@/api/system/dict/data'
import
{
getDicts
}
from
'@/api/system/dict/data'
import
request
from
'@/utils/request'
import
request
from
'@/utils/request'
import
dayjs
from
'dayjs'
import
dayjs
from
'dayjs'
// ==================== 上传文件 ====================
// ==================== 文件预览弹窗 ====================
// ==================== 文件预览弹窗 ====================
const
previewDialogVisible
=
ref
(
false
)
const
previewDialogVisible
=
ref
(
false
)
const
previewUrl
=
ref
(
''
)
const
previewUrl
=
ref
(
''
)
const
previewFileName
=
ref
(
''
)
const
previewFileName
=
ref
(
''
)
const
previewFileType
=
ref
(
''
)
// 'image', 'pdf', 'unsupported'
const
previewFileType
=
ref
(
''
)
// 'image', 'pdf', 'unsupported'
//新增文件上传自定义方法开始
// 预览文件(支持图片和PDF)
// 预览文件(页面内弹窗,不打开新窗口)
// 预览文件(页面内弹窗,不打开新窗口)
function
previewFile
(
file
,
item
)
{
function
previewFile
(
file
,
item
)
{
...
@@ -364,7 +324,6 @@ function removeFile(file, item) {
...
@@ -364,7 +324,6 @@ function removeFile(file, item) {
const
newList
=
fileList
.
filter
(
f
=>
f
.
uid
!==
file
.
uid
)
const
newList
=
fileList
.
filter
(
f
=>
f
.
uid
!==
file
.
uid
)
handleUploadRemove
(
file
,
newList
,
item
)
// 调用原有的删除处理函数
handleUploadRemove
(
file
,
newList
,
item
)
// 调用原有的删除处理函数
}
}
//新增文件上传自定义方法结束
// 文件大小格式化
// 文件大小格式化
function
formatFileSize
(
bytes
)
{
function
formatFileSize
(
bytes
)
{
...
@@ -374,6 +333,7 @@ function formatFileSize(bytes) {
...
@@ -374,6 +333,7 @@ function formatFileSize(bytes) {
const
i
=
Math
.
floor
(
Math
.
log
(
bytes
)
/
Math
.
log
(
k
))
const
i
=
Math
.
floor
(
Math
.
log
(
bytes
)
/
Math
.
log
(
k
))
return
parseFloat
((
bytes
/
Math
.
pow
(
k
,
i
)).
toFixed
(
2
))
+
' '
+
sizes
[
i
]
return
parseFloat
((
bytes
/
Math
.
pow
(
k
,
i
)).
toFixed
(
2
))
+
' '
+
sizes
[
i
]
}
}
function
beforeUpload
(
file
,
item
)
{
function
beforeUpload
(
file
,
item
)
{
// 检查文件大小
// 检查文件大小
if
(
item
.
maxSize
&&
file
.
size
>
item
.
maxSize
)
{
if
(
item
.
maxSize
&&
file
.
size
>
item
.
maxSize
)
{
...
@@ -393,10 +353,8 @@ function beforeUpload(file, item) {
...
@@ -393,10 +353,8 @@ function beforeUpload(file, item) {
return
true
return
true
}
}
function
handleUploadSuccess
(
response
,
file
,
fileList
,
item
)
{
// 假设你的后端返回格式:{ code: 200, data: { url: '...', name: 'xxx.pdf' } }
// 你可以通过 item.responseMap 自定义映射,这里先用通用方式
function
handleUploadSuccess
(
response
,
file
,
fileList
,
item
)
{
const
data
=
response
.
data
||
response
const
data
=
response
.
data
||
response
const
url
=
data
.
url
||
data
.
fileUrl
||
data
.
path
const
url
=
data
.
url
||
data
.
fileUrl
||
data
.
path
const
name
=
data
.
name
||
data
.
fileName
||
file
.
name
const
name
=
data
.
name
||
data
.
fileName
||
file
.
name
...
@@ -416,9 +374,8 @@ function handleUploadSuccess(response, file, fileList, item) {
...
@@ -416,9 +374,8 @@ function handleUploadSuccess(response, file, fileList, item) {
// 触发 model 更新
// 触发 model 更新
handleModelChange
([...
fileList
],
item
)
handleModelChange
([...
fileList
],
item
)
ElMessage
.
success
(
`文件
${
file
.
name
}
上传成功`
)
ElMessage
.
success
(
`文件
${
file
.
name
}
上传成功`
)
// console.log('上传成功', item)
}
}
function
handleExceed
(
files
,
fileList
)
{
function
handleExceed
(
files
,
fileList
)
{
ElMessage
.
warning
(
'超出文件数量限制'
)
ElMessage
.
warning
(
'超出文件数量限制'
)
}
}
...
@@ -432,6 +389,7 @@ function handleUploadRemove(file, fileList, item) {
...
@@ -432,6 +389,7 @@ function handleUploadRemove(file, fileList, item) {
// 用户删除文件时,同步更新 model
// 用户删除文件时,同步更新 model
handleModelChange
([...
fileList
],
item
)
handleModelChange
([...
fileList
],
item
)
}
}
// ==================== 工具函数:深拷贝配置(保留函数) ====================
// ==================== 工具函数:深拷贝配置(保留函数) ====================
function
deepCloneConfig
(
obj
)
{
function
deepCloneConfig
(
obj
)
{
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
if
(
obj
===
null
||
typeof
obj
!==
'object'
)
return
obj
...
@@ -445,6 +403,7 @@ function deepCloneConfig(obj) {
...
@@ -445,6 +403,7 @@ function deepCloneConfig(obj) {
}
}
return
cloned
return
cloned
}
}
function
parseToDate
(
str
)
{
function
parseToDate
(
str
)
{
if
(
!
str
)
return
null
if
(
!
str
)
return
null
if
(
str
===
'today'
)
{
if
(
str
===
'today'
)
{
...
@@ -459,6 +418,7 @@ function parseToDate(str) {
...
@@ -459,6 +418,7 @@ function parseToDate(str) {
}
}
return
null
return
null
}
}
// ==================== 生成 disabledDate 函数 ====================
// ==================== 生成 disabledDate 函数 ====================
function
getDisabledDateFn
(
item
)
{
function
getDisabledDateFn
(
item
)
{
const
{
minDate
,
maxDate
}
=
item
const
{
minDate
,
maxDate
}
=
item
...
@@ -503,6 +463,7 @@ function getDisabledDateFn(item) {
...
@@ -503,6 +463,7 @@ function getDisabledDateFn(item) {
return
false
return
false
}
}
}
}
// ==================== Props & Emits ====================
// ==================== Props & Emits ====================
const
props
=
defineProps
({
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
modelValue
:
{
type
:
Object
,
default
:
()
=>
({})
},
...
@@ -520,9 +481,7 @@ const emit = defineEmits([
...
@@ -520,9 +481,7 @@ const emit = defineEmits([
// ==================== Refs ====================
// ==================== Refs ====================
const
formRef
=
ref
(
null
)
const
formRef
=
ref
(
null
)
// 使用 shallowRef 避免深层响应式(表单通常扁平)
const
localModel
=
ref
({
...
props
.
modelValue
})
const
localModel
=
ref
({
...
props
.
modelValue
})
// 记录哪些字段的字典已加载
const
dictLoaded
=
ref
(
new
Set
())
const
dictLoaded
=
ref
(
new
Set
())
const
internalConfig
=
ref
([])
const
internalConfig
=
ref
([])
const
remoteOptions
=
ref
({})
// { prop: [options] }
const
remoteOptions
=
ref
({})
// { prop: [options] }
...
@@ -546,7 +505,31 @@ const formRules = computed(() => {
...
@@ -546,7 +505,31 @@ const formRules = computed(() => {
return
rules
return
rules
})
})
// 1. 外部 modelValue 变化时,安全同步(仅当内容不同时)
// 同步 extra 字段:根据当前选中的 option 填充 extra 字段(不触发外部 emit)
function
syncExtraFieldsForProp
(
prop
,
value
)
{
const
item
=
internalConfig
.
value
.
find
(
i
=>
i
.
prop
===
prop
)
if
(
!
item
||
item
.
type
!==
'select'
||
!
item
.
onChangeExtraFields
)
return
false
const
options
=
getSelectOptions
(
item
)
const
selectedOption
=
options
.
find
(
opt
=>
String
(
opt
.
value
)
===
String
(
value
))
if
(
selectedOption
&&
selectedOption
.
raw
)
{
let
needUpdate
=
false
const
newModel
=
{
...
localModel
.
value
}
for
(
const
[
targetProp
,
sourceKey
]
of
Object
.
entries
(
item
.
onChangeExtraFields
))
{
const
extraValue
=
selectedOption
.
raw
[
sourceKey
]
if
(
newModel
[
targetProp
]
!==
extraValue
)
{
newModel
[
targetProp
]
=
extraValue
needUpdate
=
true
}
}
if
(
needUpdate
)
{
localModel
.
value
=
newModel
return
true
}
}
return
false
}
// 监听 config 变化(支持动态 config)
// 监听 config 变化(支持动态 config)
watch
(
watch
(
()
=>
props
.
config
,
()
=>
props
.
config
,
...
@@ -559,7 +542,6 @@ watch(
...
@@ -559,7 +542,6 @@ watch(
const
initialModel
=
{}
const
initialModel
=
{}
for
(
const
item
of
internalConfig
.
value
)
{
for
(
const
item
of
internalConfig
.
value
)
{
const
key
=
item
.
prop
const
key
=
item
.
prop
// 优先用父传值,否则用默认值
if
(
props
.
modelValue
?.[
key
]
!==
undefined
)
{
if
(
props
.
modelValue
?.[
key
]
!==
undefined
)
{
initialModel
[
key
]
=
props
.
modelValue
[
key
]
initialModel
[
key
]
=
props
.
modelValue
[
key
]
}
else
if
(
}
else
if
(
...
@@ -572,27 +554,22 @@ watch(
...
@@ -572,27 +554,22 @@ watch(
}
}
}
}
// ✅ 在这里同步 modelValue(包括 extra 字段)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
localModel
.
value
=
syncModelFromProps
(
props
.
modelValue
,
internalConfig
.
value
)
// console.log('子组件监测config变化', localModel.value)
},
},
{
immediate
:
true
}
{
immediate
:
true
}
)
)
// console.log('🚀 子组件 props.modelValue 初始值:', props.modelValue)
// 监听 modelValue(用于后续外部更新)
// 监听 modelValue(用于后续外部更新)
watch
(
watch
(
()
=>
props
.
modelValue
,
()
=>
props
.
modelValue
,
newVal
=>
{
newVal
=>
{
if
(
!
newVal
||
!
internalConfig
.
value
)
return
if
(
!
newVal
||
!
internalConfig
.
value
)
return
// ✅ 同样使用 sync 函数
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
localModel
.
value
=
syncModelFromProps
(
newVal
,
internalConfig
.
value
)
// console.log('子组件监测 modelValue 变化:', localModel.value)
},
},
{
deep
:
true
}
{
deep
:
true
}
)
)
//
提取同步逻辑
//
从 props 同步模型数据
function
syncModelFromProps
(
newModelValue
,
newConfig
)
{
function
syncModelFromProps
(
newModelValue
,
newConfig
)
{
if
(
!
newModelValue
||
!
newConfig
)
return
{}
if
(
!
newModelValue
||
!
newConfig
)
return
{}
...
@@ -614,22 +591,17 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -614,22 +591,17 @@ function syncModelFromProps(newModelValue, newConfig) {
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
const
prop
=
item
.
prop
const
prop
=
item
.
prop
const
idValue
=
newModelValue
[
prop
]
// e.g. 2
const
idValue
=
newModelValue
[
prop
]
let
sourceObj
=
null
let
sourceObj
=
null
// 情况1: 如果 newModelValue[prop] 本身就是对象 → 直接用(兼容旧逻辑)
if
(
idValue
&&
typeof
idValue
===
'object'
)
{
if
(
idValue
&&
typeof
idValue
===
'object'
)
{
sourceObj
=
idValue
sourceObj
=
idValue
}
}
else
if
(
Array
.
isArray
(
item
.
options
)
&&
idValue
!==
undefined
&&
idValue
!==
null
)
{
// 情况2: 如果是 primitive(string/number),且有 options → 反查
else
if
(
Array
.
isArray
(
item
.
options
)
&&
idValue
!==
undefined
&&
idValue
!==
null
)
{
// 默认用 option.value 匹配,可配置 valueKey
const
valueKey
=
item
.
valueKey
||
'value'
const
valueKey
=
item
.
valueKey
||
'value'
sourceObj
=
item
.
options
.
find
(
opt
=>
opt
[
valueKey
]
===
idValue
)
sourceObj
=
item
.
options
.
find
(
opt
=>
opt
[
valueKey
]
===
idValue
)
}
}
// 如果找到了 sourceObj,就提取 extra 字段
if
(
sourceObj
&&
typeof
sourceObj
===
'object'
)
{
if
(
sourceObj
&&
typeof
sourceObj
===
'object'
)
{
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
const
val
=
getNestedValue
(
sourceObj
,
subPath
)
const
val
=
getNestedValue
(
sourceObj
,
subPath
)
...
@@ -646,9 +618,7 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -646,9 +618,7 @@ function syncModelFromProps(newModelValue, newConfig) {
const
extraMap
=
item
.
onChangeExtraFields
const
extraMap
=
item
.
onChangeExtraFields
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
if
(
!
extraMap
||
typeof
extraMap
!==
'object'
)
continue
// 如果 newModelValue 中没有 sourceField,说明没有重新计算
if
(
newModelValue
[
sourceField
]
===
undefined
)
{
if
(
newModelValue
[
sourceField
]
===
undefined
)
{
// 那么保留 localModel 中对应的 extra 字段
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
for
(
const
[
targetKey
,
subPath
]
of
Object
.
entries
(
extraMap
))
{
if
(
localModel
.
value
.
hasOwnProperty
(
targetKey
))
{
if
(
localModel
.
value
.
hasOwnProperty
(
targetKey
))
{
synced
[
targetKey
]
=
localModel
.
value
[
targetKey
]
synced
[
targetKey
]
=
localModel
.
value
[
targetKey
]
...
@@ -659,79 +629,60 @@ function syncModelFromProps(newModelValue, newConfig) {
...
@@ -659,79 +629,60 @@ function syncModelFromProps(newModelValue, newConfig) {
// 4. 保留 newModelValue 中已有的 extra 字段和其他额外字段
// 4. 保留 newModelValue 中已有的 extra 字段和其他额外字段
for
(
const
key
in
newModelValue
)
{
for
(
const
key
in
newModelValue
)
{
// 如果已经同步过了(比如主字段或第2步写入的extra),跳过
if
(
synced
.
hasOwnProperty
(
key
))
continue
if
(
synced
.
hasOwnProperty
(
key
))
continue
// 判断是否是某个 extra 目标字段
const
isExtraTarget
=
newConfig
.
some
(
const
isExtraTarget
=
newConfig
.
some
(
item
=>
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
item
=>
item
.
onChangeExtraFields
&&
item
.
onChangeExtraFields
.
hasOwnProperty
(
key
)
)
)
// 如果是 extra 字段,且 newModelValue 里有值 → 保留它!
if
(
isExtraTarget
)
{
if
(
isExtraTarget
)
{
synced
[
key
]
=
newModelValue
[
key
]
synced
[
key
]
=
newModelValue
[
key
]
}
}
else
if
(
!
newConfig
.
some
(
item
=>
item
.
prop
===
key
))
{
// 如果不是 extra,也不是主字段 → 也保留(兼容 hidden 字段等)
else
if
(
!
newConfig
.
some
(
item
=>
item
.
prop
===
key
))
{
synced
[
key
]
=
newModelValue
[
key
]
synced
[
key
]
=
newModelValue
[
key
]
}
}
}
}
// console.log('🚀 子组件 进行modelvalue处理:', synced)
return
synced
return
synced
}
}
function
getNestedValue
(
obj
,
path
)
{
function
getNestedValue
(
obj
,
path
)
{
return
path
.
split
(
'.'
).
reduce
((
current
,
key
)
=>
current
?.[
key
],
obj
)
return
path
.
split
(
'.'
).
reduce
((
current
,
key
)
=>
current
?.[
key
],
obj
)
}
}
// 当字典加载完成时,触发同步
function
markDictLoaded
(
prop
)
{
function
markDictLoaded
(
prop
)
{
dictLoaded
.
value
.
add
(
prop
)
dictLoaded
.
value
.
add
(
prop
)
// 尝试同步该字段
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
if
(
props
.
modelValue
?.[
prop
]
!==
undefined
)
{
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
localModel
.
value
[
prop
]
=
props
.
modelValue
[
prop
]
}
}
}
}
// 2. 用户操作导致 localModel 变化时,emit(防抖可选)
// 用户操作导致 localModel 变化时,emit
function
handleModelChange
(
value
,
item
)
{
function
handleModelChange
(
value
,
item
)
{
console
.
group
(
'用户操作导致 localModel 变化时,emit(防抖可选)'
)
const
newModel
=
{
...
localModel
.
value
,
[
item
.
prop
]:
value
}
const
newModel
=
{
...
localModel
.
value
,
[
item
.
prop
]:
value
}
if
(
item
?.
type
===
'select'
&&
item
.
onChangeExtraFields
)
{
if
(
item
?.
type
===
'select'
&&
item
.
onChangeExtraFields
)
{
const
options
=
getSelectOptions
(
item
)
const
options
=
getSelectOptions
(
item
)
// console.log('可用 options:', options)
const
opt
=
options
.
find
(
o
=>
String
(
o
.
value
)
===
String
(
value
))
const
opt
=
options
.
find
(
o
=>
o
.
value
===
value
)
// console.log('匹配的 opt:', opt)
if
(
opt
?.
raw
)
{
if
(
opt
?.
raw
)
{
for
(
const
[
targetProp
,
sourceKey
]
of
Object
.
entries
(
item
.
onChangeExtraFields
))
{
for
(
const
[
targetProp
,
sourceKey
]
of
Object
.
entries
(
item
.
onChangeExtraFields
))
{
const
extraValue
=
opt
.
raw
[
sourceKey
]
newModel
[
targetProp
]
=
opt
.
raw
[
sourceKey
]
newModel
[
targetProp
]
=
extraValue
// console.log(`✅ 设置 ${targetProp} =`, extraValue)
}
}
}
}
}
}
localModel
.
value
=
newModel
localModel
.
value
=
newModel
// console.log('子组件用户操作后,modelvalue值==', newModel)
nextTick
(()
=>
{
nextTick
(()
=>
{
if
(
!
isEqualShallow
(
props
.
modelValue
,
newModel
))
{
if
(
!
isEqualShallow
(
props
.
modelValue
,
newModel
))
{
// console.log('如果新旧值不一样,反馈给父组件', newModel)
emit
(
'update:modelValue'
,
newModel
)
emit
(
'update:modelValue'
,
newModel
)
}
else
{
console
.
log
(
'🚫 跳过 emit:认为相等'
)
}
}
})
})
if
(
item
.
type
===
'select'
)
{
if
(
item
.
type
===
'select'
)
{
// console.log('如果是select类型,反馈给父组件', item.prop, value, item)
emit
(
'selectChange'
,
item
.
prop
,
value
,
item
)
emit
(
'selectChange'
,
item
.
prop
,
value
,
item
)
}
else
if
(
item
.
type
==
'upload'
)
{
}
else
if
(
item
.
type
==
'upload'
)
{
// 传给父组件最新的上传值newModel
emit
(
'uploadSuccess'
,
item
.
prop
,
newModel
)
emit
(
'uploadSuccess'
,
item
.
prop
,
newModel
)
}
else
if
(
item
.
type
==
'input'
)
{
}
else
if
(
item
.
type
==
'input'
)
{
emit
(
'inputChange'
,
item
.
prop
,
value
,
item
)
emit
(
'inputChange'
,
item
.
prop
,
value
,
item
)
}
}
console
.
groupEnd
()
}
}
// 辅助函数:浅比较两个对象
function
isEqualShallow
(
a
,
b
)
{
function
isEqualShallow
(
a
,
b
)
{
const
keysA
=
Object
.
keys
(
a
)
const
keysA
=
Object
.
keys
(
a
)
const
keysB
=
Object
.
keys
(
b
)
const
keysB
=
Object
.
keys
(
b
)
...
@@ -761,84 +712,34 @@ async function loadDictOptions(dictType) {
...
@@ -761,84 +712,34 @@ async function loadDictOptions(dictType) {
dictStore
.
setDict
(
dictType
,
options
)
dictStore
.
setDict
(
dictType
,
options
)
return
options
return
options
}
catch
(
err
)
{
}
catch
(
err
)
{
// console.error(`加载字典 ${dictType} 失败`, err)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
ElMessage
.
error
(
`字典
${
dictType
}
加载失败`
)
return
[]
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'
,
'monthrange'
].
includes
(
item
.
type
))
{
initialData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
initialData
[
key
]
=
item
.
defaultValue
??
''
}
}
// 预加载 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
)
})
// ==================== 获取 select 选项 ====================
// ==================== 获取 select 选项 ====================
function
getSelectOptions
(
item
)
{
function
getSelectOptions
(
item
)
{
const
key
=
item
.
prop
const
key
=
item
.
prop
// 字典选项
if
(
item
.
dictType
||
item
.
api
)
{
if
(
item
.
dictType
||
item
.
api
)
{
// 字典选项
return
(
remoteOptions
.
value
[
key
]
||
[]).
map
(
opt
=>
({
return
(
remoteOptions
.
value
[
key
]
||
[]).
map
(
opt
=>
({
value
:
opt
.
value
,
value
:
opt
.
value
,
label
:
opt
.
label
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// ← 必须存 raw
raw
:
opt
.
raw
}))
}))
}
else
if
(
item
.
options
)
{
}
else
if
(
item
.
options
)
{
// 静态选项s
return
item
.
options
.
map
(
opt
=>
({
return
item
.
options
.
map
(
opt
=>
({
value
:
opt
.
value
,
value
:
opt
.
value
,
label
:
opt
.
label
,
label
:
opt
.
label
,
raw
:
opt
.
raw
// 保留原始
raw
:
opt
.
raw
}))
}))
}
}
return
[]
return
[]
}
}
// 初始化加载远程 API 选项(无搜索词)
async
function
loadRemoteOptionsForInit
(
item
)
{
async
function
loadRemoteOptionsForInit
(
item
)
{
const
{
prop
,
api
,
requestParams
}
=
item
const
{
prop
,
api
,
requestParams
}
=
item
try
{
try
{
// 构造请求体:只传 requestParams,不传 keyword
const
payload
=
{
const
payload
=
{
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{})
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{})
}
}
...
@@ -854,38 +755,45 @@ async function loadRemoteOptionsForInit(item) {
...
@@ -854,38 +755,45 @@ async function loadRemoteOptionsForInit(item) {
?
item
.
transform
(
res
)
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
newOptions
=
list
.
map
(
i
=>
({
const
newOptions
=
list
.
map
(
i
=>
({
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
label
:
i
[
item
.
labelKey
||
'label'
],
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
raw
:
i
}))
}))
remoteOptions
.
value
[
prop
]
=
newOptions
remoteOptions
.
value
[
prop
]
=
newOptions
markDictLoaded
(
prop
)
// ← 关键:标记已加载
markDictLoaded
(
prop
)
// 同步 extra 字段
const
currentVal
=
localModel
.
value
[
prop
]
if
(
currentVal
!==
undefined
&&
currentVal
!==
null
&&
currentVal
!==
''
)
{
syncExtraFieldsForProp
(
prop
,
currentVal
)
}
}
catch
(
err
)
{
}
catch
(
err
)
{
ElMessage
.
error
(
`预加载
${
item
.
label
}
失败`
)
ElMessage
.
error
(
`预加载
${
item
.
label
}
失败`
)
remoteOptions
.
value
[
prop
]
=
[]
remoteOptions
.
value
[
prop
]
=
[]
}
}
}
}
// ==================== 加载远程 API 选项(首次 focus 时加载,无搜索词) ====================
// 加载远程 API 选项(focus 时调用,无搜索词)
async
function
loadRemoteOptions
(
item
)
{
async
function
loadRemoteOptions
(
item
)
{
const
{
prop
,
api
,
requestParams
,
keywordField
,
debounceWait
,
...
rest
}
=
item
const
{
prop
,
api
,
requestParams
}
=
item
if
(
!
api
)
return
if
(
!
api
)
return
// 如果已经有选项且不是强制刷新,可跳过;但为了保证初次加载,remoteOptions[prop] 为空时才加载
if
(
remoteOptions
.
value
[
prop
]
&&
remoteOptions
.
value
[
prop
].
length
>
0
)
return
try
{
try
{
remoteLoading
.
value
[
prop
]
=
true
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:合并 requestParams + 分页(默认第一页)
const
payload
=
{
const
payload
=
{
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{})
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{})
// 注意:此时无 keyword,所以不加 keywordField
}
}
const
res
=
await
request
({
const
res
=
await
request
({
url
:
api
,
url
:
api
,
method
:
'post'
,
// ← 改为 POST
method
:
'post'
,
data
:
payload
// ← 参数放 body
data
:
payload
})
})
const
list
=
const
list
=
...
@@ -893,15 +801,19 @@ async function loadRemoteOptions(item) {
...
@@ -893,15 +801,19 @@ async function loadRemoteOptions(item) {
?
item
.
transform
(
res
)
?
item
.
transform
(
res
)
:
res
.
data
?.
records
||
res
.
data
||
[]
:
res
.
data
?.
records
||
res
.
data
||
[]
// 建议:统一转成字符串(或根据 item.valueType 判断)
const
newOptions
=
list
.
map
(
i
=>
({
const
newOptions
=
list
.
map
(
i
=>
({
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
// ← 强制转字符串
value
:
String
(
i
[
item
.
valueKey
||
'value'
]),
label
:
i
[
item
.
labelKey
||
'label'
],
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
raw
:
i
}))
}))
remoteOptions
.
value
[
prop
]
=
newOptions
remoteOptions
.
value
[
prop
]
=
newOptions
// ✅ 关键:标记该字段字典已加载
markDictLoaded
(
prop
)
markDictLoaded
(
prop
)
// 同步 extra 字段
const
currentVal
=
localModel
.
value
[
prop
]
if
(
currentVal
!==
undefined
&&
currentVal
!==
null
&&
currentVal
!==
''
)
{
syncExtraFieldsForProp
(
prop
,
currentVal
)
}
}
catch
(
err
)
{
}
catch
(
err
)
{
ElMessage
.
error
(
`加载
${
item
.
label
}
失败`
)
ElMessage
.
error
(
`加载
${
item
.
label
}
失败`
)
remoteOptions
.
value
[
prop
]
=
[]
remoteOptions
.
value
[
prop
]
=
[]
...
@@ -910,7 +822,7 @@ async function loadRemoteOptions(item) {
...
@@ -910,7 +822,7 @@ async function loadRemoteOptions(item) {
}
}
}
}
//
==================== 远程搜索(带关键词,防抖) ====================
//
远程搜索(带关键词,防抖)
let
searchTimeout
=
null
let
searchTimeout
=
null
function
handleFilterChange
(
keyword
,
item
)
{
function
handleFilterChange
(
keyword
,
item
)
{
const
{
prop
,
api
,
requestParams
,
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
const
{
prop
,
api
,
requestParams
,
keywordField
=
'keyword'
,
debounceWait
=
300
}
=
item
...
@@ -921,16 +833,15 @@ function handleFilterChange(keyword, item) {
...
@@ -921,16 +833,15 @@ function handleFilterChange(keyword, item) {
try
{
try
{
remoteLoading
.
value
[
prop
]
=
true
remoteLoading
.
value
[
prop
]
=
true
// 构造请求体:requestParams + 分页 + 动态关键词字段
const
payload
=
{
const
payload
=
{
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{}),
...(
typeof
requestParams
===
'function'
?
requestParams
()
:
requestParams
||
{}),
[
keywordField
]:
keyword
// ← 动态字段名,如 name / companyName
[
keywordField
]:
keyword
}
}
const
res
=
await
request
({
const
res
=
await
request
({
url
:
api
,
url
:
api
,
method
:
'post'
,
// ← POST 请求
method
:
'post'
,
data
:
payload
// ← body 传参
data
:
payload
})
})
const
list
=
const
list
=
...
@@ -939,9 +850,9 @@ function handleFilterChange(keyword, item) {
...
@@ -939,9 +850,9 @@ function handleFilterChange(keyword, item) {
:
res
.
data
?.
records
||
res
.
data
||
[]
:
res
.
data
?.
records
||
res
.
data
||
[]
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
remoteOptions
.
value
[
prop
]
=
list
.
map
(
i
=>
({
value
:
i
[
item
.
valueKey
||
'value'
]
,
value
:
String
(
i
[
item
.
valueKey
||
'value'
])
,
label
:
i
[
item
.
labelKey
||
'label'
],
label
:
i
[
item
.
labelKey
||
'label'
],
raw
:
i
// ← 保存完整对象
raw
:
i
}))
}))
}
catch
(
err
)
{
}
catch
(
err
)
{
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
ElMessage
.
error
(
`搜索
${
item
.
label
}
失败`
)
...
@@ -952,46 +863,6 @@ function handleFilterChange(keyword, item) {
...
@@ -952,46 +863,6 @@ function handleFilterChange(keyword, item) {
}
}
// ==================== 数字输入处理 ====================
// ==================== 数字输入处理 ====================
// function handleNumberInput(value, item) {
// const { inputType = 'text', decimalDigits = 2, prop } = item
// if (!prop) return
// let result = String(value ?? '').trim()
// if (inputType === 'integer') {
// // 只保留数字
// result = result.replace(/[^\d]/g, '')
// } else if (inputType === 'decimal') {
// // 1. 只保留数字和小数点
// result = result.replace(/[^\d.]/g, '')
// // 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
// if (result.startsWith('.')) {
// result = '0.' + result.slice(1)
// }
// // 3. 保证最多一个小数点
// const parts = result.split('.')
// if (parts.length > 2) {
// result = parts[0] + '.' + parts.slice(1).join('')
// }
// // 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 }
// }
// }
function
handleNumberInput
(
value
,
item
)
{
function
handleNumberInput
(
value
,
item
)
{
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
const
{
inputType
=
'text'
,
decimalDigits
=
2
,
prop
}
=
item
if
(
!
prop
)
return
if
(
!
prop
)
return
...
@@ -999,83 +870,109 @@ function handleNumberInput(value, item) {
...
@@ -999,83 +870,109 @@ function handleNumberInput(value, item) {
let
result
=
String
(
value
??
''
).
trim
()
let
result
=
String
(
value
??
''
).
trim
()
if
(
inputType
===
'integer'
)
{
if
(
inputType
===
'integer'
)
{
// 只保留数字和负号
result
=
result
.
replace
(
/
[^
-
\d]
/g
,
''
)
result
=
result
.
replace
(
/
[^
-
\d]
/g
,
''
)
// 如果有多个负号或者负号不在开头,则移除多余的负号
if
((
result
.
match
(
/-/g
)
||
[]).
length
>
1
)
{
if
((
result
.
match
(
/-/g
)
||
[]).
length
>
1
)
{
result
=
result
.
replace
(
/-/g
,
''
).
replace
(
/^/
,
'-'
)
// 仅保留一个负号在最前面
result
=
result
.
replace
(
/-/g
,
''
).
replace
(
/^/
,
'-'
)
}
}
}
else
if
(
inputType
===
'decimalNumber'
)
{
}
else
if
(
inputType
===
'decimalNumber'
)
{
// 可以输入正数,负数,小数
// 1. 只保留数字、小数点和负号
result
=
result
.
replace
(
/
[^
-
\d
.
]
/g
,
''
)
result
=
result
.
replace
(
/
[^
-
\d
.
]
/g
,
''
)
// 2. 处理负号:确保最多只有一个负号且必须在开头
if
((
result
.
match
(
/-/g
)
||
[]).
length
>
1
)
{
if
((
result
.
match
(
/-/g
)
||
[]).
length
>
1
)
{
result
=
result
.
replace
(
/-/g
,
''
)
// 移除所有负号
result
=
result
.
replace
(
/-/g
,
''
)
if
(
result
.
startsWith
(
'-'
))
{
if
(
result
.
startsWith
(
'-'
))
{
result
=
'-'
+
result
.
slice
(
1
)
// 确保负号在最前面
result
=
'-'
+
result
.
slice
(
1
)
}
else
{
}
else
{
result
=
'-'
+
result
// 如果原本没有负号但需要保留数值,可以省略此步骤
result
=
'-'
+
result
}
}
}
}
// 3. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好)
if
(
result
.
startsWith
(
'.'
))
{
if
(
result
.
startsWith
(
'.'
))
{
result
=
'0'
+
result
result
=
'0'
+
result
}
else
if
(
result
.
startsWith
(
'-.'
))
{
}
else
if
(
result
.
startsWith
(
'-.'
))
{
result
=
'-0'
+
result
.
slice
(
2
)
result
=
'-0'
+
result
.
slice
(
2
)
}
}
// 4. 保证最多一个小数点
const
parts
=
result
.
split
(
'.'
)
const
parts
=
result
.
split
(
'.'
)
if
(
parts
.
length
>
2
)
{
if
(
parts
.
length
>
2
)
{
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
}
}
// 5. 限制小数位数(但保留结尾的小数点!)
if
(
result
.
includes
(
'.'
))
{
if
(
result
.
includes
(
'.'
))
{
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
// 如果小数部分超过限制,截断
if
(
decPart
.
length
>
decimalDigits
)
{
if
(
decPart
.
length
>
decimalDigits
)
{
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
}
}
// ✅ 不再删除结尾的 '.'
}
}
}
else
if
(
inputType
===
'decimal'
)
{
}
else
if
(
inputType
===
'decimal'
)
{
// 可以输入正整数和小数
// 1. 只保留数字和小数点
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
result
=
result
.
replace
(
/
[^\d
.
]
/g
,
''
)
// 2. 去掉开头的小数点(不允许 ".5" → 改为 "0.5" 更好,但这里先简单处理)
if
(
result
.
startsWith
(
'.'
))
{
if
(
result
.
startsWith
(
'.'
))
{
result
=
'0.'
+
result
.
slice
(
1
)
result
=
'0.'
+
result
.
slice
(
1
)
}
}
// 3. 保证最多一个小数点
const
parts
=
result
.
split
(
'.'
)
const
parts
=
result
.
split
(
'.'
)
if
(
parts
.
length
>
2
)
{
if
(
parts
.
length
>
2
)
{
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
result
=
parts
[
0
]
+
'.'
+
parts
.
slice
(
1
).
join
(
''
)
}
}
// 4. 限制小数位数(但保留结尾的小数点!)
if
(
result
.
includes
(
'.'
))
{
if
(
result
.
includes
(
'.'
))
{
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
const
[
intPart
,
decPart
]
=
result
.
split
(
'.'
)
// 如果小数部分超过限制,截断
if
(
decPart
.
length
>
decimalDigits
)
{
if
(
decPart
.
length
>
decimalDigits
)
{
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
result
=
intPart
+
'.'
+
decPart
.
slice
(
0
,
decimalDigits
)
}
}
// ✅ 不再删除结尾的 '.'
}
}
}
}
// 防止重复赋值(可选优化)
if
(
localModel
.
value
[
prop
]
!==
result
)
{
if
(
localModel
.
value
[
prop
]
!==
result
)
{
localModel
.
value
=
{
...
localModel
.
value
,
[
prop
]:
result
}
localModel
.
value
=
{
...
localModel
.
value
,
[
prop
]:
result
}
}
}
}
}
// ==================== 初始化 ====================
onMounted
(
async
()
=>
{
internalConfig
.
value
=
deepCloneConfig
(
props
.
config
)
const
initialData
=
{}
const
dictTypePromises
=
[]
const
apiPromises
=
[]
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'
,
'monthrange'
].
includes
(
item
.
type
))
{
initialData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
initialData
[
key
]
=
item
.
defaultValue
??
''
}
}
if
(
item
.
type
===
'select'
&&
item
.
dictType
)
{
dictTypePromises
.
push
(
loadDictOptions
(
item
.
dictType
).
then
(
opts
=>
{
remoteOptions
.
value
[
key
]
=
opts
markDictLoaded
(
key
)
})
)
}
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
)
}
}
if
(
Object
.
keys
(
initialData
).
length
>
0
)
{
localModel
.
value
=
{
...
localModel
.
value
,
...
initialData
}
}
// 等待所有字典和远程选项加载完成
await
Promise
.
allSettled
([...
dictTypePromises
,
...
apiPromises
])
// 所有 select 选项加载完成后,为有 onChangeExtraFields 且有值的字段填充 extra 字段
for
(
const
item
of
internalConfig
.
value
)
{
if
(
item
.
type
===
'select'
&&
item
.
onChangeExtraFields
)
{
const
currentVal
=
localModel
.
value
[
item
.
prop
]
if
(
currentVal
!==
undefined
&&
currentVal
!==
null
&&
currentVal
!==
''
)
{
syncExtraFieldsForProp
(
item
.
prop
,
currentVal
)
}
}
}
})
// ==================== 暴露方法 ====================
// ==================== 暴露方法 ====================
defineExpose
({
defineExpose
({
getFormData
()
{
getFormData
()
{
...
@@ -1096,7 +993,7 @@ defineExpose({
...
@@ -1096,7 +993,7 @@ defineExpose({
if
([
'checkbox-group'
,
'daterange'
,
'monthrange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
if
([
'checkbox-group'
,
'daterange'
,
'monthrange'
].
includes
(
item
.
type
)
||
item
.
multiple
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
resetData
[
key
]
=
item
.
defaultValue
??
[]
}
else
if
(
item
.
type
===
'upload'
)
{
}
else
if
(
item
.
type
===
'upload'
)
{
resetData
[
key
]
=
item
.
defaultValue
??
[]
// upload 也是数组
resetData
[
key
]
=
item
.
defaultValue
??
[]
}
else
{
}
else
{
resetData
[
key
]
=
item
.
defaultValue
??
''
resetData
[
key
]
=
item
.
defaultValue
??
''
}
}
...
@@ -1104,36 +1001,20 @@ defineExpose({
...
@@ -1104,36 +1001,20 @@ defineExpose({
localModel
.
value
=
{
...
resetData
}
localModel
.
value
=
{
...
resetData
}
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
nextTick
(()
=>
formRef
.
value
?.
clearValidate
())
},
},
// ✅ 新增:强制刷新某个字段的远程选项
async
refreshRemoteOptions
(
targetProp
)
{
async
refreshRemoteOptions
(
targetProp
)
{
console
.
log
(
`[SearchForm] 收到刷新请求:
${
targetProp
}
`
)
// 1. 查找配置项
const
item
=
internalConfig
.
value
.
find
(
i
=>
i
.
prop
===
targetProp
)
const
item
=
internalConfig
.
value
.
find
(
i
=>
i
.
prop
===
targetProp
)
if
(
!
item
)
{
if
(
!
item
)
{
console
.
warn
(
`[SearchForm] 未找到 prop 为
${
targetProp
}
的配置项`
)
console
.
warn
(
`[SearchForm] 未找到 prop 为
${
targetProp
}
的配置项`
)
return
return
}
}
if
(
item
.
type
!==
'select'
||
!
item
.
api
)
{
if
(
item
.
type
!==
'select'
||
!
item
.
api
)
{
console
.
warn
(
`[SearchForm] 字段
${
targetProp
}
不是远程 Select 或没有 API`
)
console
.
warn
(
`[SearchForm] 字段
${
targetProp
}
不是远程 Select 或没有 API`
)
return
return
}
}
console
.
log
(
`[SearchForm] 开始强制加载
${
targetProp
}
的数据,API:
${
item
.
api
}
`
)
// 2. 关键:在调用前,先清空旧数据,防止子组件内部的 "已加载则跳过" 逻辑生效
// 如果你的 loadRemoteOptions 里有 "if (remoteOptions.value[prop]?.length > 0) return"
// 这里必须先清空
remoteOptions
.
value
[
targetProp
]
=
[]
remoteOptions
.
value
[
targetProp
]
=
[]
remoteLoading
.
value
[
targetProp
]
=
true
// 手动开启 loading
remoteLoading
.
value
[
targetProp
]
=
true
try
{
try
{
// 3. 调用内部加载函数
// 注意:直接调用 loadRemoteOptions,它会读取最新的 requestParams
await
loadRemoteOptions
(
item
)
await
loadRemoteOptions
(
item
)
console
.
log
(
`[SearchForm]
${
targetProp
}
加载完成`
)
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
`[SearchForm]
${
targetProp
}
加载失败`
,
error
)
console
.
error
(
`[SearchForm]
${
targetProp
}
加载失败`
,
error
)
throw
error
throw
error
...
@@ -1145,34 +1026,28 @@ defineExpose({
...
@@ -1145,34 +1026,28 @@ defineExpose({
</
script
>
</
script
>
<
style
scoped
>
<
style
scoped
>
/* 预览弹窗样式 */
.preview-container
{
.preview-container
{
display
:
flex
;
display
:
flex
;
justify-content
:
center
;
justify-content
:
center
;
align-items
:
center
;
align-items
:
center
;
min-height
:
400px
;
min-height
:
400px
;
}
}
.preview-image-wrapper
{
.preview-image-wrapper
{
text-align
:
center
;
text-align
:
center
;
}
}
.preview-image
{
.preview-image
{
max-width
:
100%
;
max-width
:
100%
;
max-height
:
70vh
;
max-height
:
70vh
;
object-fit
:
contain
;
object-fit
:
contain
;
}
}
.preview-pdf
{
.preview-pdf
{
width
:
100%
;
width
:
100%
;
height
:
70vh
;
height
:
70vh
;
}
}
.preview-unsupported
{
.preview-unsupported
{
text-align
:
center
;
text-align
:
center
;
padding
:
40px
;
padding
:
40px
;
}
}
.preview-unsupported
p
{
.preview-unsupported
p
{
margin
:
16px
0
;
margin
:
16px
0
;
color
:
#909399
;
color
:
#909399
;
...
@@ -1180,14 +1055,12 @@ defineExpose({
...
@@ -1180,14 +1055,12 @@ defineExpose({
.custom-upload-wrapper
{
.custom-upload-wrapper
{
width
:
100%
;
width
:
100%
;
}
}
.custom-file-list
{
.custom-file-list
{
margin-top
:
12px
;
margin-top
:
12px
;
border
:
1px
solid
#dcdfe6
;
border
:
1px
solid
#dcdfe6
;
border-radius
:
4px
;
border-radius
:
4px
;
background-color
:
#fff
;
background-color
:
#fff
;
}
}
.file-item
{
.file-item
{
display
:
flex
;
display
:
flex
;
justify-content
:
space-between
;
justify-content
:
space-between
;
...
@@ -1195,11 +1068,9 @@ defineExpose({
...
@@ -1195,11 +1068,9 @@ defineExpose({
padding
:
8px
12px
;
padding
:
8px
12px
;
border-bottom
:
1px
solid
#ebeef5
;
border-bottom
:
1px
solid
#ebeef5
;
}
}
.file-item
:last-child
{
.file-item
:last-child
{
border-bottom
:
none
;
border-bottom
:
none
;
}
}
.file-name
{
.file-name
{
flex
:
1
;
flex
:
1
;
font-size
:
14px
;
font-size
:
14px
;
...
@@ -1209,7 +1080,6 @@ defineExpose({
...
@@ -1209,7 +1080,6 @@ defineExpose({
white-space
:
nowrap
;
white-space
:
nowrap
;
margin-right
:
16px
;
margin-right
:
16px
;
}
}
.file-actions
{
.file-actions
{
display
:
flex
;
display
:
flex
;
gap
:
12px
;
gap
:
12px
;
...
@@ -1217,11 +1087,9 @@ defineExpose({
...
@@ -1217,11 +1087,9 @@ defineExpose({
.formBox
{
.formBox
{
box-sizing
:
border-box
;
box-sizing
:
border-box
;
}
}
.search-form-item
{
.search-form-item
{
margin-bottom
:
20px
;
margin-bottom
:
20px
;
}
}
.iconStyle
{
.iconStyle
{
color
:
#409eff
;
color
:
#409eff
;
}
}
...
...
src/views/financialCenter/financialBilling.vue
View file @
a0e22287
...
@@ -434,9 +434,9 @@ const searchConfig = ref([
...
@@ -434,9 +434,9 @@ const searchConfig = ref([
}
}
},
},
{
{
type
:
'
date
range'
,
type
:
'
month
range'
,
prop
:
'payoutDate'
,
prop
:
'payoutDate'
,
label
:
'出账
日
(估)'
,
label
:
'出账
月
(估)'
,
startPlaceholder
:
'开始时间'
,
startPlaceholder
:
'开始时间'
,
endPlaceholder
:
'结束时间'
endPlaceholder
:
'结束时间'
},
},
...
@@ -712,7 +712,7 @@ const confirmRateExchange = async () => {
...
@@ -712,7 +712,7 @@ const confirmRateExchange = async () => {
rateExchangeFlag
.
value
=
false
rateExchangeFlag
.
value
=
false
loadTableData
()
loadTableData
()
}
catch
(
error
)
{
}
catch
(
error
)
{
ElMessage
.
success
(
'结算汇率修改失败'
)
ElMessage
.
error
(
'结算汇率修改失败'
)
rateExchangeFlag
.
value
=
true
rateExchangeFlag
.
value
=
true
if
(
error
.
message
&&
error
.
message
.
includes
(
'Validation'
))
{
if
(
error
.
message
&&
error
.
message
.
includes
(
'Validation'
))
{
ElMessage
.
error
(
'必填项不能为空'
)
ElMessage
.
error
(
'必填项不能为空'
)
...
@@ -1099,12 +1099,20 @@ const addCheckRecordConfig = [
...
@@ -1099,12 +1099,20 @@ const addCheckRecordConfig = [
},
},
{
{
type
:
'
date
'
,
type
:
'
month
'
,
prop
:
'payoutDate'
,
prop
:
'payoutDate'
,
label
:
'出账日期'
,
label
:
'出账月(估)'
,
placeholder
:
'请选择'
,
maxDate
:
'today'
,
rules
:
[{
required
:
true
,
message
:
'出账月(估)必填'
,
trigger
:
'blur'
}]
},
{
type
:
'month'
,
prop
:
'actualPayoutDate'
,
label
:
'出账月(实)'
,
placeholder
:
'请选择'
,
placeholder
:
'请选择'
,
maxDate
:
'today'
,
maxDate
:
'today'
,
rules
:
[{
required
:
true
,
message
:
'出账
日期
必填'
,
trigger
:
'blur'
}]
rules
:
[{
required
:
true
,
message
:
'出账
月(实)
必填'
,
trigger
:
'blur'
}]
},
},
// {
// {
// type: 'input',
// type: 'input',
...
...
src/views/financialCenter/payables.vue
View file @
a0e22287
...
@@ -156,7 +156,7 @@
...
@@ -156,7 +156,7 @@
/>
/>
<el-table-column
fixed=
"right"
label=
"操作"
min-width=
"120"
>
<el-table-column
fixed=
"right"
label=
"操作"
min-width=
"120"
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
>
<el-popover
placement=
"right"
:width=
"200"
trigger=
"click"
v-if=
"row.type == '1'"
>
<template
#
reference
>
<template
#
reference
>
<el-icon>
<el-icon>
<MoreFilled
/>
<MoreFilled
/>
...
@@ -797,16 +797,16 @@ const updatePayRecordFormConfig = [
...
@@ -797,16 +797,16 @@ const updatePayRecordFormConfig = [
prop
:
'fortunePeriod'
,
prop
:
'fortunePeriod'
,
label
:
'佣金期数'
,
label
:
'佣金期数'
,
inputType
:
'decimal'
,
inputType
:
'decimal'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
//
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
},
{
{
type
:
'input'
,
type
:
'input'
,
prop
:
'fortuneTotalPeriod'
,
prop
:
'fortuneTotalPeriod'
,
label
:
'总期数'
,
label
:
'总期数'
,
inputType
:
'decimal'
,
inputType
:
'decimal'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
,
visible
:
formData
=>
formData
.
fortuneBizType
===
'R'
rules
:
[{
pattern
:
/^
\d
+$/
,
message
:
'只能输入正整数'
,
trigger
:
'blur'
}]
//
rules: [{ pattern: /^\d+$/, message: '只能输入正整数', trigger: 'blur' }]
},
},
{
{
type
:
'select'
,
type
:
'select'
,
...
@@ -1333,7 +1333,13 @@ const handleConfirmAddPayRecord = async () => {
...
@@ -1333,7 +1333,13 @@ const handleConfirmAddPayRecord = async () => {
const
handleConfirmUpdatePayRecord
=
async
()
=>
{
const
handleConfirmUpdatePayRecord
=
async
()
=>
{
if
(
selectedRow
.
value
.
type
==
'1'
)
{
if
(
selectedRow
.
value
.
type
==
'1'
)
{
try
{
try
{
const
formData
=
updatePayRecordFormRef
.
value
.
getFormData
()
const
formData
=
await
updatePayRecordFormRef
.
value
.
validate
()
console
.
log
(
'===================================='
)
console
.
log
(
'formData'
,
formData
)
console
.
log
(
'===================================='
)
if
(
!
formData
)
{
return
}
const
params
=
{
const
params
=
{
...
formData
,
...
formData
,
expectedFortuneBizId
:
selectedRow
.
value
.
expectedFortuneBizId
expectedFortuneBizId
:
selectedRow
.
value
.
expectedFortuneBizId
...
@@ -1346,7 +1352,10 @@ const handleConfirmUpdatePayRecord = async () => {
...
@@ -1346,7 +1352,10 @@ const handleConfirmUpdatePayRecord = async () => {
loadPayRecordTableData
(
selectedRow
.
value
.
expectedFortuneBizId
)
loadPayRecordTableData
(
selectedRow
.
value
.
expectedFortuneBizId
)
expectedFortuneListData
()
expectedFortuneListData
()
}
catch
(
error
)
{
}
catch
(
error
)
{
ElMessage
.
error
(
error
.
message
)
if
(
error
.
message
&&
error
.
message
.
includes
(
'Validation'
))
{
ElMessage
.
error
(
'必填项不能为空'
)
}
ElMessage
.
error
(
'更新失败'
)
}
}
}
}
}
}
...
...
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