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
8e482ad9
Commit
8e482ad9
authored
May 13, 2026
by
yuzhenWang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
发布测试
parent
feb487f6
Show 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 @
8e482ad9
...
@@ -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 @
8e482ad9
...
@@ -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 @
8e482ad9
...
@@ -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