Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
zrch-risk-39
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Administrator
zrch-risk-39
Commits
c9689b4d
提交
c9689b4d
authored
2月 04, 2026
作者:
kxjia
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Tb8.vue 校验
上级
ecf267ec
显示空白字符变更
内嵌
并排
正在显示
1 个修改的文件
包含
680 行增加
和
234 行删除
+680
-234
Tb8.vue
...isk-client-39/src/views/baosong/report/components/Tb8.vue
+680
-234
没有找到文件。
zrch-risk-client-39/src/views/baosong/report/components/Tb8.vue
浏览文件 @
c9689b4d
<
template
>
<div
class=
"bank-report-table"
@
click=
"close
Tooltip
"
>
<div
class=
"bank-report-table"
@
click=
"close
AllTooltips
"
>
<!-- 加载遮罩层 -->
<div
v-if=
"loading"
class=
"loading-overlay"
>
<div
class=
"loading-spinner"
>
...
...
@@ -78,7 +78,7 @@
<vxe-column
field=
"serialNumber"
title=
"序号"
width=
"80"
></vxe-column>
<vxe-column
field=
"project"
title=
"项目"
width=
"200"
>
<
template
#
default=
"{ row }"
>
<span
style=
"font-weight: bold;
size:20
px"
>
{{
row
.
project
}}
</span>
<span
style=
"font-weight: bold;
font-size: 14
px"
>
{{
row
.
project
}}
</span>
</
template
>
</vxe-column>
<vxe-column
field=
"content"
title=
"内容"
row-resize
>
...
...
@@ -89,29 +89,27 @@
<span
v-else-if=
"item.type === 'br'"
><br></span>
<span
v-else-if=
"item.type === 'brspace'"
>
<br>
<template
v-for=
"i in item.value||1
"
:key=
"i"
>
<template
v-for=
"i in (item.value || 1)
"
:key=
"i"
>
</
template
>
</span>
<
template
v-else-if=
"item.type === 'yesno'"
>
<!-- 是/否单选组 -->
<vxe-radio-group
v-model=
"formData[row.code +'_'+ item.field]"
>
<
template
v-else-if=
"item.type === 'yesno'"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
>
<vxe-radio
label=
"是"
content=
"是"
></vxe-radio>
<vxe-radio
label=
"否"
content=
"否"
></vxe-radio>
</vxe-radio-group>
<!-- 额外字段区域 -->
<template
v-if=
"item.extraField || item.extraFields"
>
<div
v-if=
"formData[row.code +'_'+ item.field] === '是'"
class=
"extra-fields-container"
>
<div
v-if=
"formData[getFieldKey(row.code, item.field)] === '是'"
class=
"extra-fields-container"
>
<!-- 单个额外字段的情况 -->
<template
v-if=
"item.extraField"
>
<span
class=
"extra-fields-prompt"
>
如是,
</span>
<span
class=
"extra-label"
>
{{
item
.
extraLabel
}}
:
</span>
<vxe-input
v-model=
"formData[row.code + '_'+ item.extraField
]"
v-model=
"formData[getFieldKey(row.code, item.extraField)
]"
style=
"width: 300px"
></vxe-input>
<span
v-if=
"item.unit"
class=
"unit"
>
{{
item
.
unit
}}
</span>
...
...
@@ -126,14 +124,14 @@
<span
class=
"field-label"
>
{{
fieldOpt
.
label
}}
:
</span>
<template
v-if=
"fieldOpt.formType === 'input'"
>
<vxe-input
v-model=
"formData[row.code + '_'+ fieldOpt.field
]"
v-model=
"formData[getFieldKey(row.code, fieldOpt.field)
]"
style=
"width: 250px"
></vxe-input>
</
template
>
<
template
v-else-if=
"fieldOpt.formType === 'number'"
>
<vxe-input
v-model=
"formData[row.code + '_'+ fieldOpt.field
]"
v-model=
"formData[getFieldKey(row.code, fieldOpt.field)
]"
type=
"number"
style=
"width: 250px"
></vxe-input>
...
...
@@ -141,7 +139,7 @@
</
template
>
<
template
v-else-if=
"fieldOpt.formType === 'checkbox'"
>
<vxe-checkbox-group
v-model=
"formData[row.code + '_'+ fieldOpt.field
]"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, fieldOpt.field)
]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in fieldOpt.options"
:key=
"optIndex"
...
...
@@ -152,7 +150,7 @@
</
template
>
<
template
v-else-if=
"fieldOpt.formType === 'radio'"
>
<vxe-radio-group
v-model=
"formData[row.code + '_'+ fieldOpt.field
]"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, fieldOpt.field)
]"
>
<vxe-radio
v-for=
"(opt, optIndex) in fieldOpt.options"
:key=
"optIndex"
...
...
@@ -164,7 +162,7 @@
<
template
v-else
>
<vxe-input
v-model=
"formData[row.code + '_'+ fieldOpt.field
]"
v-model=
"formData[getFieldKey(row.code, fieldOpt.field)
]"
:type=
"fieldOpt.formType"
style=
"width: 100px"
></vxe-input>
...
...
@@ -175,7 +173,7 @@
<div
class=
"other-option"
>
其他:
<vxe-input
v-model=
"formData[row.code + '_'+ fieldOpt.otherField
]"
v-model=
"formData[getFieldKey(row.code, fieldOpt.otherField)
]"
style=
"width: 300px"
></vxe-input>
</div>
...
...
@@ -187,66 +185,137 @@
</template>
</template>
<!-- 普通单选组 -->
<span
v-else-if=
"item.type === 'radio-group'"
class=
"radio-group"
>
<vxe-radio-group
v-model=
"formData[row.code +'_'+ item.field
]"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)
]"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-radio>
</vxe-radio-group>
</span>
<!-- 带额外字段的单选组 -->
<span
v-else-if=
"item.type === 'radio-group-extraFields'"
class=
"radio-group"
style=
"width:100%"
>
<vxe-radio-group
v-model=
"formData[row.code +'_'+ item.field
]"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)
]"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt.label"
>
{{ opt.label }}
<
template
v-if=
"opt.extraField&&formData[row.code +'_'+ item.field]==
opt.label"
>
<
template
v-if=
"opt.extraField && formData[getFieldKey(row.code, item.field)] ===
opt.label"
>
<span>
{{
opt
.
extraField
}}
</span>
<vxe-input
v-model=
"formData[row.code + '_'+ opt.otereField]"
style=
"width: 200px;"
></vxe-input>
<vxe-input
v-model=
"formData[getFieldKey(row.code, opt.otereField)]"
style=
"width: 200px;"
></vxe-input>
</
template
>
<
template
v-if=
"opt.extraFields&&formData[row.code +'_'+ item.field]==
opt.label"
>
<template
v-for=
"(ccopt,
ccind) in opt.extraFields"
:key=
"ccind"
>
<
template
v-if=
"opt.extraFields && formData[getFieldKey(row.code, item.field)] ===
opt.label"
>
<template
v-for=
"(ccopt,
ccind) in opt.extraFields"
:key=
"ccind"
>
<span>
{{
ccopt
.
label
}}
:
</span>
<vxe-input
v-model=
"formData[
row.code + '_'+ ccopt.field]"
style=
"width: 200px;"
></vxe-input>
<vxe-input
v-model=
"formData[
getFieldKey(row.code, ccopt.field)]"
style=
"width: 200px;"
></vxe-input>
</
template
>
</template>
</vxe-radio>
</vxe-radio-group>
<span
v-if=
"item.unit"
class=
"unit"
>
{{ item.unit }}
</span>
</span>
<!-- 多选组 -->
<div
v-else-if=
"item.type === 'checkbox-group'"
>
<span
class=
"checkbox-group"
>
<vxe-checkbox-group
v-model=
"formData[row.code+'_'+item.field
]"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)
]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-checkbox>
<
template
v-if=
"item.otherOption"
>
<vxe-checkbox
label=
"其他"
>
其他:
<vxe-input
v-model=
"formData[row.code + '_'+ item.otherField]"
style=
"width: 200px;"
:disabled=
"!(formData[row.code+'_'+item.field]?.indexOf('其他')>-1)"
>
</vxe-input>
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
style=
"width: 200px;"
:disabled=
"!(formData[getFieldKey(row.code, item.field)]?.includes('其他'))"
></vxe-input>
</vxe-checkbox>
</
template
>
</vxe-checkbox-group>
</span>
</div>
<
template
v-else-if=
"item.type=='textarea'"
>
<vxe-textarea
v-model=
"formData[row.code +'_'+ item.field]"
:rows=
"item.rows"
:style=
"
{width:item.width}">
</vxe-textarea>
<!-- 文本域 -->
<
template
v-else-if=
"item.type === 'textarea'"
>
<vxe-textarea
v-model=
"formData[getFieldKey(row.code, item.field)]"
:rows=
"item.rows"
:style=
"
{width: item.width}"
>
</vxe-textarea>
</
template
>
<!-- 其他输入类型 -->
<
template
v-else
>
<vxe-input
:type=
"item.type"
v-model=
"formData[row.code +'_'+ item.field]"
size=
"mini"
class=
"table-input"
:style=
"
{width:item.width}">
</vxe-input>
<div
class=
"input-wrapper"
>
<vxe-input
:type=
"item.type"
v-model=
"formData[getFieldKey(row.code, item.field)]"
size=
"mini"
class=
"table-input"
:style=
"
{width: item.width}"
:disabled="!item.hasRight"
@blur="handleInputBlur(row.code, item.field, item.matchedFormula?.formula)"
/>
<span
v-if=
"item.unit"
class=
"unit"
>
{{
item
.
unit
}}
</span>
</
template
>
</template>
<!-- 帮助图标 -->
<span
v-if=
"showHelpIcon(row.code, item.field, item)"
class=
"help-icon"
@
click
.
stop=
"toggleTooltip(row.code, item.field)"
title=
"点击查看校验规则"
>
?
</span>
<!-- 错误图标 -->
<span
v-if=
"inputErrors[getFieldKey(row.code, item.field)]"
class=
"error-icon"
@
click
.
stop=
"toggleErrorTooltip(row.code, item.field)"
title=
"点击查看错误详情"
>
✗
</span>
<!-- 帮助提示 -->
<div
v-if=
"showTooltip && hoveredKey === getFieldKey(row.code, item.field)"
class=
"tooltip"
@
click
.
stop
>
{{
item
.
matchedFormula
?.
des
||
'暂无校验规则'
}}
</div>
<!-- 错误提示 -->
<div
v-if=
"showErrorTooltip && hoveredErrorKey === getFieldKey(row.code, item.field)"
class=
"error-tooltip"
@
click
.
stop
>
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
message
}}
<br>
<span
style=
"color: #ffcccc;"
>
公式:
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
formula
}}
</span>
<template
v-if=
"inputErrors[getFieldKey(row.code, item.field)].error"
>
<br>
<span
style=
"color: #ff9999; font-size: 11px;"
>
错误:
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
error
}}
</span>
</
template
>
</div>
</div>
</template>
</template>
</div>
</template>
</vxe-column>
<vxe-column
field=
"remarks"
title=
"备注"
width=
"200"
>
<
template
#
default=
"{ row }"
>
<vxe-textarea
:rows=
"row.remarks.rows"
v-model=
"formData[row.code+'_'+row.remarks.field]"
style=
"height:100%;"
>
</vxe-textarea>
<vxe-textarea
:rows=
"row.remarks.rows"
v-model=
"formData[getFieldKey(row.code, row.remarks.field)]"
style=
"height:100%;"
></vxe-textarea>
</
template
>
</vxe-column>
</vxe-table>
...
...
@@ -267,212 +336,295 @@
<
script
lang=
"ts"
setup
>
import
HistoryFillCheck
from
'./check/historyFillCheck.vue'
import
{
tableFormData
}
from
'../../data/tb8.data'
;
import
{
ref
,
reactive
,
nextTick
,
onMounted
,
toRaw
}
from
'vue'
import
{
VxeUI
,
VxeToolbarInstance
,
VxeToolbarPropTypes
}
from
'vxe-table'
import
{
batchSaveOrUpdate
,
queryRecord
,
batchSaveOrUpdateBeforeDelete
}
from
'../../record/BaosongTaskRecord.api'
import
{
tableFormData
}
from
'../../data/tb8.data'
import
{
ref
,
reactive
,
nextTick
,
onMounted
,
computed
}
from
'vue'
import
{
VxeUI
}
from
'vxe-table'
import
{
batchSaveOrUpdate
,
queryRecord
,
batchSaveOrUpdateBeforeDelete
}
from
'../../record/BaosongTaskRecord.api'
import
{
queryAllTplItemForUser
,
findUserRightForTplItem
}
from
'../../alloc/BaosongTaskAlloc.api'
import
{
allTplItems
}
from
'../../tpl/BaosongTplItem.api'
import
{
getTblvalidFormula
}
from
'../../tpl/BaosongDataValid.api'
import
{
useRoute
}
from
'vue-router'
;
import
{
useRoute
}
from
'vue-router'
const
route
=
useRoute
();
const
tableRef
=
ref
();
const
tplItemMap
=
ref
({});
const
historyFillCheckRef
=
ref
<
any
>
(
null
);
const
route
=
useRoute
()
const
tableRef
=
ref
()
const
tplItemMap
=
ref
<
Record
<
string
,
TplItemInfo
>>
({})
const
historyFillCheckRef
=
ref
<
any
>
(
null
)
// 查询参数
const
queryParam
=
ref
({
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
tplName
:
''
,
tplCode
:
''
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
tplName
:
''
,
tplCode
:
''
})
// 权限相关状态
// 状态变量
const
loading
=
ref
(
false
)
const
formData
=
reactive
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormData
[]
>
([])
// 权限和验证相关
const
userAllocItems
=
ref
<
string
[]
>
([])
const
validFormula
=
ref
<
any
[]
>
([])
const
validationResultsList
=
ref
<
any
[]
>
([])
const
validFormula
=
ref
<
FormulaItem
[]
>
([])
const
validationResultsList
=
ref
<
ValidationResult
[]
>
([])
const
drawerVisible
=
ref
(
false
)
const
historyDrawerVisible
=
ref
(
false
)
// 工具提示状态
const
showTooltip
=
ref
(
false
)
const
hoveredKey
=
ref
(
''
)
const
showErrorTooltip
=
ref
(
false
)
const
hoveredErrorKey
=
ref
(
''
)
const
inputErrors
=
ref
<
Record
<
string
,
InputError
>>
({})
const
isInitialized
=
ref
(
false
)
// 验证公式提示
const
validationTooltipVisible
=
ref
(
false
)
const
validationTooltipPosition
=
ref
({
left
:
0
,
top
:
0
})
const
validationTooltipContent
=
ref
(
''
)
// 接口定义
interface
FormData
{
id
:
number
|
null
taskid
:
number
tplid
:
number
itemid
?:
number
itempid
?:
number
itempid
?:
number
content
:
string
tplcode
:
string
rind
:
number
tplcode
:
string
rind
:
number
}
onMounted
(
async
()
=>
{
if
(
route
.
query
.
taskId
)
{
queryParam
.
value
.
taskId
=
Number
(
route
.
query
.
taskId
);
}
if
(
route
.
query
.
taskName
)
{
queryParam
.
value
.
taskName
=
String
(
route
.
query
.
taskName
);
}
if
(
route
.
query
.
tplId
)
{
queryParam
.
value
.
tplId
=
Number
(
route
.
query
.
tplId
);
}
if
(
route
.
query
.
tplName
)
{
queryParam
.
value
.
tplName
=
String
(
route
.
query
.
tplName
);
}
if
(
route
.
query
.
tplCode
)
{
queryParam
.
value
.
tplCode
=
String
(
route
.
query
.
tplCode
);
}
interface
TplItemInfo
{
pid
:
number
itemid
:
number
formTp
:
string
code
:
string
}
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
}
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
}
// 获取权限和验证公式
interface
ValidationResult
{
field
:
string
description
:
string
formula
:
string
isValid
:
boolean
fieldValue
:
any
rowCode
:
string
}
// 计算属性
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
))
// 生命周期钩子
onMounted
(
async
()
=>
{
await
initializePage
()
})
// 初始化页面
const
initializePage
=
async
()
=>
{
try
{
await
setQueryParams
()
// 并行获取权限和验证公式
await
Promise
.
all
([
fetchUserRights
(),
fetchValidationFormulas
()
])
console
.
log
(
'获取到的权限:'
,
userAllocItems
.
value
.
length
)
console
.
log
(
'获取到的公式:'
,
validFormula
.
value
.
length
)
await
setTplItemMap
()
setFormItemRight
()
await
setData
()
isInitialized
.
value
=
true
// 延迟刷新帮助图标
setTimeout
(()
=>
{
refreshHelpIcons
()
},
300
)
}
catch
(
error
)
{
console
.
error
(
'初始化页面失败:'
,
error
)
showErrorMessage
(
'初始化页面失败'
,
error
)
}
}
// 设置查询参数
const
setQueryParams
=
()
=>
{
const
params
=
route
.
query
if
(
params
.
taskId
)
queryParam
.
value
.
taskId
=
Number
(
params
.
taskId
)
if
(
params
.
taskName
)
queryParam
.
value
.
taskName
=
String
(
params
.
taskName
)
if
(
params
.
tplId
)
queryParam
.
value
.
tplId
=
Number
(
params
.
tplId
)
if
(
params
.
tplName
)
queryParam
.
value
.
tplName
=
String
(
params
.
tplName
)
if
(
params
.
tplCode
)
queryParam
.
value
.
tplCode
=
String
(
params
.
tplCode
)
}
// 获取用户权限
const
fetchUserRights
=
async
()
=>
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
})
}
// 获取验证公式
const
fetchValidationFormulas
=
async
()
=>
{
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
,
})
}
catch
(
error
)
{
console
.
error
(
'获取权限或验证公式失败:'
,
error
)
}
await
setTplItemMap
()
await
setData
();
})
const
loading
=
ref
(
false
)
const
formData
=
reactive
({});
const
formValues
=
ref
<
FormData
[]
>
([])
}
// 保存批量数据
const
saveBatch
=
async
()
=>
{
try
{
loading
.
value
=
true
await
getFillDatas
()
if
(
formValues
.
value
.
length
>
0
)
{
if
(
formValues
.
value
.
length
>
0
)
{
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
VxeUI
.
modal
.
message
({
content
:
'保存成功'
,
status
:
'success'
})
}
else
{
VxeUI
.
modal
.
message
({
content
:
'没有需要保存的数据'
,
status
:
'warning'
})
}
}
catch
(
error
)
{
VxeUI
.
modal
.
message
({
content
:
`保存失败:
${
error
.
message
}
`
,
status
:
'error'
}
)
showErrorMessage
(
'保存失败'
,
error
)
}
finally
{
loading
.
value
=
false
}
}
const
getFillDatas
=
async
()
=>
{
// 获取填充数据
const
getFillDatas
=
async
()
=>
{
try
{
formValues
.
value
=
[]
for
(
const
strKey
in
formData
)
{
const
valData
=
formData
[
strKey
]
if
(
valData
!==
undefined
&&
valData
!==
""
)
{
let
st
=
strKey
.
indexOf
(
"_"
);
let
t1
=
strKey
.
substring
(
0
,
st
);
let
t2
=
strKey
.
substring
(
st
+
1
);
await
setFormValues
(
t1
,
t2
,
valData
,
1
)
if
(
valData
!==
undefined
&&
valData
!==
""
)
{
const
st
=
strKey
.
indexOf
(
"_"
)
const
pcode
=
strKey
.
substring
(
0
,
st
)
const
field
=
strKey
.
substring
(
st
+
1
)
await
setFormValues
(
pcode
,
field
,
valData
,
1
)
}
}
}
catch
(
error
)
{
console
.
log
(
error
)
}
finally
{
}
console
.
error
(
'获取填充数据失败:'
,
error
)
}
}
// 设置表单值
const
setFormValues
=
async
(
pcode
:
string
,
code
:
string
,
valData
:
any
,
rind
:
number
)
=>
{
if
(
!
valData
)
return
const
strKey
=
`
${
pcode
}
_
${
code
}
`
const
item
=
tplItemMap
.
value
[
strKey
]
if
(
!
item
)
{
console
.
warn
(
`未找到对应的模板项:
${
strKey
}
`
)
return
}
const
setFormValues
=
async
(
pcode
,
code
,
valData
,
rind
)
=>
{
if
(
!
valData
)
return
;
let
vals
=
Array
.
isArray
(
valData
)
?
valData
.
join
(
','
)
:
valData
let
strKey
=
pcode
+
"_"
+
code
const
item
=
tplItemMap
.
value
[
strKey
];
const
{
pid
,
itemid
}
=
item
??
{};
const
vals
=
Array
.
isArray
(
valData
)
?
valData
.
join
(
','
)
:
String
(
valData
)
le
t
tempForm
:
FormData
=
{
cons
t
tempForm
:
FormData
=
{
id
:
null
,
rind
:
rind
,
rind
,
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
itemid
,
itempid
:
pid
,
content
:
vals
};
itemid
:
item
.
itemid
,
itempid
:
item
.
pid
,
content
:
vals
}
formValues
.
value
.
push
(
tempForm
)
}
async
function
setData
()
{
try
{
loading
.
value
=
true
;
const
taskId
=
queryParam
.
value
.
taskId
;
const
tplid
=
queryParam
.
value
.
tplId
;
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
]);
await
setTplItemMap
();
const
recordData
=
await
queryRecord
({
taskid
:
taskId
,
tplid
:
tplid
});
const
valueObj
=
{};
for
(
const
data
of
recordData
)
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
;
valueObj
[
key
]
=
data
.
content
;
}
// 加载数据
const
setData
=
async
()
=>
{
try
{
loading
.
value
=
true
// 清空表单数据
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
])
for
(
const
strKey
of
Object
.
keys
(
tplItemMap
.
value
))
{
await
setTplItemMap
()
const
item
=
tplItemMap
.
value
[
strKey
];
if
(
!
item
)
continue
;
// 查询记录数据
const
recordData
=
await
queryRecord
({
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
})
const
{
pid
,
itemid
,
formTp
}
=
item
;
const
dataVal
=
valueObj
[
`
${
pid
}
_
${
itemid
}
_1`
];
// 构建数据映射
const
valueObj
:
Record
<
string
,
string
>
=
{}
recordData
.
forEach
((
data
:
any
)
=>
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
valueObj
[
key
]
=
data
.
content
})
if
(
!
dataVal
)
continue
;
// 设置表单数据
Object
.
keys
(
tplItemMap
.
value
).
forEach
(
strKey
=>
{
const
item
=
tplItemMap
.
value
[
strKey
]
const
{
pid
,
itemid
,
formTp
}
=
item
const
dataKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueObj
[
dataKey
]
if
(
dataVal
!==
undefined
)
{
if
(
formTp
===
'checkbox'
)
{
formData
[
strKey
]
=
dataVal
?.
split
(
","
)
||
[];
formData
[
strKey
]
=
dataVal
.
split
(
","
)
||
[]
}
else
{
formData
[
strKey
]
=
dataVal
||
""
;
formData
[
strKey
]
=
dataVal
||
""
}
}
})
}
catch
(
error
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
});
showErrorMessage
(
'加载数据失败'
,
error
)
}
finally
{
loading
.
value
=
false
;
loading
.
value
=
false
}
}
async
function
setTplItemMap
()
{
// 设置模板项映射
const
setTplItemMap
=
async
()
=>
{
try
{
const
tplid
=
queryParam
.
value
.
tplId
const
tplItem
=
await
allTplItems
({
tplid
:
tplid
,
const
tplItems
=
await
allTplItems
({
tplid
:
queryParam
.
value
.
tplId
,
})
tplItem
.
forEach
(
item
=>
{
let
strKey
=
item
.
pcode
+
"_"
+
item
.
xmlcode
;
tplItemMap
.
value
[
strKey
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
};
const
map
:
Record
<
string
,
TplItemInfo
>
=
{}
tplItems
.
forEach
(
item
=>
{
const
strKey
=
`
${
item
.
pcode
}
_
${
item
.
xmlcode
}
`
map
[
strKey
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
}
})
tplItemMap
.
value
=
map
}
catch
(
error
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败`
,
status
:
'error'
})
}
finally
{
showErrorMessage
(
'加载模板项失败'
,
error
)
}
}
// 工具函数
const
getFieldKey
=
(
pcode
:
string
|
undefined
,
field
:
string
):
string
=>
{
return
pcode
?
`
${
pcode
}
_
${
field
}
`
:
field
}
// 校验数据
...
...
@@ -492,7 +644,7 @@ const performValidation = () => {
process
.
push
(
'开始校验'
)
process
.
push
(
`找到
${
validFormula
.
value
.
length
}
个验证公式`
)
validFormula
.
value
.
forEach
((
formulaItem
:
any
)
=>
{
validFormula
.
value
.
forEach
((
formulaItem
:
FormulaItem
)
=>
{
process
.
push
(
`开始校验公式:
${
formulaItem
.
des
}
`
)
const
result
=
evaluateFormula
(
formulaItem
.
formula
,
formulaItem
.
des
,
process
)
if
(
result
)
{
...
...
@@ -503,7 +655,7 @@ const performValidation = () => {
}
// 解析和执行验证公式
const
evaluateFormula
=
(
formula
:
string
,
description
:
string
,
process
:
string
[]):
any
=>
{
const
evaluateFormula
=
(
formula
:
string
,
description
:
string
,
process
:
string
[]):
ValidationResult
|
null
=>
{
try
{
const
fieldMatch
=
formula
.
match
(
/
\[(\w
+
)\]
/
)
if
(
!
fieldMatch
)
{
...
...
@@ -524,11 +676,11 @@ const evaluateFormula = (formula: string, description: string, process: string[]
return
null
}
const
strKey
=
`
${
row
.
code
}
_
${
fieldName
}
`
const
strKey
=
getFieldKey
(
row
.
code
,
fieldName
)
const
fieldValue
=
formData
[
strKey
]
// 检查是否有空字段规则
const
emptyFieldRule
=
validFormula
.
value
.
find
((
f
:
any
)
=>
const
emptyFieldRule
=
validFormula
.
value
.
find
((
f
:
FormulaItem
)
=>
f
.
formula
.
includes
(
fieldName
)
&&
f
.
des
.
includes
(
'空字段'
)
)
...
...
@@ -538,7 +690,7 @@ const evaluateFormula = (formula: string, description: string, process: string[]
}
const
expression
=
formula
.
replace
(
/
\[(\w
+
)\]
/g
,
(
_
,
field
)
=>
{
const
value
=
formData
[
`
${
row
.
code
}
_
${
field
}
`
]
const
value
=
formData
[
getFieldKey
(
row
.
code
,
field
)
]
return
value
!==
undefined
?
`Number(
${
value
}
)`
:
'0'
})
...
...
@@ -559,13 +711,24 @@ const evaluateFormula = (formula: string, description: string, process: string[]
}
}
// 处理校验结果点击,定位到表格
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
// 处理校验结果点击
const
handleValidationResultClick
=
(
result
:
ValidationResult
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
VxeUI
.
modal
.
message
({
content
:
message
,
status
})
// 滚动到对应字段
scrollToField
(
result
)
}
// 滚动到字段位置
const
scrollToField
=
(
result
:
ValidationResult
)
=>
{
const
row
=
tableFormData
.
find
((
r
:
any
)
=>
r
.
code
===
result
.
rowCode
)
if
(
row
)
{
const
fieldItem
=
row
.
content
.
find
((
c
:
any
)
=>
c
.
field
===
result
.
field
)
if
(
fieldItem
)
{
const
strKey
=
`
${
row
.
code
}
_
${
result
.
field
}
`
const
strKey
=
getFieldKey
(
row
.
code
,
result
.
field
)
const
inputElement
=
document
.
querySelector
(
`[data-field="
${
strKey
}
"]`
)
as
HTMLElement
if
(
inputElement
)
{
inputElement
.
scrollIntoView
({
behavior
:
'smooth'
,
block
:
'center'
})
...
...
@@ -580,7 +743,7 @@ const handleValidationResultClick = (result: any) => {
}
// 显示验证公式帮助
const
showValidationHelp
=
(
formulaItem
:
any
,
event
:
MouseEvent
)
=>
{
const
showValidationHelp
=
(
formulaItem
:
FormulaItem
,
event
:
MouseEvent
)
=>
{
const
rect
=
(
event
.
target
as
HTMLElement
).
getBoundingClientRect
()
validationTooltipPosition
.
value
=
{
left
:
rect
.
left
+
rect
.
width
/
2
,
...
...
@@ -590,8 +753,12 @@ const showValidationHelp = (formulaItem: any, event: MouseEvent) => {
validationTooltipVisible
.
value
=
true
}
// 关闭验证公式帮助
const
closeTooltip
=
()
=>
{
// 关闭所有提示
const
closeAllTooltips
=
()
=>
{
showTooltip
.
value
=
false
hoveredKey
.
value
=
''
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
validationTooltipVisible
.
value
=
false
}
...
...
@@ -605,26 +772,228 @@ const closeHistoryDrawer = () => {
historyDrawerVisible
.
value
=
false
}
// 显示帮助图标
const
showHelpIcon
=
(
rowCode
:
string
,
field
:
string
,
item
:
any
):
boolean
=>
{
if
(
item
.
hasValidFormula
!==
undefined
)
{
return
item
.
hasValidFormula
===
true
}
const
key
=
getFieldKey
(
rowCode
,
field
)
const
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
if
(
!
hasRight
)
return
false
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
field
)
return
!!
formulaResult
.
formula
}
// 设置表单项权限
const
setFormItemRight
=
()
=>
{
console
.
log
(
'开始设置表单权限...'
)
console
.
log
(
'userAllocItems 数量:'
,
userAllocItems
.
value
.
length
)
console
.
log
(
'validFormula 数量:'
,
validFormula
.
value
.
length
)
tableFormData
.
forEach
((
row
:
any
)
=>
{
if
(
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
row
.
content
.
forEach
((
item
:
any
)
=>
{
if
(
item
.
field
)
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
item
.
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
if
(
item
.
hasRight
)
{
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formulaResult
.
formula
item
.
matchedFormula
=
formulaResult
.
formula
||
null
}
else
{
item
.
hasValidFormula
=
false
item
.
matchedFormula
=
null
}
}
})
}
})
console
.
log
(
'表单权限设置完成'
)
}
// 查找字段对应的最早公式
const
findEarliestFormulaForField
=
(
formulas
:
FormulaItem
[],
field
:
string
):
{
formula
:
FormulaItem
|
null
,
index
:
number
}
=>
{
let
result
=
{
formula
:
null
,
index
:
Infinity
}
formulas
?.
forEach
((
f
:
FormulaItem
)
=>
{
const
text
=
f
.
formula
||
''
const
escapedField
=
field
.
replace
(
/
[
.*+?^${}()|[
\]\\]
/g
,
'
\\
$&'
)
const
regex
=
new
RegExp
(
`\\b
${
escapedField
}
\\b(?!\\w)`
,
'g'
)
const
match
=
regex
.
exec
(
text
)
if
(
match
&&
match
.
index
<
result
.
index
)
{
result
=
{
formula
:
f
,
index
:
match
.
index
}
}
})
return
result
}
// 刷新帮助图标
const
refreshHelpIcons
=
()
=>
{
console
.
log
(
'刷新帮助图标...'
)
tableFormData
.
forEach
((
row
:
any
)
=>
{
if
(
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
row
.
content
.
forEach
((
item
:
any
)
=>
{
if
(
item
.
field
)
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
if
(
hasRight
)
{
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formulaResult
.
formula
item
.
matchedFormula
=
formulaResult
.
formula
||
null
}
else
{
item
.
hasValidFormula
=
false
item
.
matchedFormula
=
null
}
}
})
}
})
nextTick
(()
=>
{
console
.
log
(
'帮助图标刷新完成'
)
})
}
// 输入框失去焦点处理
const
handleInputBlur
=
(
rowCode
:
string
,
field
:
string
,
formula
?:
string
)
=>
{
if
(
!
formula
)
return
const
key
=
getFieldKey
(
rowCode
,
field
)
const
value
=
formData
[
key
]
if
(
!
value
||
value
===
''
)
{
delete
inputErrors
.
value
[
key
]
return
}
validateFieldFormula
(
rowCode
,
field
,
formula
)
}
// 验证字段公式
const
validateFieldFormula
=
(
rowCode
:
string
,
field
:
string
,
formula
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
try
{
const
expression
=
buildExpression
(
formula
,
rowCode
)
const
isValid
=
eval
(
expression
)
if
(
!
isValid
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
}
}
else
{
delete
inputErrors
.
value
[
key
]
}
}
catch
(
error
:
any
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
,
error
:
error
.
message
}
}
}
// 构建表达式
const
buildExpression
=
(
formula
:
string
,
rowCode
:
string
):
string
=>
{
const
reservedWords
=
[
'and'
,
'or'
,
'not'
,
'equal'
,
'less'
,
'greater'
,
'if'
,
'else'
,
'true'
,
'false'
]
// 提取字段名
const
fieldNames
=
formula
.
match
(
/
\b[
A-Za-z
][
A-Za-z0-9_
]
*
\b
/g
)
||
[]
const
uniqueFields
=
[...
new
Set
(
fieldNames
.
filter
(
f
=>
!
reservedWords
.
includes
(
f
.
toLowerCase
())
))]
// 按长度降序排序,避免替换时出现部分匹配
uniqueFields
.
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
)
let
expression
=
formula
for
(
const
fieldName
of
uniqueFields
)
{
const
key
=
getFieldKey
(
rowCode
,
fieldName
)
const
value
=
formData
[
key
]
if
(
value
!==
undefined
&&
value
!==
''
)
{
expression
=
expression
.
replace
(
new
RegExp
(
`\\b
${
fieldName
}
\\b`
,
'g'
),
`Number(
${
value
}
)`
)
}
}
return
expression
}
// 工具提示控制
const
toggleTooltip
=
(
rowCode
:
string
,
field
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
hoveredKey
.
value
===
key
)
{
showTooltip
.
value
=
false
hoveredKey
.
value
=
''
}
else
{
showTooltip
.
value
=
true
hoveredKey
.
value
=
key
}
}
const
toggleErrorTooltip
=
(
rowCode
:
string
,
field
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
hoveredErrorKey
.
value
===
key
)
{
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
}
else
{
showErrorTooltip
.
value
=
true
hoveredErrorKey
.
value
=
key
}
}
// 错误消息显示
const
showErrorMessage
=
(
title
:
string
,
error
:
any
)
=>
{
const
message
=
error
instanceof
Error
?
error
.
message
:
String
(
error
)
VxeUI
.
modal
.
message
({
content
:
`
${
title
}
:
${
message
}
`
,
status
:
'error'
})
}
</
script
>
<
style
lang=
"less"
scoped
>
/* 基础字体和排版 */
.bank-report-table {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
color: #000;
margin: 5px auto;
width: 90%; /* 统一使用一个宽度 */
height:80vh
width: 90%;
height: 80vh;
position: relative;
}
/* 表格样式 */
.custom-table
, .attachment-table {
.custom-table
{
width: 100%;
border: 1px solid #000;
border-collapse: collapse; /* 统一边框处理 */
border-collapse: collapse;
.vxe-header--column,
.vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
}
/* 加载遮罩层
样式
*/
/* 加载遮罩层 */
.loading-overlay {
position: absolute;
top: 0;
...
...
@@ -650,7 +1019,7 @@ const closeHistoryDrawer = () => {
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin
2
s linear infinite;
animation: spin
1
s linear infinite;
}
@keyframes spin {
...
...
@@ -713,20 +1082,20 @@ const closeHistoryDrawer = () => {
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.result-item
:hover {
&
:hover {
background-color: #f5f5f5;
}
}
.result-item
.success {
&
.success {
background-color: #f6ffed;
border-left: 4px solid #52c41a;
}
}
.result-item
.error {
&
.error {
background-color: #fff2f0;
border-left: 4px solid #ff4d4f;
}
}
.result-field {
...
...
@@ -768,55 +1137,52 @@ const closeHistoryDrawer = () => {
white-space: pre-wrap;
}
/* 表格单元格样式 */
.custom-table .vxe-header--column,
.custom-table .vxe-body--column,
.attachment-table .vxe-header--column,
.attachment-table .vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
/* 内容单元格 */
.content-cell {
line-height: 1.8;
}
/* 输入框包装器 */
.input-wrapper {
position: relative;
display: inline-block;
}
/* 输入框样式 */
.table-input,
.vxe-input {
height: 24px;
line-height:
30px;
line-height:
24px;
min-width: 150px;
margin: 0 2px;
padding: 0 5px;
border: none; /* 去掉多余的边框,只留底部边框 */
border-bottom: 1px solid #333;
background: transparent;
text-align: center;
}
border: 1px solid #d9d9d9;
border-radius: 2px;
background: #fff;
text-align: left;
/* 表单元素的标签和图标 */
.vxe-radio--label,
.vxe-checkbox--label {
font-size: 12px;
}
.vxe-radio--icon,
.vxe-checkbox--icon {
font-size: 12px;
&:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
}
/*
单选框组和复选框组布局 */
/*
表单组件样式 */
.vxe-radio-group,
.vxe-checkbox-group {
display: inline-block;
display: inline-flex;
flex-wrap: wrap;
gap: 8px;
margin-right: 5px;
max-width: 99%;
max-width: 100%;
}
.vxe-radio,
.vxe-checkbox {
margin: 2px 5px;
white-space: nowrap;
}
/* 其他辅助样式 */
.radio-group,
.checkbox-group {
display: inline-block;
...
...
@@ -824,43 +1190,115 @@ const closeHistoryDrawer = () => {
max-width: 99%;
}
.vxe-radio,
.vxe-checkbox {
margin: 5px 5px 5px 30px;
white-space: nowrap;
/* 单位样式 */
.unit {
margin-left: 4px;
color: #666;
font-size: 12px;
}
/* 附件部分样式 */
.attachments {
margin-top: 20px;
width: 100%;
/* 图标样式 */
.help-icon {
margin-left: 5px;
cursor: pointer;
color: #1890ff;
font-weight: bold;
font-size: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
line-height: 20px;
background: #e6f7ff;
border-radius: 50%;
transition: all 0.3s;
opacity: 0;
animation: fadeIn 0.5s forwards;
animation-delay: 0.3s;
&:hover {
background: #bae7ff;
transform: scale(1.1);
}
}
.attachments h4 {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
.error-icon {
margin-left: 5px;
cursor: pointer;
color: #ff4d4f;
font-size: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
line-height: 20px;
transition: all 0.3s;
&:hover {
transform: scale(1.1);
}
}
.attachment-table {
width: 100%;
border: 1px solid #000;
border-collapse: collapse; /* 统一边框处理 */
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* 提示框样式 */
.tooltip, .error-tooltip {
position: absolute;
top: 100%;
left: 0;
margin-top: 5px;
padding: 10px;
font-size: 12px;
border-radius: 4px;
z-index: 1000;
white-space: pre-wrap;
max-width: 300px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
/* 无用或重复定义已优化移除 */
.tooltip {
background: #333;
color: #fff;
/* 其他特殊块 */
blockquote {
padding: 0 5px;
color: black;
cursor: pointer;
&::before {
content: '';
position: absolute;
bottom: 100%;
left: 10px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #333 transparent;
}
}
</
style
>
.error-tooltip {
background: #8b0000;
color: #fff;
border: 1px solid #ff4d4f;
&::before {
content: '';
position: absolute;
bottom: 100%;
left: 10px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #8b0000 transparent;
}
}
<
style
scoped
>
/* 额外字段样式 */
.extra-fields-container {
margin-left: 40px;
margin-top: 8px;
...
...
@@ -883,8 +1321,15 @@ blockquote {
margin-top: 8px;
}
.unit
{
margin-left
:
8px
;
color
:
#888
;
/* 响应式调整 */
@media (max-width: 1200px) {
.bank-report-table {
width: 95%;
}
.table-input,
.vxe-input {
min-width: 120px;
}
}
</
style
>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论