Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
zrch-risk-39
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Administrator
zrch-risk-39
Commits
12b89e58
提交
12b89e58
authored
2月 11, 2026
作者:
kxjia
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
完善功能
上级
574734b0
显示空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
2358 行增加
和
2286 行删除
+2358
-2286
tb6.data.ts
zrch-risk-client-39/src/views/baosong/data/tb6.data.ts
+1
-1
Tb1.vue
...isk-client-39/src/views/baosong/report/components/Tb1.vue
+0
-1
Tb10.vue
...sk-client-39/src/views/baosong/report/components/Tb10.vue
+794
-618
Tb6.vue
...isk-client-39/src/views/baosong/report/components/Tb6.vue
+799
-808
Tb9.vue
...isk-client-39/src/views/baosong/report/components/Tb9.vue
+760
-854
BaosongTaskReviewList.vue
...ent-39/src/views/baosong/review/BaosongTaskReviewList.vue
+3
-2
index.vue
zrch-risk-client-39/src/views/demo/form/index.vue
+1
-1
StProblemCheckPlanForm.vue
...roject/problemCheck/components/StProblemCheckPlanForm.vue
+0
-1
没有找到文件。
zrch-risk-client-39/src/views/baosong/data/tb6.data.ts
浏览文件 @
12b89e58
...
...
@@ -33,7 +33,7 @@ export const tableFormData = [
{
type
:
'text'
,
value
:
'<strong>网络安全责任制建立情况</strong>'
},
{
type
:
'br'
},
{
type
:
'text'
,
value
:
'建立并落实网络安全工作责任制'
},
{
type
:
'radio-group'
,
field
:
'C23A001'
,
options
:
[
'是'
,
'否'
]
},
{
type
:
'radio-group'
,
field
:
'C23A001'
,
options
:
[
'是'
,
'否'
]
,
value
:
''
},
{
type
:
'br'
},
{
type
:
'text'
,
value
:
'网络安全第一责任人:'
},
{
type
:
'text'
,
value
:
'姓名'
},
...
...
zrch-risk-client-39/src/views/baosong/report/components/Tb1.vue
浏览文件 @
12b89e58
...
...
@@ -652,7 +652,6 @@ const collectVxeTableData = async () => {
}
}
if
(
formData
.
footer
)
{
alert
(
formData
.
footerPcode
)
for
(
const
[
code
,
value
]
of
Object
.
entries
(
formData
.
footer
))
{
await
addFormValue
(
formData
.
footerPcode
,
code
,
value
,
1
)
}
...
...
zrch-risk-client-39/src/views/baosong/report/components/Tb10.vue
浏览文件 @
12b89e58
<
template
>
<div
class=
"bank-report-table"
style=
"height:80vh"
@
click=
"closeAllTooltips"
>
<div
class=
"bank-report-table"
style=
"height:80vh"
@
click=
"closeAllTooltips"
>
<!-- 加载遮罩层 -->
<div
v-if=
"loading"
class=
"loading-overlay"
>
<div
class=
"loading-spinner"
>
...
...
@@ -8,77 +12,115 @@
</div>
</div>
<!-- 工具栏 -->
<vxe-toolbar>
<template
#
button
>
<div
class=
"toolbar-info"
>
<span
class=
"info-label"
>
填报任务:
{{
queryParam
?.
taskName
}}
</span>
<span
class=
"info-label"
>
表格:
{{
queryParam
?.
tplCode
}}
{{
queryParam
?.
tplName
}}
</span>
<template
#
buttons
>
<div
style=
"margin:10px"
>
<span
class=
"table-info-text"
>
填报任务:
{{
queryParam
.
taskName
}}
</span>
<span
class=
"table-info-text"
style=
"margin-left:20px"
>
表格:
{{
queryParam
.
tplCode
}}
{{
queryParam
.
tplName
}}
</span>
</div>
</
template
>
<
template
#
tools
v-if=
"!queryParam.comfrom"
>
<vxe-button
status=
"primary"
icon=
"vxe-icon-edit"
@
click=
"handleOpenHistoryDrawer()"
:disabled=
"loading"
>
近5年数据填报
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"checkData()"
>
校验
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"saveBatch"
>
保存
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-edit"
@
click=
"handleOpenHistoryDrawer"
:disabled=
"loading"
>
近5年数据填报
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"validateData"
>
校验
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"saveBatch"
>
保存
</vxe-button>
</
template
>
</vxe-toolbar>
<!-- 校验抽屉 -->
<ValidationDrawer
ref=
"validationDrawerRef"
v-model=
"drawerVisible"
:tableFormData=
"tableFormData"
@
validationResultClick=
"handleValidationResultClick"
/>
<!-- 主表格 -->
<vxe-table
border
ref=
"tableRef"
height=
"auto"
show-header-overflow
:data=
"tableFormData"
:column-config=
"{ resizable: true }"
:row-config=
"{ resizable: true }"
class=
"custom-table"
height=
"auto"
:merge-cells=
"mergeCells"
>
<vxe-column
field=
"serialNumber"
title=
"序号"
width=
"80"
></vxe-column>
<vxe-column
field=
"project"
title=
"项目"
width=
"50"
>
<
template
#
default=
"{ row }"
>
<span
class=
"project-title"
>
{{
row
.
project
}}
</span>
</
template
>
</vxe-column>
<vxe-column
field=
"project2"
title=
""
width=
"50"
>
<vxe-column
field=
"serialNumber"
title=
"序号"
width=
"50"
></vxe-column>
<vxe-column
field=
"project"
title=
"项目"
width=
"100"
>
<
template
#
default=
"{ row }"
>
<span
class=
"project-
title"
>
{{
row
.
project2
}}
</span>
<span
class=
"project-
name"
>
{{
row
.
project
}}
</span>
</
template
>
</vxe-column>
<vxe-column
field=
"content"
title=
"内容"
row-resize
>
<vxe-column
field=
"content"
title=
"内容"
>
<
template
#
default=
"{ row }"
>
<div
class=
"content-cell"
>
<!-- 多列表格 -->
<template
v-if=
"row.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"row.project"
:columnsPerRow=
"6"
:records=
"[]"
:fields=
"row.content"
:ref=
"(el) => setMultiColumnTableRef(el, row.code)"
class=
"embedded-table
"
style=
"margin:0px;padding:0px
"
/>
</
template
>
<!-- 附件表格 -->
<
template
v-else-if=
"row.type === 'AttachTable'"
>
<AttachTable
:title=
"row.project"
:records=
"row.datas"
:fields=
"row.content"
:pcode=
"row.code"
:calcSum=
"row.calcSum"
:showFooter=
"row.showFooter"
:ref=
"(el) => setAttachTableRef(el, row.code)"
class=
"embedded-table"
:disabled=
"!row.hasRight"
style=
"margin:0px;padding:0px"
/>
</
template
>
<!-- Vxe表格 -->
<
template
v-else-if=
"row.type === 'VxeTable'"
>
<MyVxeTable1
:title=
"row.project"
:data=
"row.data"
:columns=
"row.columns"
:pcode=
"row.code"
:footerData=
"row.footerData"
:showFooter=
"row.showFooter"
:ref=
"(el) => setMyVxeTableRef(el, row.code)"
:disabled=
"!row.hasRight"
style=
"margin:0px;padding:0px"
/>
</
template
>
<!-- 普通字段渲染 -->
<
template
v-else
>
<template
v-for=
"(item, index) in row.content"
:key=
"index"
>
<!-- 文本类型 -->
<span
v-if=
"item.type === 'text'"
v-html=
"item.value"
></span>
<!-- 换行 -->
<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"
>
...
...
@@ -86,152 +128,117 @@
</
template
>
</span>
<
template
v-else-if=
"item.type === 'AttachTable'"
>
<AttachTable
:title=
"item.project"
:records=
"item.datas"
:fields=
"item.content"
:pcode=
"item.code"
:ref=
"(el) => setAttachTableRef(el, item.code)"
class=
"embedded-table"
/>
</
template
>
<
template
v-else-if=
"item.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"item.project"
:records=
"[]"
:fields=
"item.content"
:ref=
"(el) => setMultiColumnTableRef(el, item.code)"
class=
"embedded-table"
/>
</
template
>
<
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[getFieldKey(row.code, item.field)] === '是'"
class=
"extra-fields-container"
>
如是,
<template
v-if=
"item.extraField"
>
{{
item
.
extraLabel
}}
:
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.extraField)]"
class=
"extra-input"
></vxe-input>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
</
template
>
<
template
v-else-if=
"item.extraFields"
>
<template
v-for=
"(ccopt, ccind) in item.extraFields"
:key=
"ccind"
>
<span>
{{
ccopt
.
label
}}
:
</span>
<template
v-if=
"ccopt.formType === 'input'"
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
class=
"extra-input"
></vxe-input>
</
template
>
<
template
v-else-if=
"ccopt.formType === 'number'"
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
type=
"number"
class=
"extra-input"
></vxe-input>
<span
class=
"unit"
>
{{
ccopt
.
unit
}}
</span>
</
template
>
<
template
v-else-if=
"ccopt.formType === 'checkbox'"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
:content=
"opt"
></vxe-checkbox>
</vxe-checkbox-group>
</
template
>
<
template
v-else-if=
"ccopt.formType === 'radio'"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
:options=
"ccopt.options"
>
<vxe-radio
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
:content=
"opt"
></vxe-radio>
</vxe-radio-group>
</
template
>
<
template
v-else
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
:type=
"ccopt.formType"
class=
"extra-input"
></vxe-input>
</
template
>
<br>
<
template
v-if=
"ccopt.otherOption"
>
其他:
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.otherField)]"
class=
"extra-input"
></vxe-input>
</
template
>
<br>
</template>
</template>
</div>
</template>
</template>
<!-- 单选组 -->
<span
v-else-if=
"item.type === 'radio-group'"
class=
"radio-group"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
style=
"margin-left: 20px;"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
style=
"width:100%"
>
{{ opt }}
</vxe-radio>
</vxe-radio-group>
</span>
<span
v-else-if=
"item.type === 'radio-group-extraFields'"
class=
"radio-group"
>
<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[getFieldKey(row.code, item.field)] === opt.label"
>
<span>
{{
opt
.
extraLabel
}}
</span>
<vxe-input
v-model=
"formData[getFieldKey(row.code, opt.extraField)]"
class=
"extra-input"
></vxe-input>
</
template
>
<
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[getFieldKey(row.code, ccopt.field)]"
class=
"extra-input"
></vxe-input>
</
template
>
</template>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight || isRadioDisabled(row.code, item)"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-radio>
</vxe-radio-group>
</span>
<!-- 多选组(带其他选项) -->
<div
v-else-if=
"item.type === 'checkbox-group'"
>
<div
style=
"width:400px;margin-left:30px"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
<span
class=
"checkbox-group"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-checkbox>
<
template
v-if=
"item.otherOption"
>
<vxe-checkbox
label=
"其他"
>
<
template
v-if=
"item.otherField"
>
<div
style=
"width:100%"
>
<vxe-checkbox
label=
"其他"
:disabled=
"!item.hasRight"
>
其他:
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
class=
"extra-input"
:disabled=
"!(formData[row.code+'_'+item.field]?.indexOf('其他')>-1)"
></vxe-input>
</vxe-checkbox>
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
:disabled=
"!isOtherFieldEnabled(row.code, item) || !item.hasRight"
style=
"width: 50%"
@
blur=
"handleInputBlur(row.code, item.otherField, item.matchedFormula?.formula)"
/>
</div>
</
template
>
</vxe-checkbox-group>
<!-- 其他选项 -->
</span>
</div>
<!-- 多选带输入框 -->
<div
v-else-if=
"item.type === 'checkboxAndInput'"
>
<span
style=
"margin-left:20px"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
:disabled=
"!item.hasRight"
style=
"margin-left: 0px;display: block;"
>
{{ opt }}
<vxe-input
type=
"number"
v-model=
"formData[getFieldKey(row.code, item.optionItemField[optIndex])]"
:disabled=
"!item.hasRight"
style=
"width: 100px;margin: 0px;"
@
blur=
"handleInputBlur(row.code, item.optionItemField[optIndex], item.matchedFormula?.formula)"
/>
<span
class=
"unit"
>
{{ item.optionItemUint }}
</span>
</vxe-checkbox>
</vxe-checkbox-group>
</span>
</div>
<
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
>
<div
class=
"input-wrapper"
>
<vxe-input
:disabled=
"!item.hasRight || isInputDisabled(row.code, item)"
:type=
"item.type"
v-model=
"formData[getFieldKey(row.code, item.field)]"
size=
"mini"
class=
"table-input"
:style=
"item.style"
@
blur=
"handleInputBlur(row.code, item.field, item.matchedFormula?.formula)"
/>
<span
v-if=
"item.unit"
class=
"unit"
>
{{
item
.
unit
}}
</span>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
<!-- 帮助图标 -->
<span
v-if=
"
showHelpIcon(row.code, item.field, item)"
v-if=
"
item.hasValidFormula"
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=
"点击查看错误详情"
>
<i
class=
"vxe-icon-error"
>
✗
</i>
</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"
...
...
@@ -249,6 +256,15 @@
</span>
</
template
>
</div>
<!-- 校验规则提示 -->
<div
v-if=
"showTooltip && hoveredKey === getFieldKey(row.code, item.field)"
class=
"tooltip"
@
click
.
stop
>
{{ item.matchedFormula?.des || '暂无校验规则' }}
</div>
</div>
</template>
</template>
...
...
@@ -256,34 +272,75 @@
</div>
</template>
</vxe-column>
<vxe-column
field=
"remarks"
title=
"备注"
width=
"100"
>
<!-- 备注列 -->
<vxe-column
field=
"remarks"
title=
"备注"
width=
"60"
>
<
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)]"
/>
</
template
>
</vxe-column>
</vxe-table>
<!-- 历史填报检查组件 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
closeDrawer=
"closeHistoryDrawer"
/>
<!-- 校验抽屉 -->
<ValidationDrawer
ref=
"validationDrawerRef"
v-model=
"drawerVisible"
:tableFormData=
"tableFormData"
@
validationResultClick=
"handleValidationResultClick"
/>
<!-- 历史填报抽屉 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
closeDrawer=
"closeHistoryDrawer"
/>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
reactive
,
onMounted
,
computed
}
from
'vue'
import
{
VxeUI
,
VxeTableInstance
}
from
'vxe-table'
import
'vxe-table/lib/style.css'
import
{
useRoute
}
from
'vue-router'
// 组件导入
import
MultiColumnTable
from
'../tableComponents/MultiColumnTable.vue'
import
AttachTable
from
'../tableComponents/AttachTable.vue'
import
HistoryFillCheck
from
'./check/historyFillCheck.vue'
import
MyVxeTable1
from
'../tableComponents/MyVxeTable.vue'
import
HistoryFillCheck
from
'./check/HistoryFillCheck.vue'
import
ValidationDrawer
from
'./check/ValidationDrawer.vue'
// 数据导入
import
{
tableFormData
}
from
'../../data/tb10.data'
import
{
ref
,
reactive
,
nextTick
,
onMounted
}
from
'vue'
import
{
VxeUI
,
VxeTablePropTypes
}
from
'vxe-table'
import
{
batchSaveOrUpdateBeforeDelete
,
queryRecord
}
from
'../../record/BaosongTaskRecord.api'
import
{
queryAllTplItemForUser
,
findUserRightForTplItem
}
from
'../../alloc/BaosongTaskAlloc.api'
// API 导入
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'
interface
FormData
{
// 类型定义
interface
QueryParam
{
taskId
:
number
taskName
:
string
tplId
:
number
tplName
:
string
tplCode
:
string
comfrom
:
string
}
interface
FormDataItem
{
id
:
number
|
null
taskid
:
number
tplid
:
number
...
...
@@ -294,18 +351,65 @@ interface FormData {
rind
:
number
}
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
}
interface
ContentItem
{
type
:
string
field
:
string
value
?:
any
hasRight
?:
boolean
hasValidFormula
?:
boolean
matchedFormula
?:
FormulaItem
relatedFiled
?:
string
otherField
?:
string
optionItemField
?:
string
[]
[
key
:
string
]:
any
}
interface
TableRow
{
code
:
string
project
:
string
type
?:
string
content
:
ContentItem
[]
|
any
remarks
:
{
rows
:
number
;
field
:
string
}
hasRight
?:
boolean
[
key
:
string
]:
any
}
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
}
interface
TplItemMapValue
{
pid
:
number
itemid
:
number
formTp
:
string
code
:
string
}
// 路由和组件引用
const
route
=
useRoute
()
const
tableRef
=
ref
()
const
tplItemMap
=
ref
<
Record
<
string
,
any
>>
({})
const
historyFillCheckRef
=
ref
<
any
>
(
null
)
const
tableRef
=
ref
<
VxeTableInstance
>
()
const
historyFillCheckRef
=
ref
()
const
validationDrawerRef
=
ref
()
const
loading
=
ref
(
false
)
const
formData
=
reactive
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormData
[]
>
([])
const
childMultiTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childAttachTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
queryParam
=
ref
({
// 响应式状态
const
loading
=
ref
(
true
)
const
historyDrawerVisible
=
ref
(
false
)
const
drawerVisible
=
ref
(
false
)
const
showTooltip
=
ref
(
false
)
const
hoveredKey
=
ref
(
''
)
const
showErrorTooltip
=
ref
(
false
)
const
hoveredErrorKey
=
ref
(
''
)
// 数据状态
const
queryParam
=
ref
<
QueryParam
>
({
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
...
...
@@ -314,483 +418,584 @@ const queryParam = ref({
comfrom
:
''
})
// 权限相关状态
const
formData
=
reactive
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormDataItem
[]
>
([])
const
inputErrors
=
ref
<
Record
<
string
,
InputError
>>
({})
const
userAllocItems
=
ref
<
string
[]
>
([])
const
validFormula
=
ref
<
any
[]
>
([])
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
,
any
>>
({})
const
isInitialized
=
ref
(
false
)
const
validFormula
=
ref
<
FormulaItem
[]
>
([])
const
tplItemMap
=
ref
<
Record
<
string
,
TplItemMapValue
>>
({})
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
// 子组件引用
const
childMultiTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childAttachTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childMyVexTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
// 计算属性
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
))
// 工具函数
const
getFieldKey
=
(
rowCode
:
string
,
field
:
string
):
string
=>
{
return
`
${
rowCode
}
_
${
field
}
`
}
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
const
isRadioDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
const
isInputDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
const
isOtherFieldEnabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
const
checkboxKey
=
getFieldKey
(
rowCode
,
item
.
field
)
const
checkboxValue
=
formData
[
checkboxKey
]
||
[]
return
checkboxValue
.
includes
(
'其他'
)
}
// 初始化
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
)
}
if
(
route
.
query
.
comfrom
)
{
queryParam
.
value
.
comfrom
=
String
(
route
.
query
.
comfrom
)
await
initPage
()
})
const
initPage
=
async
()
=>
{
try
{
loading
.
value
=
true
await
initQueryParams
()
await
Promise
.
all
([
loadUserRights
(),
loadValidationFormulas
(),
loadTemplateItems
()
])
await
setFormItemRight
()
await
loadTableData
()
}
catch
(
error
:
any
)
{
showErrorMessage
(
`初始化页面失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
// 设置查询参数
setQueryParams
();
const
initQueryParams
=
()
=>
{
const
{
taskId
,
taskName
,
tplId
,
tplName
,
tplCode
,
comfrom
}
=
route
.
query
// 获取权限和验证公式
try
{
if
(
taskId
)
queryParam
.
value
.
taskId
=
Number
(
taskId
)
if
(
taskName
)
queryParam
.
value
.
taskName
=
String
(
taskName
)
if
(
tplId
)
queryParam
.
value
.
tplId
=
Number
(
tplId
)
if
(
tplName
)
queryParam
.
value
.
tplName
=
String
(
tplName
)
if
(
tplCode
)
queryParam
.
value
.
tplCode
=
String
(
tplCode
)
if
(
comfrom
)
queryParam
.
value
.
comfrom
=
String
(
comfrom
)
}
const
loadUserRights
=
async
()
=>
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
})
}
const
loadValidationFormulas
=
async
()
=>
{
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
,
tplid
:
queryParam
.
value
.
tplId
})
}
catch
(
error
)
{
console
.
error
(
'获取权限或验证公式失败:'
,
error
)
}
}
await
setTplItemMap
()
setFormItemRight
()
await
setData
()
const
loadTemplateItems
=
async
()
=>
{
const
items
=
await
allTplItems
({
tplid
:
queryParam
.
value
.
tplId
})
isInitialized
.
value
=
true
tplItemMap
.
value
=
items
.
reduce
((
acc
:
Record
<
string
,
TplItemMapValue
>
,
item
)
=>
{
const
key
=
`
${
item
.
pcode
}
_
${
item
.
xmlcode
}
`
acc
[
key
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
}
return
acc
},
{})
}
setTimeout
(()
=>
{
refreshHelpIcons
()
},
300
)
})
const
setFormItemRight
=
()
=>
{
tableFormData
.
forEach
((
row
:
TableRow
)
=>
{
if
(
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
row
.
content
.
forEach
((
item
:
ContentItem
)
=>
{
if
(
item
.
field
)
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
item
.
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
const
getFieldKey
=
(
pcode
:
string
|
undefined
,
field
:
string
):
string
=>
{
return
pcode
?
`
${
pcode
}
_
${
field
}
`
:
field
if
(
item
.
hasRight
)
{
const
{
formula
}
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formula
item
.
matchedFormula
=
formula
}
}
})
}
})
}
// 保存相关
const
saveBatch
=
async
()
=>
{
try
{
loading
.
value
=
true
formValues
.
value
=
[]
if
(
!
await
validateForm
())
return
await
prepareFormData
()
// Process main form data
for
(
const
strKey
in
formData
)
{
const
valData
=
formData
[
strKey
]
if
(
valData
)
{
const
[
pcode
,
code
]
=
strKey
.
split
(
'_'
)
await
setFormValues
(
pcode
,
code
,
valData
,
1
)
if
(
formValues
.
value
.
length
===
0
)
{
showWarningMessage
(
'没有需要保存的数据'
)
return
}
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
showSuccessMessage
(
'保存成功'
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`保存失败:
${
error
.
message
}
`
)
}
}
const
validateForm
=
async
():
Promise
<
boolean
>
=>
{
const
$table
=
tableRef
.
value
if
(
!
$table
)
return
true
const
checkResult
=
$table
.
validate
()
if
(
!
checkResult
)
{
showErrorMessage
(
'表单验证失败,请检查填写内容'
)
return
false
}
return
true
}
await
getAttachTableFormData
()
await
getAllMultiTableFormData
()
const
prepareFormData
=
async
()
=>
{
formValues
.
value
=
[]
if
(
formValues
.
value
.
length
>
0
)
{
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
VxeUI
.
modal
.
message
({
content
:
'保存成功'
,
status
:
'success'
})
}
else
{
VxeUI
.
modal
.
message
({
content
:
'没有需要保存的数据'
,
status
:
'warning'
}
)
// 处理主表单数据
for
(
const
[
key
,
value
]
of
Object
.
entries
(
formData
))
{
if
(
value
!==
undefined
&&
value
!==
null
&&
value
!==
''
)
{
const
[
pcode
,
code
]
=
key
.
split
(
'_'
)
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
catch
(
error
)
{
console
.
error
(
'保存失败:'
,
error
)
VxeUI
.
modal
.
message
({
content
:
`保存失败:
${
error
instanceof
Error
?
error
.
message
:
String
(
error
)}
`
,
status
:
'error'
})
}
finally
{
loading
.
value
=
false
}
// 处理子表格数据
await
Promise
.
all
([
collectAttachTableData
(),
collectMultiTableData
(),
collectVxeTableData
()
])
}
const
setMultiColumnTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
{
childMultiTableRefs
.
value
[
code
]
=
el
const
addFormValue
=
async
(
pcode
:
string
,
code
:
string
,
value
:
any
,
rind
:
number
)
=>
{
if
(
!
value
)
return
const
stringValue
=
Array
.
isArray
(
value
)
?
value
.
join
(
','
)
:
String
(
value
)
const
key
=
getFieldKey
(
pcode
,
code
)
const
item
=
tplItemMap
.
value
[
key
]
if
(
!
item
)
return
const
formItem
:
FormDataItem
=
{
id
:
null
,
rind
,
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
item
.
itemid
,
itempid
:
item
.
pid
,
content
:
stringValue
}
formValues
.
value
.
push
(
formItem
)
}
const
setAttachTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
{
childAttachTableRefs
.
value
[
code
]
=
el
// 数据收集函数
const
collectAttachTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childAttachTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
await
child
.
getFormData
()
for
(
const
[
code
,
value
]
of
Object
.
entries
(
datas
))
{
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
}
}
const
getAllMultiTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childMultiTableRefs
.
value
)
{
const
child
=
childMultiTableRefs
.
value
[
pcode
]
const
collectMultiTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMultiTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
child
.
getFormData
()
const
datas
=
await
child
.
getFormData
()
let
rind
=
0
for
(
const
obj
of
datas
)
{
rind
++
Object
.
keys
(
obj
).
forEach
(
code
=>
{
setFormValues
(
pcode
,
code
,
obj
[
code
]
,
rind
)
}
)
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
}
}
}
}
const
getAttachTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childAttachTableRefs
.
value
)
{
const
child
=
childAttachTableRefs
.
value
[
pcode
]
const
collectVxeTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMyVexTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
child
.
getFormData
()
for
(
const
code
in
datas
)
{
await
setFormValues
(
pcode
,
code
,
datas
[
code
],
1
)
const
formData
=
await
child
.
getFormData
()
let
rind
=
0
// 表格数据
for
(
const
obj
of
formData
.
datas
)
{
rind
++
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
}
if
(
formData
.
footer
)
{
for
(
const
[
code
,
value
]
of
Object
.
entries
(
formData
.
footer
))
{
await
addFormValue
(
formData
.
footerPcode
,
code
,
value
,
1
)
}
}
}
}
}
const
setFormValues
=
async
(
pcode
:
string
,
code
:
string
,
valData
:
any
,
rind
:
number
)
=>
{
if
(
!
valData
)
return
if
(
Array
.
isArray
(
valData
)
&&
valData
.
length
==
0
)
return
// 数据加载
const
loadTableData
=
async
()
=>
{
try
{
loading
.
value
=
true
const
vals
=
Array
.
isArray
(
valData
)
?
valData
.
join
(
','
)
:
valData
const
strKey
=
`
${
pcode
}
_
${
code
}
`
const
item
=
tplItemMap
.
value
[
strKey
]
const
{
pid
,
itemid
}
=
item
??
{}
// 清空现有数据
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
])
const
tempForm
:
FormData
=
{
id
:
null
,
rind
:
rind
,
const
recordData
=
await
queryRecord
({
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
itemid
,
itempid
:
pid
,
content
:
vals
tplid
:
queryParam
.
value
.
tplId
})
// 转换记录数据为便于查找的结构
const
valueMap
=
recordData
.
reduce
((
acc
:
Record
<
string
,
string
>
,
data
)
=>
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
acc
[
key
]
=
data
.
content
return
acc
},
{})
// 填充表格数据
await
fillTableData
(
valueMap
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`加载数据失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
formValues
.
value
.
push
(
tempForm
)
const
fillTableData
=
async
(
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
row
of
tableFormData
)
{
if
(
!
row
.
type
&&
row
.
content
)
{
await
fillSimpleFields
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'AttachTable'
&&
row
.
datas
)
{
await
fillAttachTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'MultiColumnTable'
&&
row
.
content
)
{
await
fillMultiColumnTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'VxeTable'
&&
row
.
columns
)
{
await
fillVxeTable
(
row
,
valueMap
)
}
}
}
async
function
setData
()
{
try
{
loading
.
value
=
true
;
const
taskId
=
queryParam
.
value
.
taskId
;
const
tplid
=
queryParam
.
value
.
tplId
;
const
fillSimpleFields
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
if
(
!
row
.
content
||
!
Array
.
isArray
(
row
.
content
))
return
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
]);
const
{
pid
,
itemid
,
formTp
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
await
setTplItemMap
();
const
recordData
=
await
queryRecord
({
taskid
:
taskId
,
tplid
:
tplid
});
if
(
dataVal
===
undefined
)
continue
const
valueObj
=
{};
for
(
const
data
of
recordData
)
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
;
valueObj
[
key
]
=
data
.
content
;
if
(
formTp
===
'checkbox'
)
{
formData
[
key
]
=
dataVal
.
split
(
','
).
filter
(
Boolean
)
}
else
{
formData
[
key
]
=
dataVal
}
const
shouldSkip
=
(
key
)
=>
{
const
refsToCheck
=
[
childMultiTableRefs
.
value
,
childAttachTableRefs
.
value
,
];
for
(
const
refs
of
refsToCheck
)
{
for
(
const
pcode
of
Object
.
keys
(
refs
))
{
if
(
key
.
startsWith
(
pcode
+
'_'
))
{
return
true
;
// 处理特殊字段类型
if
(
item
.
type
===
'checkboxAndInput'
&&
item
.
optionItemField
)
{
await
fillCheckboxInputFields
(
row
,
item
,
valueMap
)
}
if
(
item
.
otherField
)
{
await
fillOtherField
(
row
,
item
,
valueMap
)
}
}
return
false
;
};
}
for
(
const
strKey
of
Object
.
keys
(
tplItemMap
.
value
))
{
if
(
shouldSkip
(
strKey
))
{
continue
;
const
fillCheckboxInputFields
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
field
of
item
.
optionItemField
||
[])
{
const
key
=
getFieldKey
(
row
.
code
,
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
const
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
if
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
const
item
=
tplItemMap
.
value
[
strKey
];
if
(
!
item
)
continue
;
const
{
pid
,
itemid
,
formTp
}
=
item
;
const
dataVal
=
valueObj
[
`
${
pid
}
_
${
itemid
}
_1`
];
if
(
!
dataVal
)
continue
;
if
(
formTp
===
'checkbox'
)
{
formData
[
strKey
]
=
dataVal
?.
split
(
","
)
||
[];
}
else
{
formData
[
strKey
]
=
dataVal
||
""
;
}
}
const
fillOtherField
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
otherField
!
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
return
const
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
if
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
}
for
(
const
pcode
of
Object
.
keys
(
childAttachTableRefs
.
value
))
{
const
child
=
childAttachTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
(
key
=>
key
.
startsWith
(
pcode
+
'_'
)
);
const
fillAttachTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childAttachTableRefs
.
value
[
row
.
code
]
if
(
!
child
)
return
const
attachTableData
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
const
tableData
:
Record
<
string
,
any
>
=
{}
const
{
pid
,
itemid
,
formTp
,
code
}
=
tmpData
;
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
;
for
(
const
data
of
row
.
datas
||
[])
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
data
))
{
if
(
typeof
value
===
'object'
&&
value
.
field
)
{
const
fieldKey
=
getFieldKey
(
row
.
code
,
value
.
field
)
const
tplItem
=
tplItemMap
.
value
[
fieldKey
]
if
(
!
tplItem
)
continue
if
(
formTp
===
'checkbox'
)
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]?.
split
(
","
)
||
[];
}
else
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]
||
""
;
const
{
pid
,
itemid
,
formTp
,
code
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
if
(
dataVal
)
{
tableData
[
code
]
=
formTp
===
'checkbox'
?
dataVal
.
split
(
','
)
:
dataVal
}
}
}
child
.
setFormData
(
attachTableData
);
}
for
(
const
pcode
of
Object
.
keys
(
childMultiTableRefs
.
value
))
{
const
child
=
childMultiTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
(
key
=>
key
.
startsWith
(
pcode
+
'_'
)
);
child
.
setFormData
(
tableData
)
}
const
rowsMap
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
const
fillMultiColumnTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMultiTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
content
))
return
const
{
pid
,
itemid
,
code
}
=
tmpData
;
const
valKeys
=
Object
.
keys
(
valueObj
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
);
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
for
(
const
valKey
of
valKeys
)
{
const
parts
=
valKey
.
split
(
'_'
);
const
rind
=
parts
[
2
];
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{};
}
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
rowsMap
[
rind
][
code
]
=
valueObj
[
valKey
];
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{}
}
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
const
tableDatas
=
Object
.
values
(
rowsMap
);
nextTick
();
child
.
setFormData
(
tableDatas
);
}
}
catch
(
error
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
});
}
finally
{
loading
.
value
=
false
;
}
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
const
fillVxeTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMyVexTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
columns
))
return
async
function
setTplItemMap
()
{
try
{
const
taskId
=
queryParam
.
value
.
taskId
const
tplid
=
queryParam
.
value
.
tplId
const
tplItem
=
await
allTplItems
({
tplid
:
tplid
})
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
tplItemMap
.
value
=
tplItem
.
reduce
((
acc
,
item
)
=>
{
const
strKey
=
`
${
item
.
pcode
}
_
${
item
.
xmlcode
}
`
acc
[
strKey
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
for
(
const
column
of
row
.
columns
)
{
if
(
!
column
.
field
)
continue
const
key
=
getFieldKey
(
row
.
code
,
column
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
||
Object
.
keys
(
tplItem
).
length
===
0
)
continue
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{}
}
return
acc
},
{}
as
Record
<
string
,
any
>
)
}
catch
(
error
)
{
console
.
error
(
'加载数据失败:'
,
error
)
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
instanceof
Error
?
error
.
message
:
String
(
error
)}
`
,
status
:
'error'
})
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
}
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
// 校验
数据
const
check
Data
=
()
=>
{
// 校验
相关
const
validate
Data
=
()
=>
{
drawerVisible
.
value
=
true
validationDrawerRef
.
value
.
setValidateData
(
validFormula
.
value
,
formData
)
validationDrawerRef
.
value
.
setValidateData
(
validFormula
.
value
,
formData
);
}
const
handleInputBlur
=
(
rowCode
:
string
,
field
:
string
,
formula
?:
string
)
=>
{
if
(
!
formula
)
return
;
if
(
!
formula
)
return
const
key
=
getFieldKey
(
rowCode
,
field
)
;
const
value
=
formData
[
key
]
;
const
key
=
getFieldKey
(
rowCode
,
field
)
const
value
=
formData
[
key
]
if
(
!
value
||
value
===
''
)
{
delete
inputErrors
.
value
[
key
]
;
return
;
delete
inputErrors
.
value
[
key
]
return
}
validateFieldFormula
(
rowCode
,
field
,
formula
)
;
}
;
validateFieldFormula
(
rowCode
,
field
,
formula
)
}
const
validateFieldFormula
=
(
rowCode
:
string
,
field
:
string
,
formula
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
;
const
key
=
getFieldKey
(
rowCode
,
field
)
try
{
const
expression
=
buildExpression
(
formula
,
rowCode
)
;
const
isValid
=
eval
(
expression
)
;
const
expression
=
buildExpression
(
formula
,
rowCode
)
const
isValid
=
eval
(
expression
)
if
(
!
isValid
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
}
;
}
}
else
{
delete
inputErrors
.
value
[
key
]
;
delete
inputErrors
.
value
[
key
]
}
}
catch
(
error
:
any
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
,
error
:
error
.
message
};
}
};
}
}
const
buildExpression
=
(
formula
:
string
,
rowCode
:
string
):
string
=>
{
const
fieldNames
=
formula
.
match
(
/
\b[
A-Za-z
][
A-Za-z0-9_
]
*
\b
/g
)
||
[]
;
const
fieldNames
=
formula
.
match
(
/
\b[
A-Za-z
][
A-Za-z0-9_
]
*
\b
/g
)
||
[]
const
uniqueFields
=
[...
new
Set
(
fieldNames
.
filter
(
f
=>
!
[
'and'
,
'or'
,
'not'
,
'equal'
,
'less'
,
'greater'
,
'if'
,
'else'
,
'true'
,
'false'
]
.
includes
(
f
.
toLowerCase
())
))]
;
))]
uniqueFields
.
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
)
;
uniqueFields
.
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
)
let
expression
=
formula
;
let
expression
=
formula
for
(
const
fieldName
of
uniqueFields
)
{
const
key
=
getFieldKey
(
rowCode
,
fieldName
);
const
value
=
formData
[
key
];
const
key
=
getFieldKey
(
rowCode
,
fieldName
)
const
value
=
formData
[
key
]
if
(
value
!==
undefined
)
{
// 检查字段类型,如果是日期类型,转换为时间戳进行比较
const
row
=
tableFormData
.
find
((
r
:
TableRow
)
=>
r
.
code
===
rowCode
)
let
processedValue
=
value
if
(
row
&&
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
const
fieldItem
=
row
.
content
.
find
((
c
:
ContentItem
)
=>
c
.
field
===
fieldName
)
if
(
fieldItem
&&
fieldItem
.
type
===
'date'
)
{
// 将日期字符串转换为时间戳(毫秒)
const
date
=
new
Date
(
value
)
if
(
!
isNaN
(
date
.
getTime
()))
{
processedValue
=
date
.
getTime
()
}
}
}
expression
=
expression
.
replace
(
new
RegExp
(
`\\b
${
fieldName
}
\\b`
,
'g'
),
`Number(
${
v
alue
}
)`
)
;
`Number(
${
processedV
alue
}
)`
)
}
}
return
expression
;
}
;
return
expression
}
// 提示工具相关
const
toggleTooltip
=
(
rowCode
:
string
,
field
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
;
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
hoveredKey
.
value
===
key
)
{
showTooltip
.
value
=
false
;
hoveredKey
.
value
=
''
;
showTooltip
.
value
=
false
hoveredKey
.
value
=
''
}
else
{
showTooltip
.
value
=
true
;
hoveredKey
.
value
=
key
;
showTooltip
.
value
=
true
hoveredKey
.
value
=
key
}
}
;
}
const
toggleErrorTooltip
=
(
rowCode
:
string
,
field
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
;
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
hoveredErrorKey
.
value
===
key
)
{
showErrorTooltip
.
value
=
false
;
hoveredErrorKey
.
value
=
''
;
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
}
else
{
showErrorTooltip
.
value
=
true
;
hoveredErrorKey
.
value
=
key
;
showErrorTooltip
.
value
=
true
hoveredErrorKey
.
value
=
key
}
}
;
}
const
closeAllTooltips
=
()
=>
{
showTooltip
.
value
=
false
;
hoveredKey
.
value
=
''
;
showErrorTooltip
.
value
=
false
;
hoveredErrorKey
.
value
=
''
;
};
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
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
VxeUI
.
modal
.
message
({
content
:
message
,
status
})
showTooltip
.
value
=
false
hoveredKey
.
value
=
''
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
}
//
打开历史填报抽屉
//
历史填报
const
handleOpenHistoryDrawer
=
()
=>
{
historyFillCheckRef
.
value
?.
onDrawerShow
(
queryParam
.
value
.
tplId
)
historyDrawerVisible
.
value
=
true
}
// 关闭历史填报抽屉
const
closeHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
false
}
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
)
console
.
log
(
`字段
${
key
}
: hasRight =
${
item
.
hasRight
}
`
)
if
(
item
.
hasRight
)
{
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formulaResult
.
formula
item
.
matchedFormula
=
formulaResult
.
formula
||
null
}
console
.
log
(
`字段
${
key
}
: hasValidFormula =
${
item
.
hasValidFormula
}
`
)
}
else
{
item
.
hasValidFormula
=
false
item
.
matchedFormula
=
null
}
}
})
}
})
// 校验结果处理
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
console
.
log
(
'表单权限设置完成'
)
VxeUI
.
modal
.
message
({
content
:
message
,
status
}
)
}
// 工具函数
const
findEarliestFormulaForField
=
(
formulas
:
FormulaItem
[],
field
:
string
...
...
@@ -811,185 +1016,129 @@ const findEarliestFormulaForField = (
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
)
// 消息提示
const
showSuccessMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'success'
})
}
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
}
}
})
}
})
const
showWarningMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'warning'
})
}
nextTick
((
)
=>
{
})
const
showErrorMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'error'
})
}
const
mergeCells
=
ref
<
VxeTablePropTypes
.
MergeCells
>
([
{
row
:
0
,
col
:
1
,
rowspan
:
1
,
colspan
:
2
},
{
row
:
1
,
col
:
1
,
rowspan
:
1
,
colspan
:
2
},
{
row
:
2
,
col
:
1
,
rowspan
:
1
,
colspan
:
2
},
{
row
:
3
,
col
:
1
,
rowspan
:
6
,
colspan
:
1
},
// 子组件引用设置
const
setMultiColumnTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMultiTableRefs
.
value
[
code
]
=
el
}
const
setAttachTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childAttachTableRefs
.
value
[
code
]
=
el
}
{
row
:
9
,
col
:
1
,
rowspan
:
1
,
colspan
:
2
},
{
row
:
10
,
col
:
1
,
rowspan
:
1
,
colspan
:
2
},
])
const
setMyVxeTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMyVexTableRefs
.
value
[
code
]
=
el
}
</
script
>
<
style
lang=
"less"
scoped
>
// Base table styles
.bank-report-table {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
color: #000;
margin: 5px auto;
width: 90%;
}
.toolbar-info {
margin: 10px;
.info-label {
font-weight: bold;
margin-left: 20px;
&:first-child {
margin-left: 0;
}
}
}
position: relative;
.custom-table {
// Shared table styles
.custom-table, .attachment-table {
width: 100%;
border: 1px solid #000;
border-collapse: collapse;
:deep(.vxe-header--column)
,
:deep(.vxe-body--column)
{
.vxe-header--column
,
.vxe-body--column
{
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
}
}
/* 加载遮罩层样式 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-spinner {
display: flex;
flex-direction: column;
align-items: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
// 表格信息文本
.table-info-text {
font-weight: bold;
}
.loading-text {
margin-top: 10px;
color: #666
;
// 项目名称
.project-name {
font-weight: bold
;
font-size: 14px;
}
// Cell styles
.content-cell {
line-height: 1.8;
padding: 5px;
}
.project-title {
font-weight: bold;
font-size: 14px;
}
.table-input,
.vxe-input {
height: 24px;
line-height: 24px;
min-width: 150px;
margin: 0 2px;
padding: 0 5px;
border: none;
border-bottom: 1px solid #333;
background: transparent;
text-align: center;
}
// 表单控件组
.radio-group, .checkbox-group {
display: inline-block;
margin-left: 10px;
.extra-input {
width: 200px;
margin-left: 5px;
}
.vxe-radio, .vxe-checkbox {
margin-left: 0px;
white-space: nowrap;
line-height: 30px;
padding: 0px 10px;
.extra-fields-container {
margin-left: 40px;
margin-top: 5px;
}
&--label {
font-size: 12px;
}
.vxe-radio-group,
.vxe-checkbox-group {
display: inline-block;
margin-right: 5px;
max-width: 99%;
&--icon {
font-size: 12px;
}
}
}
.radio-group,
.checkbox-group {
// VXE specific overrides
.vxe {
&-radio-group, &-checkbox-group {
display: inline-block;
max-width: 99%;
}
.vxe-radio,
.vxe-checkbox {
margin: 10px;
white-space: nowrap;
}
}
.unit
{
margin-left: 3
px;
color: #666;
&-cell
{
padding: 5
px;
}
}
.embedded-table {
margin: 0;
.vxe-input {
border-bottom: 1px solid #333;
text-align: center;
margin: 5px 3px;
padding: 0;
border: none;
border-top: none;
border-left: none;
border-right: none;
background: transparent;
}
// 输入框包装器
.input-wrapper {
position: relative;
display: inline-block;
}
// 单位文本
.unit {
margin-left: 5px;
font-size: 12px;
}
// 帮助图标
.help-icon {
margin-left: 5px;
cursor: pointer;
...
...
@@ -1004,27 +1153,13 @@ const mergeCells = ref<VxeTablePropTypes.MergeCells>([
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);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
// 错误图标
.error-icon {
margin-left: 5px;
cursor: pointer;
...
...
@@ -1042,6 +1177,7 @@ const mergeCells = ref<VxeTablePropTypes.MergeCells>([
}
}
// 提示框
.tooltip, .error-tooltip {
position: absolute;
top: 100%;
...
...
@@ -1074,15 +1210,54 @@ const mergeCells = ref<VxeTablePropTypes.MergeCells>([
.error-tooltip {
background: #8b0000;
color: #fff;
border: 1px solid #ff4d4f;
&::before {
content: '';
position: absolute;
bottom: 100%;
left:
10
px;
left:
5
px;
border-width: 5px;
border-style: solid;
border-color: transparent transparent #8b0000 transparent;
}
}
// 加载遮罩
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-spinner {
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 14px;
color: #666;
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/baosong/report/components/Tb6.vue
浏览文件 @
12b89e58
<
template
>
<div
class=
"bank-report-table"
style=
"height: 80vh"
@
click=
"closeAllTooltips"
>
<div
class=
"bank-report-table"
style=
"height:80vh"
@
click=
"closeAllTooltips"
>
<!-- 加载遮罩层 -->
<div
v-if=
"loading"
class=
"loading-overlay"
>
<div
class=
"loading-spinner"
>
...
...
@@ -8,6 +12,7 @@
</div>
</div>
<!-- 工具栏 -->
<vxe-toolbar>
<template
#
buttons
>
<div
style=
"margin:10px"
>
...
...
@@ -43,14 +48,7 @@
</
template
>
</vxe-toolbar>
<!-- 校验抽屉 -->
<ValidationDrawer
ref=
"validationDrawerRef"
v-model=
"drawerVisible"
:tableFormData=
"tableFormData"
@
validationResultClick=
"handleValidationResultClick"
/>
<!-- 主表格 -->
<vxe-table
border
ref=
"tableRef"
...
...
@@ -72,15 +70,18 @@
<vxe-column
field=
"content"
title=
"内容"
>
<
template
#
default=
"{ row }"
>
<div
class=
"content-cell"
>
<!-- 多列表格 -->
<template
v-if=
"row.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"row.project"
:records=
"[]"
:fields=
"row.content"
:ref=
"(el) => setMultiColumnTableRef(el, row.code)"
style=
"margin:
0px; padding:
0px"
style=
"margin:
0px;padding:
0px"
/>
</
template
>
<!-- 附件表格 -->
<
template
v-else-if=
"row.type === 'AttachTable'"
>
<AttachTable
:title=
"row.project"
...
...
@@ -109,162 +110,107 @@
style=
"margin:0px;padding:0px"
/>
</
template
>
<!-- 普通字段渲染 -->
<
template
v-else
>
<template
v-for=
"(item, index) in row.content"
:key=
"index"
>
<span
v-if=
"item.type === 'text'"
v-html=
"item.value"
style=
"margin-right: 10px"
></span>
<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
>
</span>
<!-- 文本类型 -->
<span
v-if=
"item.type === 'text'"
v-html=
"item.value"
></span>
<
template
v-else-if=
"item.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"item.project"
:records=
"[]"
:fields=
"item.content"
:ref=
"(el) => setMultiColumnTableRef(el, item.code)"
style=
"margin: 0px; padding: 0px"
/>
</
template
>
<!-- 换行 -->
<span
v-else-if=
"item.type === 'br'"
><br></span>
<
template
v-else-if=
"item.type === 'AttachTable'"
>
<AttachTable
:title=
"item.project"
:records=
"item.datas"
:fields=
"item.content"
:pcode=
"item.code"
:calcSum=
"item.calcSum"
:showFooter=
"item.showFooter"
:ref=
"(el) => setAttachTableRef(el, item.code)"
style=
"margin: 0px; padding: 0px"
/>
<!-- 带缩进的换行 -->
<span
v-else-if=
"item.type === 'brspace'"
>
<br>
<template
v-for=
"i in (item.value || 1)"
:key=
"i"
>
</
template
>
</span>
<!-- 单选组 -->
<span
v-else-if=
"item.type === 'radio-group'"
class=
"radio-group"
>
<vxe-radio-group
v-model=
"formData[row.code + '_' + item.field]"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-radio>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight || isRadioDisabled(row.code, item)"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-radio>
</vxe-radio-group>
</span>
<div
v-else-if=
"item.type === 'radio-group-extraFields'"
class=
"radio-group"
>
<vxe-radio-group
v-model=
"formData[row.code + '_' + item.field]"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt.value"
>
{{ opt.label }}
</vxe-radio>
</vxe-radio-group>
<div
v-if=
"formData[row.code + '_' + item.field]"
class=
"extra-fields"
>
<
template
v-for=
"(option, optIndex) in item.options"
:key=
"optIndex"
>
<template
v-if=
"option.value === formData[row.code + '_' + item.field] && option.extraFields"
>
<template
v-for=
"(ccopt, ccind) in option.extraFields"
:key=
"ccind"
>
<span>
{{
ccopt
.
label
}}
:
</span>
<template
v-if=
"ccopt.formType === 'number'"
>
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.field]"
type=
"number"
style=
"width: 80px"
></vxe-input>
</
template
>
<
template
v-else-if=
"ccopt.formType === 'input'"
>
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.field]"
style=
"width: 250px"
></vxe-input>
</
template
>
<
template
v-else
>
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.field]"
:type=
"ccopt.formType"
style=
"width: 100px"
></vxe-input>
</
template
>
<span
class=
"unit"
>
{{ ccopt.unit }}
</span>
</template>
</template>
</template>
</div>
</div>
<div
v-else-if=
"item.type === 'checkbox-group'"
class=
"checkbox-group"
>
<span
class=
"checkbox-group"
style=
"max-width: 800px; margin-left: 20px"
>
<vxe-checkbox-group
v-model=
"formData[row.code + '_' + item.field]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
style=
"line-height: 30px; margin-left: 0px"
>
<!-- 多选组(带其他选项) -->
<div
v-else-if=
"item.type === 'checkbox-group'"
>
<span
class=
"checkbox-group"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-checkbox>
<
template
v-if=
"item.otherOption"
>
<vxe-checkbox
label=
"其他"
style=
"margin: 0px"
>
其他:
</vxe-checkbox>
<
template
v-if=
"item.otherField"
>
<div
style=
"width:100%"
>
<vxe-checkbox
label=
"其他"
:disabled=
"!item.hasRight"
>
其他:
</vxe-checkbox>
<vxe-input
v-model=
"formData[row.code + '_' + item.otherField]"
:disabled=
"!(formData[row.code + '_' + item.field]?.indexOf('其他') > -1)"
style=
"width: 300px"
>
</vxe-input>
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
:disabled=
"!isOtherFieldEnabled(row.code, item) || !item.hasRight"
style=
"width: 50%"
@
blur=
"handleInputBlur(row.code, item.otherField, item.matchedFormula?.formula)"
/>
</div>
</
template
>
</vxe-checkbox-group>
<!-- 其他选项 -->
</span>
</div>
<!-- 多选带输入框 -->
<div
v-else-if=
"item.type === 'checkboxAndInput'"
>
<span
class=
"checkbox-group"
>
<vxe-checkbox-group
v-model=
"formData[row.code + '_' + item.field]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
style=
"display: block; margin: 0px 0px"
>
<span
style=
"margin-left:20px"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
:disabled=
"!item.hasRight"
style=
"margin-left: 0px;display: block;"
>
{{ opt }}
<vxe-input
type=
"number"
v-model=
"formData[row.code + '_' + item.optionItemField[optIndex]]"
style=
"width: 100px; margin: 0px"
>
</vxe-input>
v-model=
"formData[getFieldKey(row.code, item.optionItemField[optIndex])]"
:disabled=
"!item.hasRight"
style=
"width: 100px;margin: 0px;"
@
blur=
"handleInputBlur(row.code, item.optionItemField[optIndex], item.matchedFormula?.formula)"
/>
<span
class=
"unit"
>
{{ item.optionItemUint }}
</span>
</vxe-checkbox>
</vxe-checkbox-group>
</span>
</div>
<div
v-else-if=
"item.type === 'yesno'"
class=
"yesno-group"
>
<vxe-radio-group
v-model=
"formData[row.code + '_' + item.field]"
>
<vxe-radio
label=
"是"
>
是
</vxe-radio>
<vxe-radio
label=
"否"
>
否
</vxe-radio>
</vxe-radio-group>
<div
v-if=
"formData[row.code + '_' + item.field] === '是'"
class=
"extra-fields"
>
<!-- 处理单个extraField的情况 -->
<
template
v-if=
"item.extraField"
>
<div
style=
"margin-top: 5px"
>
<span>
{{
item
.
extraLabel
||
''
}}
:
</span>
<vxe-input
v-model=
"formData[row.code + '_' + item.extraField]"
style=
"width: 100px"
></vxe-input>
<span
v-if=
"item.unit"
class=
"unit"
>
{{
item
.
unit
}}
</span>
</div>
</
template
>
<!-- 处理extraFields数组的情况 -->
<
template
v-else-if=
"item.extraFields"
>
<template
v-for=
"(ccopt, ccind) in item.extraFields"
:key=
"ccind"
>
<div
style=
"margin-top: 5px"
>
<span>
{{
ccopt
.
label
}}
:
</span>
<template
v-if=
"ccopt.formType === 'input'"
>
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.field]"
style=
"width: 250px"
></vxe-input>
</
template
>
<
template
v-else-if=
"ccopt.formType === 'checkbox'"
>
<vxe-checkbox-group
v-model=
"formData[row.code + '_' + ccopt.field]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
>
{{
opt
}}
</vxe-checkbox>
</vxe-checkbox-group>
<template
v-if=
"ccopt.otherOption"
>
<div
style=
"margin-top: 5px; margin-left: 20px"
>
其他:
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.otherField]"
:disabled=
"
!(
Array.isArray(formData[row.code + '_' + ccopt.field]) &&
formData[row.code + '_' + ccopt.field].indexOf('其他') > -1
)
"
style=
"width: 200px"
>
</vxe-input>
</div>
</
template
>
</template>
<
template
v-else-if=
"ccopt.formType === 'radio'"
>
<vxe-radio-group
v-model=
"formData[row.code + '_' + ccopt.field]"
>
<vxe-radio
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
>
{{
opt
}}
</vxe-radio>
</vxe-radio-group>
<template
v-if=
"ccopt.otherOption && formData[row.code + '_' + ccopt.field] === '其他'"
>
<div
style=
"margin-top: 5px; margin-left: 20px"
>
其他:
<vxe-input
v-model=
"formData[row.code + '_' + ccopt.otherField]"
style=
"width: 200px"
></vxe-input>
</div>
</
template
>
</template>
</div>
</template>
</template>
</div>
</div>
<!-- 普通输入框 -->
<
template
v-else
>
<div
class=
"input-wrapper"
>
<vxe-input
:disabled=
"!item.hasRight || isInputDisabled(row.code, item)"
:type=
"item.type"
v-model=
"formData[getFieldKey(row.code, item.field)]"
size=
"mini"
...
...
@@ -272,14 +218,13 @@
:style=
"item.style"
@
blur=
"handleInputBlur(row.code, item.field, item.matchedFormula?.formula)"
/>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
<!-- 帮助图标 -
修改这里 -
->
<!-- 帮助图标 -->
<span
v-if=
"
showHelpIcon(row.code, item.field, item)
"
v-if=
"
item.hasValidFormula
"
class=
"help-icon"
@
click
.
stop=
"toggleTooltip(row.code, item.field)"
title=
"点击查看校验规则"
>
?
</span>
...
...
@@ -289,7 +234,6 @@
v-if=
"inputErrors[getFieldKey(row.code, item.field)]"
class=
"error-icon"
@
click
.
stop=
"toggleErrorTooltip(row.code, item.field)"
title=
"点击查看错误详情"
>
<i
class=
"vxe-icon-error"
>
✗
</i>
</span>
...
...
@@ -315,7 +259,7 @@
<!-- 校验规则提示 -->
<div
v-if=
"showTooltip && hoveredKey === getFieldKey(row.code, item.field)
&& item.matchedFormula
"
v-if=
"showTooltip && hoveredKey === getFieldKey(row.code, item.field)"
class=
"tooltip"
@
click
.
stop
>
...
...
@@ -328,812 +272,841 @@
</div>
</template>
</vxe-column>
</vxe-table>
<!-- 历史填报检查组件 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
close-drawer=
"closeHistoryDrawer"
/>
<!-- 原有检查组件 -->
<CheckTbData
ref=
"refCheckTbData"
></CheckTbData>
<!-- 校验抽屉 -->
<ValidationDrawer
ref=
"validationDrawerRef"
v-model=
"drawerVisible"
:tableFormData=
"tableFormData"
@
validationResultClick=
"handleValidationResultClick"
/>
<!-- 历史填报抽屉 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
closeDrawer=
"closeHistoryDrawer"
/>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
MultiColumnTable
from
'../tableComponents/MultiColumnTable.vue'
;
import
AttachTable
from
'../tableComponents/AttachTable.vue'
;
import
MyVxeTable1
from
'../tableComponents/MyVxeTable.vue'
;
import
CheckTbData
from
'./CheckTbData.vue'
;
import
HistoryFillCheck
from
'./check/historyFillCheck.vue'
;
import
ValidationDrawer
from
'./check/ValidationDrawer.vue'
;
import
{
tableFormData
}
from
'../../data/tb6.data'
;
import
{
ref
,
reactive
,
nextTick
,
onMounted
,
toRaw
,
computed
,
watch
}
from
'vue'
;
import
{
VxeUI
,
VxeToolbarInstance
,
VxeToolbarPropTypes
}
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'
;
const
refCheckTbData
=
ref
();
const
historyFillCheckRef
=
ref
<
any
>
(
null
);
const
validationDrawerRef
=
ref
();
const
route
=
useRoute
();
const
tableRef
=
ref
();
const
tplItemMap
=
ref
({});
const
queryParam
=
ref
({
import
{
ref
,
reactive
,
onMounted
,
computed
}
from
'vue'
import
{
VxeUI
,
VxeTableInstance
}
from
'vxe-table'
import
'vxe-table/lib/style.css'
import
{
useRoute
}
from
'vue-router'
// 组件导入
import
MultiColumnTable
from
'../tableComponents/MultiColumnTable.vue'
import
AttachTable
from
'../tableComponents/AttachTable.vue'
import
MyVxeTable1
from
'../tableComponents/MyVxeTable.vue'
import
HistoryFillCheck
from
'./check/HistoryFillCheck.vue'
import
ValidationDrawer
from
'./check/ValidationDrawer.vue'
// 数据导入
import
{
tableFormData
}
from
'../../data/tb6.data'
// API 导入
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'
// 类型定义
interface
QueryParam
{
taskId
:
number
taskName
:
string
tplId
:
number
tplName
:
string
tplCode
:
string
comfrom
:
string
}
interface
FormDataItem
{
id
:
number
|
null
taskid
:
number
tplid
:
number
itemid
?:
number
itempid
?:
number
content
:
string
tplcode
:
string
rind
:
number
}
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
}
interface
ContentItem
{
type
:
string
field
:
string
value
?:
any
hasRight
?:
boolean
hasValidFormula
?:
boolean
matchedFormula
?:
FormulaItem
relatedFiled
?:
string
otherField
?:
string
optionItemField
?:
string
[]
[
key
:
string
]:
any
}
interface
TableRow
{
code
:
string
project
:
string
type
?:
string
content
:
ContentItem
[]
|
any
remarks
:
{
rows
:
number
;
field
:
string
}
hasRight
?:
boolean
[
key
:
string
]:
any
}
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
}
interface
TplItemMapValue
{
pid
:
number
itemid
:
number
formTp
:
string
code
:
string
}
// 路由和组件引用
const
route
=
useRoute
()
const
tableRef
=
ref
<
VxeTableInstance
>
()
const
historyFillCheckRef
=
ref
()
const
validationDrawerRef
=
ref
()
// 响应式状态
const
loading
=
ref
(
true
)
const
historyDrawerVisible
=
ref
(
false
)
const
drawerVisible
=
ref
(
false
)
const
showTooltip
=
ref
(
false
)
const
hoveredKey
=
ref
(
''
)
const
showErrorTooltip
=
ref
(
false
)
const
hoveredErrorKey
=
ref
(
''
)
// 数据状态
const
queryParam
=
ref
<
QueryParam
>
({
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
tplName
:
''
,
tplCode
:
''
,
comfrom
:
''
});
})
const
formData
=
reactive
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormDataItem
[]
>
([])
const
inputErrors
=
ref
<
Record
<
string
,
InputError
>>
({})
const
userAllocItems
=
ref
<
string
[]
>
([])
const
validFormula
=
ref
<
FormulaItem
[]
>
([])
const
tplItemMap
=
ref
<
Record
<
string
,
TplItemMapValue
>>
({})
// 子组件引用
const
childMultiTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childAttachTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childMyVexTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
// 计算属性
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
))
// 工具函数
const
getFieldKey
=
(
rowCode
:
string
,
field
:
string
):
string
=>
{
return
`
${
rowCode
}
_
${
field
}
`
}
const
isRadioDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
const
isInputDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
const
isOtherFieldEnabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
const
checkboxKey
=
getFieldKey
(
rowCode
,
item
.
field
)
const
checkboxValue
=
formData
[
checkboxKey
]
||
[]
return
checkboxValue
.
includes
(
'其他'
)
}
// 初始化
onMounted
(
async
()
=>
{
await
initPage
()
})
const
initPage
=
async
()
=>
{
try
{
loading
.
value
=
true
await
initQueryParams
()
await
Promise
.
all
([
loadUserRights
(),
loadValidationFormulas
(),
loadTemplateItems
()
])
await
setFormItemRight
()
await
loadTableData
()
}
catch
(
error
:
any
)
{
showErrorMessage
(
`初始化页面失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
// 权限相关状态
const
userAllocItems
=
ref
<
string
[]
>
([]);
const
validFormula
=
ref
<
any
[]
>
([]);
const
drawerVisible
=
ref
(
false
);
const
historyDrawerVisible
=
ref
(
false
);
const
showTooltip
=
ref
(
false
);
const
hoveredKey
=
ref
(
''
);
const
showErrorTooltip
=
ref
(
false
);
const
hoveredErrorKey
=
ref
(
''
);
const
isInitialized
=
ref
(
false
);
const
initQueryParams
=
()
=>
{
const
{
taskId
,
taskName
,
tplId
,
tplName
,
tplCode
,
comfrom
}
=
route
.
query
interface
FormData
{
id
:
number
|
null
;
taskid
:
number
;
tplid
:
number
;
itemid
?:
number
;
itempid
?:
number
;
content
:
string
;
tplcode
:
string
;
rind
:
number
;
}
if
(
taskId
)
queryParam
.
value
.
taskId
=
Number
(
taskId
)
if
(
taskName
)
queryParam
.
value
.
taskName
=
String
(
taskName
)
if
(
tplId
)
queryParam
.
value
.
tplId
=
Number
(
tplId
)
if
(
tplName
)
queryParam
.
value
.
tplName
=
String
(
tplName
)
if
(
tplCode
)
queryParam
.
value
.
tplCode
=
String
(
tplCode
)
if
(
comfrom
)
queryParam
.
value
.
comfrom
=
String
(
comfrom
)
}
interface
FormulaItem
{
formula
:
string
;
des
:
string
;
[
key
:
string
]:
any
;
}
const
loadUserRights
=
async
()
=>
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
})
}
interface
InputError
{
message
:
string
;
formula
:
string
;
error
?:
string
;
}
const
loadValidationFormulas
=
async
()
=>
{
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
})
}
const
loadTemplateItems
=
async
()
=>
{
const
items
=
await
allTplItems
({
tplid
:
queryParam
.
value
.
tplId
})
tplItemMap
.
value
=
items
.
reduce
((
acc
:
Record
<
string
,
TplItemMapValue
>
,
item
)
=>
{
const
key
=
`
${
item
.
pcode
}
_
${
item
.
xmlcode
}
`
acc
[
key
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
}
return
acc
},
{})
}
const
setFormItemRight
=
()
=>
{
tableFormData
.
forEach
((
row
:
TableRow
)
=>
{
if
(
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
row
.
content
.
forEach
((
item
:
ContentItem
)
=>
{
if
(
item
.
field
)
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
item
.
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
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
(
item
.
hasRight
)
{
const
{
formula
}
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formula
item
.
matchedFormula
=
formula
}
if
(
route
.
query
.
tplCode
)
{
queryParam
.
value
.
tplCode
=
String
(
route
.
query
.
tplCode
);
}
if
(
route
.
query
.
comfrom
)
{
queryParam
.
value
.
comfrom
=
String
(
route
.
query
.
comfrom
);
})
}
})
}
// 初始化数据
await
refCheckTbData
.
value
.
initData
();
// 获取权限和验证公式
// 保存相关
const
saveBatch
=
async
()
=>
{
try
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
,
});
if
(
!
await
validateForm
())
return
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
,
});
await
prepareFormData
()
console
.
log
(
'获取到的权限:'
,
userAllocItems
.
value
.
length
);
console
.
log
(
'获取到的公式:'
,
validFormula
.
value
.
length
);
}
catch
(
error
)
{
console
.
error
(
'获取权限或验证公式失败:'
,
error
);
if
(
formValues
.
value
.
length
===
0
)
{
showWarningMessage
(
'没有需要保存的数据'
)
return
}
await
setTplItemMap
();
await
setFormItemRight
();
await
setData
();
// 标记初始化完成
isInitialized
.
value
=
true
;
// 延迟刷新一次,确保问号图标显示
setTimeout
(()
=>
{
refreshHelpIcons
();
},
300
);
});
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
showSuccessMessage
(
'保存成功'
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`保存失败:
${
error
.
message
}
`
)
}
}
const
loading
=
ref
(
false
);
const
formData
=
reactive
({});
const
formValues
=
ref
<
FormData
[]
>
([]);
const
inputErrors
=
ref
<
Record
<
string
,
InputError
>>
({});
const
validateForm
=
async
():
Promise
<
boolean
>
=>
{
const
$table
=
tableRef
.
value
if
(
!
$table
)
return
true
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
));
const
checkResult
=
$table
.
validate
()
if
(
!
checkResult
)
{
showErrorMessage
(
'表单验证失败,请检查填写内容'
)
return
false
}
return
true
}
const
getFieldKey
=
(
rowCode
:
string
,
field
:
string
):
string
=>
{
return
`
${
rowCode
}
_
${
field
}
`
;
};
const
prepareFormData
=
async
()
=>
{
formValues
.
value
=
[]
// 添加帮助图标显示条件计算
const
showHelpIcon
=
(
rowCode
:
string
,
field
:
string
,
item
:
any
):
boolean
=>
{
// 如果已经有 hasValidFormula 属性,直接使用
if
(
item
.
hasValidFormula
!==
undefined
)
{
return
item
.
hasValidFormula
===
true
;
// 处理主表单数据
for
(
const
[
key
,
value
]
of
Object
.
entries
(
formData
))
{
if
(
value
!==
undefined
&&
value
!==
null
&&
value
!==
''
)
{
const
[
pcode
,
code
]
=
key
.
split
(
'_'
)
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
// 否则重新计算
const
key
=
getFieldKey
(
rowCode
,
field
);
const
hasRight
=
userAllocItems
.
value
.
includes
(
key
);
// 处理子表格数据
await
Promise
.
all
([
collectAttachTableData
(),
collectMultiTableData
(),
collectVxeTableData
()
])
}
if
(
!
hasRight
)
return
false
;
const
addFormValue
=
async
(
pcode
:
string
,
code
:
string
,
value
:
any
,
rind
:
number
)
=>
{
if
(
!
value
)
return
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
field
);
return
!!
formulaResult
.
formula
;
};
const
stringValue
=
Array
.
isArray
(
value
)
?
value
.
join
(
','
)
:
String
(
value
)
const
key
=
getFieldKey
(
pcode
,
code
)
const
item
=
tplItemMap
.
value
[
key
]
const
saveBatch
=
async
()
=>
{
try
{
formValues
.
value
=
[];
await
getFillDatas
();
if
(
formValues
.
value
.
length
>
0
)
{
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
);
VxeUI
.
modal
.
message
({
content
:
'保存成功'
,
status
:
'success'
});
}
else
{
VxeUI
.
modal
.
message
({
content
:
'没有需要保存的数据'
,
status
:
'warning'
});
}
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`保存失败:
${
error
.
message
}
`
,
status
:
'error'
});
}
finally
{
loading
.
value
=
false
;
}
};
if
(
!
item
)
return
const
getFillDatas
=
async
()
=>
{
try
{
formValues
.
value
=
[];
for
(
const
strKey
in
formData
)
{
const
valData
=
formData
[
strKey
];
if
(
valData
!==
undefined
&&
valData
!==
''
)
{
let
tmpAry
=
strKey
.
split
(
'_'
);
await
setFormValues
(
tmpAry
[
0
],
tmpAry
[
1
],
valData
,
1
);
}
}
await
getAttachTableFormData
();
await
getAllMultiTableFormData
();
await
getVxeTableFormData
();
}
catch
(
error
)
{
console
.
log
(
error
);
}
finally
{
const
formItem
:
FormDataItem
=
{
id
:
null
,
rind
,
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
item
.
itemid
,
itempid
:
item
.
pid
,
content
:
stringValue
}
};
const
childMultiTableRefs
=
ref
({});
const
setMultiColumnTableRef
=
(
el
:
any
,
index
:
string
)
=>
{
if
(
el
)
{
childMultiTableRefs
.
value
[
index
]
=
el
;
}
};
formValues
.
value
.
push
(
formItem
)
}
const
getAllMultiTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childMultiTableRefs
.
value
)
{
const
child
=
childMultiTableRefs
.
value
[
pcode
];
// 数据收集函数
const
collectAttachTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childAttachTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
await
child
.
getFormData
();
let
rind
=
0
;
for
(
const
obj
of
datas
)
{
rind
++
;
Object
.
keys
(
obj
).
forEach
((
code
)
=>
{
setFormValues
(
pcode
,
code
,
obj
[
code
],
rind
);
});
}
const
datas
=
await
child
.
getFormData
()
for
(
const
[
code
,
value
]
of
Object
.
entries
(
datas
))
{
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
};
const
childAttachTableRefs
=
ref
({});
const
setAttachTableRef
=
(
el
:
any
,
index
:
string
)
=>
{
if
(
el
)
{
childAttachTableRefs
.
value
[
index
]
=
el
;
// 保存子组件实例
}
};
const
childMyVexTableRefs
=
ref
({});
const
setMyVxeTableRef
=
(
el
:
any
,
index
:
string
)
=>
{
if
(
el
)
{
childMyVexTableRefs
.
value
[
index
]
=
el
;
// 保存子组件实例
}
};
}
const
getAttachTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childAttachTableRefs
.
value
)
{
const
child
=
childAttachTableRefs
.
value
[
pcode
];
const
collectMultiTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMultiTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
await
child
.
getFormData
();
for
(
const
code
in
datas
)
{
await
setFormValues
(
pcode
,
code
,
datas
[
code
],
1
);
const
datas
=
await
child
.
getFormData
()
let
rind
=
0
for
(
const
obj
of
datas
)
{
rind
++
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
}
}
}
};
}
const
getVxeTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childMyVexTableRefs
.
value
)
{
const
child
=
childMyVexTableRefs
.
value
[
pcode
];
const
collectVxeTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMyVexTableRefs
.
value
))
{
if
(
child
)
{
const
formData
=
await
child
.
getFormData
();
console
.
log
(
'formData'
,
formData
);
let
rind
=
0
;
const
formData
=
await
child
.
getFormData
()
let
rind
=
0
// 表格数据
for
(
const
obj
of
formData
.
datas
)
{
rind
++
;
Object
.
keys
(
obj
).
forEach
((
code
)
=>
{
setFormValues
(
pcode
,
code
,
obj
[
code
],
rind
);
});
rind
++
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
}
if
(
formData
.
footer
)
{
Object
.
keys
(
formData
.
footer
).
forEach
((
code
)
=>
{
setFormValues
(
pcode
,
code
,
formData
.
footer
[
code
],
1
);
});
for
(
const
[
code
,
value
]
of
Object
.
entries
(
formData
.
footer
))
{
await
addFormValue
(
formData
.
footerPcode
,
code
,
value
,
1
)
}
}
}
};
}
}
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
loadTableData
=
async
()
=>
{
try
{
loading
.
value
=
true
let
tempForm
:
FormData
=
{
id
:
null
,
rind
:
rind
,
// 清空现有数据
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
])
const
recordData
=
await
queryRecord
({
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
itemid
,
itempid
:
pid
,
content
:
vals
,
};
formValues
.
value
.
push
(
tempForm
);
};
// 校验数据
const
validateData
=
()
=>
{
drawerVisible
.
value
=
true
;
validationDrawerRef
.
value
.
setValidateData
(
validFormula
.
value
,
formData
);
};
tplid
:
queryParam
.
value
.
tplId
})
// 转换记录数据为便于查找的结构
const
valueMap
=
recordData
.
reduce
((
acc
:
Record
<
string
,
string
>
,
data
)
=>
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
acc
[
key
]
=
data
.
content
return
acc
},
{})
// 填充表格数据
await
fillTableData
(
valueMap
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`加载数据失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
const
handleInputBlur
=
(
rowCode
:
string
,
field
:
string
,
formula
?:
string
)
=>
{
if
(
!
formula
)
return
;
const
fillTableData
=
async
(
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
row
of
tableFormData
)
{
if
(
!
row
.
type
&&
row
.
content
)
{
await
fillSimpleFields
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'AttachTable'
&&
row
.
datas
)
{
await
fillAttachTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'MultiColumnTable'
&&
row
.
content
)
{
await
fillMultiColumnTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'VxeTable'
&&
row
.
columns
)
{
await
fillVxeTable
(
row
,
valueMap
)
}
}
}
const
key
=
getFieldKey
(
rowCode
,
field
);
const
value
=
formData
[
key
];
const
fillSimpleFields
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
if
(
!
row
.
content
||
!
Array
.
isArray
(
row
.
content
))
return
if
(
!
value
||
value
===
''
)
{
delete
inputErrors
.
value
[
key
];
return
;
}
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
validateFieldFormula
(
rowCode
,
field
,
formula
);
};
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
const
validateFieldFormula
=
(
rowCode
:
string
,
field
:
string
,
formula
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
);
const
{
pid
,
itemid
,
formTp
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
try
{
const
expression
=
buildExpression
(
formula
,
rowCode
);
const
isValid
=
eval
(
expression
);
if
(
!
isValid
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
};
if
(
dataVal
===
undefined
)
continue
if
(
formTp
===
'checkbox'
)
{
formData
[
key
]
=
dataVal
.
split
(
','
).
filter
(
Boolean
)
}
else
{
delete
inputErrors
.
value
[
key
];
}
}
catch
(
error
:
any
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
,
error
:
error
.
message
};
formData
[
key
]
=
dataVal
}
};
const
buildExpression
=
(
formula
:
string
,
rowCode
:
string
):
string
=>
{
const
fieldNames
=
formula
.
match
(
/
\b[
A-Za-z
][
A-Za-z0-9_
]
*
\b
/g
)
||
[];
const
uniqueFields
=
[...
new
Set
(
fieldNames
.
filter
(
f
=>
!
[
'and'
,
'or'
,
'not'
,
'equal'
,
'less'
,
'greater'
,
'if'
,
'else'
,
'true'
,
'false'
]
.
includes
(
f
.
toLowerCase
())
))];
uniqueFields
.
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
);
// 处理特殊字段类型
if
(
item
.
type
===
'checkboxAndInput'
&&
item
.
optionItemField
)
{
await
fillCheckboxInputFields
(
row
,
item
,
valueMap
)
}
let
expression
=
formula
;
for
(
const
fieldName
of
uniqueFields
)
{
const
key
=
getFieldKey
(
rowCode
,
fieldName
);
const
value
=
formData
[
key
];
if
(
value
!==
undefined
)
{
expression
=
expression
.
replace
(
new
RegExp
(
`\\b
${
fieldName
}
\\b`
,
'g'
),
`Number(
${
value
}
)`
);
if
(
item
.
otherField
)
{
await
fillOtherField
(
row
,
item
,
valueMap
)
}
}
}
return
expression
;
};
const
fillCheckboxInputFields
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
field
of
item
.
optionItemField
||
[])
{
const
key
=
getFieldKey
(
row
.
code
,
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
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
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
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
;
if
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
}
};
}
const
closeAllTooltips
=
()
=>
{
showTooltip
.
value
=
false
;
hoveredKey
.
value
=
''
;
showErrorTooltip
.
value
=
false
;
hoveredErrorKey
.
value
=
''
;
};
const
fillOtherField
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
otherField
!
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
return
const
setFormItemRight
=
()
=>
{
console
.
log
(
'开始设置表单权限...'
);
console
.
log
(
'userAllocItems 数量:'
,
userAllocItems
.
value
.
length
);
console
.
log
(
'validFormula 数量:'
,
validFormula
.
value
.
length
);
const
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
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
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
}
console
.
log
(
`字段
${
key
}
: hasRight =
${
item
.
hasRight
}
`
);
const
fillAttachTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childAttachTableRefs
.
value
[
row
.
code
]
if
(
!
child
)
return
if
(
item
.
hasRight
)
{
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
);
const
tableData
:
Record
<
string
,
any
>
=
{}
item
.
hasValidFormula
=
!!
formulaResult
.
formula
;
item
.
matchedFormula
=
formulaResult
.
formula
||
null
;
for
(
const
data
of
row
.
datas
||
[])
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
data
))
{
if
(
typeof
value
===
'object'
&&
value
.
field
)
{
const
fieldKey
=
getFieldKey
(
row
.
code
,
value
.
field
)
const
tplItem
=
tplItemMap
.
value
[
fieldKey
]
if
(
!
tplItem
)
continue
console
.
log
(
`字段
${
key
}
: hasValidFormula =
${
item
.
hasValidFormula
}
`
);
}
else
{
item
.
hasValidFormula
=
false
;
item
.
matchedFormula
=
null
;
const
{
pid
,
itemid
,
formTp
,
code
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
if
(
dataVal
)
{
tableData
[
code
]
=
formTp
===
'checkbox'
?
dataVal
.
split
(
','
)
:
dataVal
}
}
});
}
});
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
;
};
child
.
setFormData
(
tableData
)
}
// 处理校验结果点击
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
;
const
status
=
result
.
isValid
?
'success'
:
'error'
;
const
fillMultiColumnTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMultiTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
content
))
return
VxeUI
.
modal
.
message
({
content
:
message
,
status
});
};
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
// 打开历史填报抽屉
const
handleOpenHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
true
;
};
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
// 关闭历史填报抽屉
const
closeHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
false
;
};
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
// 添加刷新帮助图标的方法
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
);
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
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
;
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{}
}
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
});
}
});
// 强制更新视图
nextTick
(()
=>
{
console
.
log
(
'帮助图标刷新完成'
);
});
};
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
// 原有检查方法
async
function
checkDataOrig
()
{
let
tplName
=
queryParam
.
value
.
tplName
;
await
getFillDatas
();
if
(
formValues
.
value
.
length
>
0
)
{
refCheckTbData
.
value
.
setIniData
(
tplName
,
formValues
.
value
);
}
}
const
fillVxeTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMyVexTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
columns
))
return
async
function
setData
()
{
try
{
loading
.
value
=
true
;
const
taskId
=
queryParam
.
value
.
taskId
;
const
tplid
=
queryParam
.
value
.
tplId
;
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
Object
.
keys
(
formData
).
forEach
((
key
)
=>
delete
formData
[
key
]);
for
(
const
column
of
row
.
columns
)
{
if
(
!
column
.
field
)
continue
await
setTplItemMap
();
const
recordData
=
await
queryRecord
({
taskid
:
taskId
,
tplid
:
tplid
});
const
key
=
getFieldKey
(
row
.
code
,
column
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
||
Object
.
keys
(
tplItem
).
length
===
0
)
continue
const
valueObj
=
{};
for
(
const
data
of
recordData
)
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
;
valueObj
[
key
]
=
data
.
content
;
}
const
shouldSkip
=
(
key
)
=>
{
const
refsToCheck
=
[
childMultiTableRefs
.
value
,
childAttachTableRefs
.
value
];
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
for
(
const
refs
of
refsToCheck
)
{
for
(
const
pcode
of
Object
.
keys
(
refs
))
{
if
(
key
.
startsWith
(
pcode
+
'_'
)
)
{
return
true
;
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
]
)
{
rowsMap
[
rind
]
=
{}
}
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
}
return
false
;
};
for
(
const
strKey
of
Object
.
keys
(
tplItemMap
.
value
))
{
if
(
shouldSkip
(
strKey
))
{
continue
;
}
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
const
item
=
tplItemMap
.
value
[
strKey
];
if
(
!
item
)
continue
;
// 校验相关
const
validateData
=
()
=>
{
drawerVisible
.
value
=
true
validationDrawerRef
.
value
.
setValidateData
(
validFormula
.
value
,
formData
);
}
const
{
pid
,
itemid
,
formTp
}
=
item
;
const
dataVal
=
valueObj
[
`
${
pid
}
_
${
itemid
}
_1`
];
const
handleInputBlur
=
(
rowCode
:
string
,
field
:
string
,
formula
?:
string
)
=>
{
if
(
!
formula
)
return
if
(
!
dataVal
)
continue
;
const
key
=
getFieldKey
(
rowCode
,
field
)
const
value
=
formData
[
key
]
if
(
formTp
===
'checkbox'
)
{
formData
[
strKey
]
=
dataVal
?.
split
(
','
)
||
[];
}
else
{
formData
[
strKey
]
=
dataVal
||
''
;
}
if
(
!
value
||
value
===
''
)
{
delete
inputErrors
.
value
[
key
]
return
}
for
(
const
pcode
of
Object
.
keys
(
childAttachTableRefs
.
value
))
{
const
child
=
childAttachTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
((
key
)
=>
key
.
startsWith
(
pcode
+
'_'
));
const
attachTableData
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
validateFieldFormula
(
rowCode
,
field
,
formula
)
}
const
{
pid
,
itemid
,
formTp
,
code
}
=
tmpData
;
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
;
const
validateFieldFormula
=
(
rowCode
:
string
,
field
:
string
,
formula
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
formTp
===
'checkbox'
)
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]?.
split
(
','
)
||
[];
try
{
const
expression
=
buildExpression
(
formula
,
rowCode
)
const
isValid
=
eval
(
expression
)
if
(
!
isValid
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
}
}
else
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]
||
''
;
delete
inputErrors
.
value
[
key
]
}
}
catch
(
error
:
any
)
{
inputErrors
.
value
[
key
]
=
{
message
:
"公式校验失败,请检查填写内容"
,
formula
,
error
:
error
.
message
}
child
.
setFormData
(
attachTableData
);
}
}
for
(
const
pcode
of
Object
.
keys
(
childMultiTableRefs
.
value
))
{
const
child
=
childMultiTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
((
key
)
=>
key
.
startsWith
(
pcode
+
'_'
));
const
rowsMap
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
const
{
pid
,
itemid
,
code
}
=
tmpData
;
const
valKeys
=
Object
.
keys
(
valueObj
).
filter
((
k
)
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
));
const
buildExpression
=
(
formula
:
string
,
rowCode
:
string
):
string
=>
{
const
fieldNames
=
formula
.
match
(
/
\b[
A-Za-z
][
A-Za-z0-9_
]
*
\b
/g
)
||
[]
const
uniqueFields
=
[...
new
Set
(
fieldNames
.
filter
(
f
=>
!
[
'and'
,
'or'
,
'not'
,
'equal'
,
'less'
,
'greater'
,
'if'
,
'else'
,
'true'
,
'false'
]
.
includes
(
f
.
toLowerCase
())
))]
for
(
const
valKey
of
valKeys
)
{
const
parts
=
valKey
.
split
(
'_'
);
const
rind
=
parts
[
2
];
uniqueFields
.
sort
((
a
,
b
)
=>
b
.
length
-
a
.
length
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{};
}
let
expression
=
formula
for
(
const
fieldName
of
uniqueFields
)
{
const
key
=
getFieldKey
(
rowCode
,
fieldName
)
const
value
=
formData
[
key
]
rowsMap
[
rind
][
code
]
=
valueObj
[
valKey
];
}
}
if
(
value
!==
undefined
)
{
// 检查字段类型,如果是日期类型,转换为时间戳进行比较
const
row
=
tableFormData
.
find
((
r
:
TableRow
)
=>
r
.
code
===
rowCode
)
let
processedValue
=
value
const
tableDatas
=
Object
.
values
(
rowsMap
);
nextTick
();
child
.
setFormData
(
tableDatas
);
if
(
row
&&
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
const
fieldItem
=
row
.
content
.
find
((
c
:
ContentItem
)
=>
c
.
field
===
fieldName
)
if
(
fieldItem
&&
fieldItem
.
type
===
'date'
)
{
// 将日期字符串转换为时间戳(毫秒)
const
date
=
new
Date
(
value
)
if
(
!
isNaN
(
date
.
getTime
()))
{
processedValue
=
date
.
getTime
()
}
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
,
});
}
finally
{
loading
.
value
=
false
;
}
}
async
function
setTplItemMap
()
{
try
{
const
tplid
=
queryParam
.
value
.
tplId
;
const
tplItem
=
await
allTplItems
({
tplid
:
tplid
,
});
tplItemMap
.
value
=
{};
tplItem
.
forEach
((
item
)
=>
{
let
strKey
=
item
.
pcode
+
'_'
+
item
.
xmlcode
;
tplItemMap
.
value
[
strKey
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
};
});
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
});
}
finally
{
expression
=
expression
.
replace
(
new
RegExp
(
`\\b
${
fieldName
}
\\b`
,
'g'
),
`Number(
${
processedValue
}
)`
)
}
}
</
script
>
<
style
lang=
"less"
scoped
>
.bank-report-table {
font-family: 'SimSun', '宋体', serif;
font-size: 12px;
color: #000;
margin: 5px auto;
width: 60%;
}
return
expression
}
/* 加载遮罩层样式 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
// 提示工具相关
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
}
}
.loading-spinner {
display: flex;
flex-direction: column;
align-items: center;
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
}
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 2s linear infinite;
}
const
closeAllTooltips
=
()
=>
{
showTooltip
.
value
=
false
hoveredKey
.
value
=
''
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// 历史填报
const
handleOpenHistoryDrawer
=
()
=>
{
historyFillCheckRef
.
value
?.
onDrawerShow
(
queryParam
.
value
.
tplId
)
historyDrawerVisible
.
value
=
true
}
.loading-text {
margin-top: 10px;
color: #666;
font-size: 14px;
}
const
closeHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
false
}
// 校验结果处理
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
VxeUI
.
modal
.
message
({
content
:
message
,
status
})
}
.custom-table {
width: 100%;
border: 1px solid #000;
}
.custom-table .vxe-header--column,
.custom-table .vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
.custom-table .vxe-cell {
padding: 5px;
}
// 工具函数
const
findEarliestFormulaForField
=
(
formulas
:
FormulaItem
[],
field
:
string
):
{
formula
:
FormulaItem
|
null
,
index
:
number
}
=>
{
let
result
=
{
formula
:
null
,
index
:
Infinity
}
.content-cell {
line-height: 1.8;
}
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
)
.table-input {
width: 80px;
margin: 0 2px;
if
(
match
&&
match
.
index
<
result
.
index
)
{
result
=
{
formula
:
f
,
index
:
match
.
index
}
}
})
.radio-group {
display: inline-block;
margin-right: 10px;
}
return
result
}
.checkbox-group {
display: inline-block;
margin-right: 10px;
}
// 消息提示
const
showSuccessMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'success'
})
}
.vxe-radio,
.vxe-checkbox {
margin-right: 4px;
white-space: nowrap;
}
const
showWarningMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'warning'
})
}
.vxe-input--inner {
height: 24px;
line-height: 24px;
padding: 0 5px;
}
const
showErrorMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'error'
})
}
.vxe-radio--label,
.vxe-checkbox--label
{
font-size: 12px;
}
// 子组件引用设置
const
setMultiColumnTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMultiTableRefs
.
value
[
code
]
=
el
}
.vxe-radio--icon,
.vxe-checkbox--icon {
font-size: 12px;
}
const
setAttachTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childAttachTableRefs
.
value
[
code
]
=
el
}
.vxe-radio-group,
.vxe-checkbox-group {
display: inline-block;
}
const
setMyVxeTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMyVexTableRefs
.
value
[
code
]
=
el
}
</
script
>
.bank-report-table {
font-family: 'SimSun', '宋体', serif;
<
style
lang=
"less"
scoped
>
// Base table styles
.bank-report-table {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
color: #000;
margin: 5px auto;
width: 90%;
}
position: relative;
.custom-table {
// Shared table styles
.custom-table, .attachment-table {
width: 100%;
border: 1px solid #000;
}
.custom-table
.vxe-header--column,
.custom-table
.vxe-body--column {
.vxe-header--column,
.vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
.content-cell {
line-height: 1.8;
}
}
.table-input {
width: 150px;
margin: 0 2px
;
}
// 表格信息文本
.table-info-text {
font-weight: bold
;
}
.radio-group {
display: inline-block;
margin-right: 10px;
}
// 项目名称
.project-name {
font-weight: bold;
font-size: 14px;
}
.checkbox-group {
display: inline-block;
margin-right: 10px
;
}
// Cell styles
.content-cell {
line-height: 1.8
;
}
.vxe-radio,
.vxe-checkbox {
margin-right: 8px;
// 表单控件组
.radio-group, .checkbox-group {
display: inline-block;
margin-left: 10px;
.vxe-radio, .vxe-checkbox {
margin-left: 0px;
white-space: nowrap;
}
line-height: 30px;
padding: 0px 10px;
/* 附件样式 */
.attachments {
margin-top: 20px;
width: 100%;
&--label {
font-size: 12px;
}
.attachments h4 {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
&--icon {
font-size: 12px;
}
}
}
.attachment-table {
width: 100%;
border: 1px solid #000;
// VXE specific overrides
.vxe {
&-radio-group, &-checkbox-group {
display: inline-block;
}
.attachment-table .vxe-header--column,
.attachment-table .vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
&-cell {
padding: 5px;
}
}
.vxe-input {
.vxe-input {
border-bottom: 1px solid #333;
text-align: center;
margin: 5px 3px;
...
...
@@ -1142,28 +1115,22 @@
border-left: none;
border-right: none;
background: transparent;
}
}
blockquote {
padding: 0px 5px;
color: black;
cursor: pointer;
}
// 输入框包装器
.input-wrapper {
// 输入框包装器
.input-wrapper {
position: relative;
display: inline-block;
}
}
// 单位文本
.unit {
// 单位文本
.unit {
margin-left: 5px;
font-size: 12px;
}
}
// 帮助图标
.help-icon {
// 帮助图标
.help-icon {
margin-left: 5px;
cursor: pointer;
color: #1890ff;
...
...
@@ -1177,29 +1144,14 @@
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);
}
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
// 错误图标
.error-icon {
// 错误图标
.error-icon {
margin-left: 5px;
cursor: pointer;
color: #ff4d4f;
...
...
@@ -1214,10 +1166,10 @@
&:hover {
transform: scale(1.1);
}
}
}
// 提示框
.tooltip, .error-tooltip {
// 提示框
.tooltip, .error-tooltip {
position: absolute;
top: 100%;
left: 0;
...
...
@@ -1229,9 +1181,9 @@
white-space: pre-wrap;
max-width: 300px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
}
.tooltip {
.tooltip {
background: #333;
color: #fff;
...
...
@@ -1244,9 +1196,9 @@
border-style: solid;
border-color: transparent transparent #333 transparent;
}
}
}
.error-tooltip {
.error-tooltip {
background: #8b0000;
color: #fff;
border: 1px solid #ff4d4f;
...
...
@@ -1260,5 +1212,43 @@
border-style: solid;
border-color: transparent transparent #8b0000 transparent;
}
}
}
// 加载遮罩
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-spinner {
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 14px;
color: #666;
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/baosong/report/components/Tb9.vue
浏览文件 @
12b89e58
<
template
>
<div
class=
"bank-report-table"
style=
"height: 80vh;"
@
click=
"closeAllTooltips"
>
<div
class=
"bank-report-table"
style=
"height:80vh"
@
click=
"closeAllTooltips"
>
<!-- 加载遮罩层 -->
<div
v-if=
"loading"
class=
"loading-overlay"
>
<div
class=
"loading-spinner"
>
...
...
@@ -8,87 +12,68 @@
</div>
</div>
<!-- 工具栏 -->
<vxe-toolbar>
<template
#
button
>
<template
#
button
s
>
<div
style=
"margin:10px"
>
<span
style=
"font-weight: bold"
>
填报任务:
{{
queryParam
?.
taskName
}}
</span>
<span
style=
"font-weight: bold;margin-left:20px"
>
表格:
{{
queryParam
?.
tplCode
}}
{{
queryParam
?.
tplName
}}
</span>
<span
class=
"table-info-text"
>
填报任务:
{{
queryParam
.
taskName
}}
</span>
<span
class=
"table-info-text"
style=
"margin-left:20px"
>
表格:
{{
queryParam
.
tplCode
}}
{{
queryParam
.
tplName
}}
</span>
</div>
</
template
>
<
template
#
tools
v-if=
"!queryParam.comfrom"
>
<vxe-button
status=
"primary"
icon=
"vxe-icon-edit"
@
click=
"handleOpenHistoryDrawer"
:disabled=
"loading"
>
近5年数据填报
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"validateData"
>
校验
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"saveBatch"
>
保存
</vxe-button>
</
template
>
</vxe-toolbar>
<!-- 校验结果抽屉 -->
<vxe-drawer
v-model=
"drawerVisible"
placement=
"right"
@
show=
"handleValidationDrawerShow"
title=
"校验结果"
width=
"40%"
:footer=
"{ show: true }"
<vxe-button
status=
"primary"
icon=
"vxe-icon-edit"
@
click=
"handleOpenHistoryDrawer"
:disabled=
"loading"
>
<
template
#
default
>
<div
class=
"validation-results"
>
<div
class=
"result-summary"
>
<div
class=
"summary-item"
>
<span
class=
"summary-label"
>
校验字段数:
</span>
<span
class=
"summary-value"
>
{{
validationResultsList
.
length
}}
</span>
</div>
<div
class=
"summary-item"
>
<span
class=
"summary-label"
>
通过:
</span>
<span
class=
"summary-value success"
>
{{
validationResultsList
.
filter
(
item
=>
item
.
isValid
).
length
}}
</span>
</div>
<div
class=
"summary-item"
>
<span
class=
"summary-label"
>
失败:
</span>
<span
class=
"summary-value error"
>
{{
validationResultsList
.
filter
(
item
=>
!
item
.
isValid
).
length
}}
</span>
</div>
</div>
<div
class=
"results-list"
>
<div
v-for=
"(result, index) in validationResultsList"
:key=
"index"
class=
"result-item"
:class=
"
{ success: result.isValid, error: !result.isValid }"
@click="handleValidationResultClick(result)"
近5年数据填报
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"validateData"
>
<div
class=
"result-field"
>
{{
result
.
field
}}
</div>
<div
class=
"result-desc"
>
{{
result
.
description
}}
</div>
<div
class=
"result-value"
>
值:
{{
result
.
fieldValue
||
'空'
}}
</div>
<div
v-if=
"!result.isValid"
class=
"result-error"
>
失败原因:
{{
result
.
description
}}
</div>
</div>
</div>
</div>
校验
</vxe-button>
<vxe-button
status=
"primary"
icon=
"vxe-icon-save"
@
click=
"saveBatch"
>
保存
</vxe-button>
</
template
>
</vxe-
drawe
r>
</vxe-
toolba
r>
<!-- 主表格 -->
<vxe-table
border
ref=
"tableRef"
height=
"auto"
show-header-overflow
:data=
"tableFormData"
:column-config=
"{ resizable: true }"
:row-config=
"{ resizable: true }"
class=
"custom-table"
height=
"auto"
>
<vxe-column
field=
"serialNumber"
title=
"序号"
width=
"80"
></vxe-column>
<vxe-column
field=
"project"
title=
"项目"
width=
"200"
>
<vxe-column
field=
"serialNumber"
title=
"序号"
width=
"50"
></vxe-column>
<vxe-column
field=
"project"
title=
"项目"
width=
"100"
>
<
template
#
default=
"{ row }"
>
<span
style=
"font-weight: bold;size:20px
"
>
{{
row
.
project
}}
</span>
<span
class=
"project-name
"
>
{{
row
.
project
}}
</span>
</
template
>
</vxe-column>
<vxe-column
field=
"content"
title=
"内容"
row-resize
>
<vxe-column
field=
"content"
title=
"内容"
>
<
template
#
default=
"{ row }"
>
<div
class=
"content-cell"
>
<!--
MultiColumnTable 类型
-->
<!--
多列表格
-->
<template
v-if=
"row.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"row.project"
:columnsPerRow=
"6"
:records=
"[]"
:fields=
"row.content"
:ref=
"(el) => setMultiColumnTableRef(el, row.code)"
...
...
@@ -96,203 +81,190 @@
/>
</
template
>
<!--
AttachTable 类型
-->
<!--
附件表格
-->
<
template
v-else-if=
"row.type === 'AttachTable'"
>
<AttachTable
:title=
"row.project"
:records=
"row.datas"
:fields=
"row.content"
:pcode=
"row.code"
:calcSum=
"row.calcSum"
:showFooter=
"row.showFooter"
:ref=
"(el) => setAttachTableRef(el, row.code)"
:disabled=
"!row.hasRight"
style=
"margin:0px;padding:0px"
/>
</
template
>
<!-- Vxe表格 -->
<
template
v-else-if=
"row.type === 'VxeTable'"
>
<MyVxeTable1
:title=
"row.project"
:data=
"row.data"
:columns=
"row.columns"
:pcode=
"row.code"
:footerData=
"row.footerData"
:showFooter=
"row.showFooter"
:ref=
"(el) => setMyVxeTableRef(el, row.code)"
:disabled=
"!row.hasRight"
style=
"margin:0px;padding:0px"
/>
</
template
>
<!--
其他类型
-->
<!--
普通字段渲染
-->
<
template
v-else
>
<template
v-for=
"(item, index) in row.content"
:key=
"index"
>
<!-- 文本类型 -->
<span
v-if=
"item.type === 'text'"
v-html=
"item.value"
></span>
<!-- 换行 -->
<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 === 'AttachTable'"
>
<AttachTable
:title=
"item.project"
:records=
"item.datas"
:fields=
"item.content"
:pcode=
"item.code"
:ref=
"(el) => setAttachTableRef(el, item.code)"
style=
"margin:0px;padding:0px"
/>
</
template
>
<
template
v-else-if=
"item.type === 'MultiColumnTable'"
>
<MultiColumnTable
:title=
"item.project"
:records=
"[]"
:fields=
"item.content"
:ref=
"(el) => setMultiColumnTableRef(el, item.code)"
style=
"margin:0px;padding:0px"
/>
</
template
>
<
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[getFieldKey(row.code, item.field)]=='是'"
style=
"margin-left:40px"
>
如是,
<template
v-if=
"item.extraField"
>
{{
item
.
extraLabel
}}
:
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.extraField)]"
style=
"width: 300px;"
></vxe-input>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
</
template
>
<
template
v-else-if=
"item.extraFields"
>
<template
v-for=
"(ccopt,ccind) in item.extraFields"
:key=
"ccind"
>
<span>
{{
ccopt
.
label
}}
:
</span>
<template
v-if=
"ccopt.formType=='input'"
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
style=
"width:250px"
></vxe-input>
</
template
>
<
template
v-else-if=
"ccopt.formType=='number'"
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
type=
"number"
style=
"width:250px"
></vxe-input>
<span
class=
"unit"
>
{{
ccopt
.
unit
}}
</span>
</
template
>
<
template
v-else-if=
"ccopt.formType=='checkbox'"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
:content=
"opt"
>
</vxe-checkbox>
</vxe-checkbox-group>
</
template
>
<
template
v-else-if=
"ccopt.formType=='radio'"
>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
:options=
"ccopt.options"
>
<vxe-radio
v-for=
"(opt, optIndex) in ccopt.options"
:key=
"optIndex"
:label=
"opt"
:content=
"opt"
>
</vxe-radio>
</vxe-radio-group>
</
template
>
<
template
v-else
>
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.field)]"
:type=
"ccopt.formType"
style=
"width:100px"
></vxe-input>
</
template
>
<br>
<
template
v-if=
"ccopt.otherOption"
>
其他:
<vxe-input
v-model=
"formData[getFieldKey(row.code, ccopt.otherField)]"
style=
"width: 300px;"
></vxe-input>
</
template
>
<br>
</template>
</template>
</div>
</template>
</template>
<!-- 单选组 -->
<span
v-else-if=
"item.type === 'radio-group'"
class=
"radio-group"
>
<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[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[getFieldKey(row.code, item.field)]==opt.label"
>
<span>
{{
opt
.
extraFieldLabel
}}
</span>
<vxe-input
v-model=
"formData[getFieldKey(row.code, opt.otherField)]"
style=
"width: 200px;"
></vxe-input>
</
template
>
<
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[getFieldKey(row.code, ccopt.field)]"
style=
"width: 200px;"
></vxe-input>
</
template
>
</template>
<vxe-radio-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight || isRadioDisabled(row.code, item)"
>
<vxe-radio
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-radio>
</vxe-radio-group>
</span>
<!-- 多选组(带其他选项) -->
<div
v-else-if=
"item.type === 'checkbox-group'"
>
<span>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
<span
class=
"checkbox-group"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
>
{{ opt }}
</vxe-checkbox>
<
template
v-if=
"item.otherOption"
>
<vxe-checkbox
label=
"其他"
>
<
template
v-if=
"item.otherField"
>
<div
style=
"width:100%"
>
<vxe-checkbox
label=
"其他"
:disabled=
"!item.hasRight"
>
其他:
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
style=
"width: 200px;"
:disabled=
"!(formData[getFieldKey(row.code, item.field)]?.indexOf('其他')>-1)"
>
</vxe-input>
</vxe-checkbox>
<vxe-input
v-model=
"formData[getFieldKey(row.code, item.otherField)]"
:disabled=
"!isOtherFieldEnabled(row.code, item) || !item.hasRight"
style=
"width: 50%"
@
blur=
"handleInputBlur(row.code, item.otherField, item.matchedFormula?.formula)"
/>
</div>
</
template
>
</vxe-checkbox-group>
<!-- 其他选项 -->
</span>
</div>
<
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
>
<!-- 多选带输入框 -->
<div
v-else-if=
"item.type === 'checkboxAndInput'"
>
<span
style=
"margin-left:20px"
>
<vxe-checkbox-group
v-model=
"formData[getFieldKey(row.code, item.field)]"
:disabled=
"!item.hasRight"
>
<vxe-checkbox
v-for=
"(opt, optIndex) in item.options"
:key=
"optIndex"
:label=
"opt"
:disabled=
"!item.hasRight"
style=
"margin-left: 0px;display: block;"
>
{{ opt }}
<vxe-input
type=
"number"
v-model=
"formData[getFieldKey(row.code, item.optionItemField[optIndex])]"
:disabled=
"!item.hasRight"
style=
"width: 100px;margin: 0px;"
@
blur=
"handleInputBlur(row.code, item.optionItemField[optIndex], item.matchedFormula?.formula)"
/>
<span
class=
"unit"
>
{{ item.optionItemUint }}
</span>
</vxe-checkbox>
</vxe-checkbox-group>
</span>
</div>
<!-- 普通输入框 -->
<
template
v-else
>
<div
class=
"input-wrapper"
>
<vxe-input
:disabled=
"!item.hasRight || isInputDisabled(row.code, item)"
:type=
"item.type"
v-model=
"formData[getFieldKey(row.code, item.field)]"
size=
"mini"
class=
"table-input"
:
disabled=
"!item.hasRight
"
:
style=
"item.style
"
@
blur=
"handleInputBlur(row.code, item.field, item.matchedFormula?.formula)"
/>
<span
v-if=
"item.unit"
class=
"unit"
>
{{
item
.
unit
}}
</span>
<span
class=
"unit"
>
{{
item
.
unit
}}
</span>
<!-- 帮助图标 -->
<span
v-if=
"
showHelpIcon(row.code, item.field, item)"
v-if=
"
item.hasValidFormula"
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=
"点击查看错误详情"
>
<i
class=
"vxe-icon-error"
>
✗
</i>
</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
}}
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
message
}}
<br>
<span
style=
"color: #ffcccc;"
>
公式:
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)]
?
.
formula
}}
公式:
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
formula
}}
</span>
<template
v-if=
"inputErrors[getFieldKey(row.code, item.field)]
?
.error"
>
<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
}}
错误:
{{
inputErrors
[
getFieldKey
(
row
.
code
,
item
.
field
)].
error
}}
</span>
</
template
>
</div>
<!-- 校验规则提示 -->
<div
v-if=
"showTooltip && hoveredKey === getFieldKey(row.code, item.field)"
class=
"tooltip"
@
click
.
stop
>
{{ item.matchedFormula?.des || '暂无校验规则' }}
</div>
</div>
</template>
</template>
...
...
@@ -301,72 +273,74 @@
</template>
</vxe-column>
<vxe-column
field=
"remarks"
title=
"备注"
width=
"150"
>
<!-- 备注列 -->
<vxe-column
field=
"remarks"
title=
"备注"
width=
"60"
>
<
template
#
default=
"{ row }"
>
<vxe-textarea
:rows=
"row.remarks.rows"
v-model=
"formData[getFieldKey(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)]"
/>
</
template
>
</vxe-column>
</vxe-table>
<!-- 历史填报检查组件 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
close-drawer=
"closeHistoryDrawer"
/>
<!-- 校验抽屉 -->
<ValidationDrawer
ref=
"validationDrawerRef"
v-model=
"drawerVisible"
:tableFormData=
"tableFormData"
@
validationResultClick=
"handleValidationResultClick"
/>
<!-- 验证公式帮助提示 -->
<div
v-if=
"validationTooltipVisible"
class=
"validation-tooltip"
:style=
"{ left: validationTooltipPosition.left + 'px', top: validationTooltipPosition.top + 'px' }"
>
<pre
class=
"tooltip-content"
>
{{ validationTooltipContent }}
</pre>
</div>
<!-- 历史填报抽屉 -->
<HistoryFillCheck
ref=
"historyFillCheckRef"
v-model=
"historyDrawerVisible"
@
closeDrawer=
"closeHistoryDrawer"
/>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
reactive
,
onMounted
,
computed
}
from
'vue'
import
{
VxeUI
,
VxeTableInstance
}
from
'vxe-table'
import
'vxe-table/lib/style.css'
import
{
useRoute
}
from
'vue-router'
// 组件导入
import
MultiColumnTable
from
'../tableComponents/MultiColumnTable.vue'
import
AttachTable
from
'../tableComponents/AttachTable.vue'
import
HistoryFillCheck
from
'./check/historyFillCheck.vue'
import
{
tableFormData
}
from
'../../data/tb9.data'
;
import
{
ref
,
reactive
,
nextTick
,
onMounted
,
computed
}
from
'vue'
import
{
VxeUI
}
from
'vxe-table'
import
{
queryRecord
,
batchSaveOrUpdateBeforeDelete
}
from
'../../record/BaosongTaskRecord.api'
import
{
findUserRightForTplItem
}
from
'../../alloc/BaosongTaskAlloc.api'
import
MyVxeTable1
from
'../tableComponents/MyVxeTable.vue'
import
HistoryFillCheck
from
'./check/HistoryFillCheck.vue'
import
ValidationDrawer
from
'./check/ValidationDrawer.vue'
// 数据导入
import
{
tableFormData
}
from
'../../data/tb9.data'
// API 导入
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'
;
const
route
=
useRoute
();
const
tableRef
=
ref
();
const
tplItemMap
=
ref
<
Record
<
string
,
any
>>
({});
const
historyFillCheckRef
=
ref
<
any
>
(
null
);
const
queryParam
=
ref
({
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
tplName
:
''
,
tplCode
:
''
,
comfrom
:
''
})
// 权限相关状态
const
userAllocItems
=
ref
<
string
[]
>
([])
const
validFormula
=
ref
<
any
[]
>
([])
const
validationResultsList
=
ref
<
any
[]
>
([])
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
,
any
>>
({})
const
isInitialized
=
ref
(
false
)
const
validationTooltipVisible
=
ref
(
false
)
const
validationTooltipPosition
=
ref
({
left
:
0
,
top
:
0
})
const
validationTooltipContent
=
ref
(
''
)
// 类型定义
interface
QueryParam
{
taskId
:
number
taskName
:
string
tplId
:
number
tplName
:
string
tplCode
:
string
comfrom
:
string
}
interface
FormData
{
interface
FormData
Item
{
id
:
number
|
null
taskid
:
number
tplid
:
number
...
...
@@ -377,515 +351,522 @@ interface FormData {
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
);
}
if
(
route
.
query
.
comfrom
)
{
queryParam
.
value
.
comfrom
=
String
(
route
.
query
.
comfrom
);
}
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
}
// 设置查询参数
setQueryParams
();
interface
ContentItem
{
type
:
string
field
:
string
value
?:
any
hasRight
?:
boolean
hasValidFormula
?:
boolean
matchedFormula
?:
FormulaItem
relatedFiled
?:
string
otherField
?:
string
optionItemField
?:
string
[]
[
key
:
string
]:
any
}
// 获取权限和验证公式
try
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
})
interface
TableRow
{
code
:
string
project
:
string
type
?:
string
content
:
ContentItem
[]
|
any
remarks
:
{
rows
:
number
;
field
:
string
}
hasRight
?:
boolean
[
key
:
string
]:
any
}
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
,
})
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
}
console
.
log
(
'获取到的权限:'
,
userAllocItems
.
value
.
length
);
console
.
log
(
'获取到的公式:'
,
validFormula
.
value
.
length
);
}
catch
(
error
)
{
console
.
error
(
'获取权限或验证公式失败:'
,
error
)
}
interface
TplItemMapValue
{
pid
:
number
itemid
:
number
formTp
:
string
code
:
string
}
await
setTplItemMap
()
setFormItemRight
()
await
setData
()
// 路由和组件引用
const
route
=
useRoute
()
const
tableRef
=
ref
<
VxeTableInstance
>
()
const
historyFillCheckRef
=
ref
()
const
validationDrawerRef
=
ref
()
isInitialized
.
value
=
true
// 响应式状态
const
loading
=
ref
(
true
)
const
historyDrawerVisible
=
ref
(
false
)
const
drawerVisible
=
ref
(
false
)
const
showTooltip
=
ref
(
false
)
const
hoveredKey
=
ref
(
''
)
const
showErrorTooltip
=
ref
(
false
)
const
hoveredErrorKey
=
ref
(
''
)
setTimeout
(()
=>
{
refreshHelpIcons
()
},
300
)
// 数据状态
const
queryParam
=
ref
<
QueryParam
>
({
taskId
:
-
1
,
taskName
:
''
,
tplId
:
-
1
,
tplName
:
''
,
tplCode
:
''
,
comfrom
:
''
})
const
loading
=
ref
(
false
)
const
formData
=
reactive
<
Record
<
string
,
any
>>
({});
const
formData
=
reactive
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormDataItem
[]
>
([])
const
inputErrors
=
ref
<
Record
<
string
,
InputError
>>
({})
const
userAllocItems
=
ref
<
string
[]
>
([])
const
validFormula
=
ref
<
FormulaItem
[]
>
([])
const
tplItemMap
=
ref
<
Record
<
string
,
TplItemMapValue
>>
({})
// 子组件引用
const
childMultiTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childAttachTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
childMyVexTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
formValues
=
ref
<
FormData
[]
>
([])
// 计算属性
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
))
//
获取字段key的统一方法
//
工具函数
const
getFieldKey
=
(
rowCode
:
string
,
field
:
string
):
string
=>
{
return
`
${
rowCode
}
_
${
field
}
`
}
const
saveBatch
=
async
()
=>
{
try
{
loading
.
value
=
true
formValues
.
value
=
[]
const
isRadioDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
for
(
const
strKey
in
formData
)
{
const
valData
=
formData
[
strKey
]
if
(
valData
)
{
let
tmpAry
=
strKey
.
split
(
"_"
)
await
setFormValues
(
tmpAry
[
0
],
tmpAry
[
1
],
valData
,
1
)
}
}
const
isInputDisabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
if
(
!
item
.
relatedFiled
)
return
false
const
relatedKey
=
getFieldKey
(
rowCode
,
item
.
relatedFiled
)
return
formData
[
relatedKey
]
!==
'是'
}
await
getAttachTableFormData
();
await
getAllMultiTableFormData
()
const
isOtherFieldEnabled
=
(
rowCode
:
string
,
item
:
ContentItem
):
boolean
=>
{
const
checkboxKey
=
getFieldKey
(
rowCode
,
item
.
field
)
const
checkboxValue
=
formData
[
checkboxKey
]
||
[]
return
checkboxValue
.
includes
(
'其他'
)
}
if
(
formValues
.
value
.
length
>
0
)
{
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
VxeUI
.
modal
.
message
({
content
:
'保存成功'
,
status
:
'success'
})
}
else
{
VxeUI
.
modal
.
message
({
content
:
'没有需要保存的数据'
,
status
:
'warning'
})
}
// 初始化
onMounted
(
async
()
=>
{
await
initPage
()
})
const
initPage
=
async
()
=>
{
try
{
loading
.
value
=
true
await
initQueryParams
()
await
Promise
.
all
([
loadUserRights
(),
loadValidationFormulas
(),
loadTemplateItems
()
])
await
setFormItemRight
()
await
loadTableData
()
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`保存失败:
${
error
.
message
}
`
,
status
:
'error'
}
)
showErrorMessage
(
`初始化页面失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
const
childMultiTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
setMultiColumnTableRef
=
(
el
:
any
,
index
:
string
)
=>
{
if
(
el
)
{
childMultiTableRefs
.
value
[
index
]
=
el
;
}
};
const
initQueryParams
=
()
=>
{
const
{
taskId
,
taskName
,
tplId
,
tplName
,
tplCode
,
comfrom
}
=
route
.
query
const
getAllMultiTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childMultiTableRefs
.
value
)
{
const
child
=
childMultiTableRefs
.
value
[
pcode
];
if
(
child
)
{
const
datas
=
child
.
getFormData
()
let
rind
=
0
for
(
const
obj
of
datas
)
{
rind
++
Object
.
keys
(
obj
).
forEach
(
code
=>
{
setFormValues
(
pcode
,
code
,
obj
[
code
],
rind
)
});
}
}
}
};
if
(
taskId
)
queryParam
.
value
.
taskId
=
Number
(
taskId
)
if
(
taskName
)
queryParam
.
value
.
taskName
=
String
(
taskName
)
if
(
tplId
)
queryParam
.
value
.
tplId
=
Number
(
tplId
)
if
(
tplName
)
queryParam
.
value
.
tplName
=
String
(
tplName
)
if
(
tplCode
)
queryParam
.
value
.
tplCode
=
String
(
tplCode
)
if
(
comfrom
)
queryParam
.
value
.
comfrom
=
String
(
comfrom
)
}
const
childAttachTableRefs
=
ref
<
Record
<
string
,
any
>>
({})
const
setAttachTableRef
=
(
el
:
any
,
index
:
string
)
=>
{
if
(
el
)
{
childAttachTableRefs
.
value
[
index
]
=
el
;
const
loadUserRights
=
async
()
=>
{
userAllocItems
.
value
=
await
findUserRightForTplItem
({
tplid
:
queryParam
.
value
.
tplId
,
taskid
:
queryParam
.
value
.
taskId
})
}
const
loadValidationFormulas
=
async
()
=>
{
validFormula
.
value
=
await
getTblvalidFormula
({
tplid
:
queryParam
.
value
.
tplId
})
}
const
loadTemplateItems
=
async
()
=>
{
const
items
=
await
allTplItems
({
tplid
:
queryParam
.
value
.
tplId
})
tplItemMap
.
value
=
items
.
reduce
((
acc
:
Record
<
string
,
TplItemMapValue
>
,
item
)
=>
{
const
key
=
`
${
item
.
pcode
}
_
${
item
.
xmlcode
}
`
acc
[
key
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
}
};
return
acc
},
{})
}
const
getAttachTableFormData
=
async
()
=>
{
for
(
const
pcode
in
childAttachTableRefs
.
value
)
{
const
child
=
childAttachTableRefs
.
value
[
pcode
];
if
(
child
)
{
const
datas
=
child
.
getFormData
()
const
setFormItemRight
=
()
=>
{
tableFormData
.
forEach
((
row
:
TableRow
)
=>
{
if
(
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
row
.
content
.
forEach
((
item
:
ContentItem
)
=>
{
if
(
item
.
field
)
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
item
.
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
for
(
const
code
in
datas
)
{
await
setFormValues
(
pcode
,
code
,
datas
[
code
],
1
)
if
(
item
.
hasRight
)
{
const
{
formula
}
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
item
.
hasValidFormula
=
!!
formula
item
.
matchedFormula
=
formula
}
}
})
}
};
const
setFormValues
=
async
(
pcode
:
string
,
code
:
string
,
valData
:
any
,
rind
:
number
)
=>
{
if
(
!
valData
)
return
;
let
vals
=
Array
.
isArray
(
valData
)
?
valData
.
join
(
','
)
:
valData
let
strKey
=
getFieldKey
(
pcode
,
code
)
const
item
=
tplItemMap
.
value
[
strKey
];
const
{
pid
,
itemid
}
=
item
??
{};
let
tempForm
:
FormData
=
{
id
:
null
,
rind
:
rind
,
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
itemid
,
itempid
:
pid
,
content
:
vals
};
formValues
.
value
.
push
(
tempForm
)
})
}
async
function
setData
()
{
// 保存相关
const
saveBatch
=
async
()
=>
{
try
{
loading
.
value
=
true
;
const
taskId
=
queryParam
.
value
.
taskId
;
const
tplid
=
queryParam
.
value
.
tplId
;
if
(
!
await
validateForm
())
return
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
]);
await
prepareFormData
()
await
setTplItemMap
();
const
recordData
=
await
queryRecord
({
taskid
:
taskId
,
tplid
:
tplid
});
if
(
formValues
.
value
.
length
===
0
)
{
showWarningMessage
(
'没有需要保存的数据'
)
return
}
const
valueObj
:
Record
<
string
,
any
>
=
{};
for
(
const
data
of
recordData
)
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
;
valueObj
[
key
]
=
data
.
content
;
await
batchSaveOrUpdateBeforeDelete
(
formValues
.
value
)
showSuccessMessage
(
'保存成功'
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`保存失败:
${
error
.
message
}
`
)
}
}
const
shouldSkip
=
(
key
:
string
)
=>
{
const
refsToCheck
=
[
childMultiTableRefs
.
value
,
childAttachTableRefs
.
value
,
];
const
validateForm
=
async
():
Promise
<
boolean
>
=>
{
const
$table
=
tableRef
.
value
if
(
!
$table
)
return
true
for
(
const
refs
of
refsToCheck
)
{
for
(
const
pcode
of
Object
.
keys
(
refs
))
{
if
(
key
.
startsWith
(
pcode
+
'_'
))
{
if
(
key
===
"TB9006_COL64"
)
{
return
false
;
}
return
true
;
const
checkResult
=
$table
.
validate
()
if
(
!
checkResult
)
{
showErrorMessage
(
'表单验证失败,请检查填写内容'
)
return
false
}
return
true
}
const
prepareFormData
=
async
()
=>
{
formValues
.
value
=
[]
// 处理主表单数据
for
(
const
[
key
,
value
]
of
Object
.
entries
(
formData
))
{
if
(
value
!==
undefined
&&
value
!==
null
&&
value
!==
''
)
{
const
[
pcode
,
code
]
=
key
.
split
(
'_'
)
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
return
false
;
};
for
(
const
strKey
of
Object
.
keys
(
tplItemMap
.
value
))
{
if
(
shouldSkip
(
strKey
))
{
continue
;
}
// 处理子表格数据
await
Promise
.
all
([
collectAttachTableData
(),
collectMultiTableData
(),
collectVxeTableData
()
])
}
const
item
=
tplItemMap
.
value
[
strKey
];
if
(
!
item
)
continue
;
const
addFormValue
=
async
(
pcode
:
string
,
code
:
string
,
value
:
any
,
rind
:
number
)
=>
{
if
(
!
value
)
return
const
{
pid
,
itemid
,
formTp
}
=
item
;
const
dataVal
=
valueObj
[
`
${
pid
}
_
${
itemid
}
_1`
];
const
stringValue
=
Array
.
isArray
(
value
)
?
value
.
join
(
','
)
:
String
(
value
)
const
key
=
getFieldKey
(
pcode
,
code
)
const
item
=
tplItemMap
.
value
[
key
]
if
(
!
dataVal
)
continue
;
if
(
!
item
)
return
if
(
formTp
===
'checkbox'
)
{
formData
[
strKey
]
=
dataVal
?.
split
(
","
)
||
[];
}
else
{
formData
[
strKey
]
=
dataVal
||
""
;
}
const
formItem
:
FormDataItem
=
{
id
:
null
,
rind
,
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
,
tplcode
:
code
,
itemid
:
item
.
itemid
,
itempid
:
item
.
pid
,
content
:
stringValue
}
for
(
const
pcode
of
Object
.
keys
(
childAttachTableRefs
.
value
))
{
const
child
=
childAttachTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
(
key
=>
key
.
startsWith
(
pcode
+
'_'
)
);
const
attachTableData
:
Record
<
string
,
any
>
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
const
{
pid
,
itemid
,
formTp
,
code
}
=
tmpData
;
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
;
formValues
.
value
.
push
(
formItem
)
}
if
(
formTp
===
'checkbox'
)
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]?.
split
(
","
)
||
[];
}
else
{
attachTableData
[
code
]
=
valueObj
[
valueKey
]
||
""
;
// 数据收集函数
const
collectAttachTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childAttachTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
await
child
.
getFormData
()
for
(
const
[
code
,
value
]
of
Object
.
entries
(
datas
))
{
await
addFormValue
(
pcode
,
code
,
value
,
1
)
}
}
child
.
setFormData
(
attachTableData
);
}
}
for
(
const
pcode
of
Object
.
keys
(
childMultiTableRefs
.
value
))
{
const
child
=
childMultiTableRefs
.
value
[
pcode
];
const
matchingKeys
=
Object
.
keys
(
tplItemMap
.
value
).
filter
(
key
=>
key
.
startsWith
(
pcode
+
'_'
)
);
const
rowsMap
:
Record
<
string
,
Record
<
string
,
any
>>
=
{};
for
(
const
key
of
matchingKeys
)
{
const
tmpData
=
tplItemMap
.
value
[
key
];
if
(
!
tmpData
)
continue
;
const
{
pid
,
itemid
,
code
}
=
tmpData
;
const
valKeys
=
Object
.
keys
(
valueObj
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
);
for
(
const
valKey
of
valKeys
)
{
const
parts
=
valKey
.
split
(
'_'
);
const
rind
=
parts
[
2
];
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{};
const
collectMultiTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMultiTableRefs
.
value
))
{
if
(
child
)
{
const
datas
=
await
child
.
getFormData
()
let
rind
=
0
for
(
const
obj
of
datas
)
{
rind
++
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
rowsMap
[
rind
][
code
]
=
valueObj
[
valKey
];
}
}
const
tableDatas
=
Object
.
values
(
rowsMap
);
nextTick
();
child
.
setFormData
(
tableDatas
);
}
}
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
});
}
finally
{
loading
.
value
=
false
;
const
collectVxeTableData
=
async
()
=>
{
for
(
const
[
pcode
,
child
]
of
Object
.
entries
(
childMyVexTableRefs
.
value
))
{
if
(
child
)
{
const
formData
=
await
child
.
getFormData
()
let
rind
=
0
// 表格数据
for
(
const
obj
of
formData
.
datas
)
{
rind
++
for
(
const
[
code
,
value
]
of
Object
.
entries
(
obj
))
{
await
addFormValue
(
pcode
,
code
,
value
,
rind
)
}
}
if
(
formData
.
footer
)
{
for
(
const
[
code
,
value
]
of
Object
.
entries
(
formData
.
footer
))
{
await
addFormValue
(
formData
.
footerPcode
,
code
,
value
,
1
)
}
}
}
}
}
async
function
setTplItemMap
()
{
// 数据加载
const
loadTableData
=
async
()
=>
{
try
{
const
tplid
=
queryParam
.
value
.
tplId
const
tplItem
=
await
allTplItems
({
tplid
:
tplid
,
})
loading
.
value
=
true
tplItemMap
.
value
=
{}
tplItem
.
forEach
((
item
:
any
)
=>
{
let
strKey
=
getFieldKey
(
item
.
pcode
,
item
.
xmlcode
)
tplItemMap
.
value
[
strKey
]
=
{
pid
:
item
.
pid
,
itemid
:
item
.
id
,
formTp
:
item
.
formTp
,
code
:
item
.
xmlcode
}
// 清空现有数据
Object
.
keys
(
formData
).
forEach
(
key
=>
delete
formData
[
key
])
const
recordData
=
await
queryRecord
({
taskid
:
queryParam
.
value
.
taskId
,
tplid
:
queryParam
.
value
.
tplId
})
}
catch
(
error
:
any
)
{
VxeUI
.
modal
.
message
({
content
:
`加载数据失败:
${
error
.
message
}
`
,
status
:
'error'
})
}
}
// 校验数据
const
validateData
=
()
=>
{
drawerVisible
.
value
=
true
}
// 转换记录数据为便于查找的结构
const
valueMap
=
recordData
.
reduce
((
acc
:
Record
<
string
,
string
>
,
data
)
=>
{
const
key
=
`
${
data
.
itempid
}
_
${
data
.
itemid
}
_
${
data
.
rind
}
`
acc
[
key
]
=
data
.
content
return
acc
},
{})
// 校验结果抽屉显示时触发
const
handleValidationDrawerShow
=
()
=>
{
performValidation
()
// 填充表格数据
await
fillTableData
(
valueMap
)
}
catch
(
error
:
any
)
{
showErrorMessage
(
`加载数据失败:
${
error
.
message
}
`
)
}
finally
{
loading
.
value
=
false
}
}
// 执行校验
const
performValidation
=
()
=>
{
validationResultsList
.
value
=
[]
const
process
:
string
[]
=
[]
process
.
push
(
'开始校验'
)
process
.
push
(
`找到
${
validFormula
.
value
.
length
}
个验证公式`
)
validFormula
.
value
.
forEach
((
formulaItem
:
any
)
=>
{
process
.
push
(
`开始校验公式:
${
formulaItem
.
des
}
`
)
const
result
=
evaluateFormula
(
formulaItem
.
formula
,
formulaItem
.
des
,
process
)
if
(
result
)
{
validationResultsList
.
value
.
push
(
result
)
const
fillTableData
=
async
(
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
row
of
tableFormData
)
{
if
(
!
row
.
type
&&
row
.
content
)
{
await
fillSimpleFields
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'AttachTable'
&&
row
.
datas
)
{
await
fillAttachTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'MultiColumnTable'
&&
row
.
content
)
{
await
fillMultiColumnTable
(
row
,
valueMap
)
}
else
if
(
row
.
type
===
'VxeTable'
&&
row
.
columns
)
{
await
fillVxeTable
(
row
,
valueMap
)
}
}
})
process
.
push
(
'校验完成'
)
}
// 解析和执行验证公式
const
evaluateFormula
=
(
formula
:
string
,
description
:
string
,
process
:
string
[]):
any
=>
{
try
{
const
fieldMatch
=
formula
.
match
(
/
\[(\w
+
)\]
/
)
if
(
!
fieldMatch
)
{
process
.
push
(
`跳过: 无法解析公式字段
${
formula
}
`
)
return
null
}
const
fillSimpleFields
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
if
(
!
row
.
content
||
!
Array
.
isArray
(
row
.
content
))
return
const
fieldName
=
fieldMatch
[
1
]
const
row
=
tableFormData
.
find
((
r
:
any
)
=>
r
.
content
?.
some
((
c
:
any
)
=>
c
.
field
===
fieldName
))
if
(
!
row
)
{
process
.
push
(
`跳过: 未找到字段
${
fieldName
}
`
)
return
null
}
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
const
fieldItem
=
row
.
content
.
find
((
c
:
any
)
=>
c
.
field
===
fieldName
)
if
(
!
fieldItem
)
{
process
.
push
(
`跳过: 未找到字段
${
fieldName
}
`
)
return
null
}
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
const
strKey
=
getFieldKey
(
row
.
code
,
fieldName
)
const
fieldValue
=
formData
[
strKey
]
const
{
pid
,
itemid
,
formTp
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
// 检查是否有空字段规则
const
emptyFieldRule
=
validFormula
.
value
.
find
((
f
:
any
)
=>
f
.
formula
.
includes
(
fieldName
)
&&
f
.
des
.
includes
(
'空字段'
)
)
if
(
dataVal
===
undefined
)
continue
if
(
emptyFieldRule
&&
(
!
fieldValue
||
fieldValue
===
''
))
{
process
.
push
(
`跳过: 字段
${
fieldName
}
为空,跳过空字段规则`
)
return
null
if
(
formTp
===
'checkbox'
)
{
formData
[
key
]
=
dataVal
.
split
(
','
).
filter
(
Boolean
)
}
else
{
formData
[
key
]
=
dataVal
}
const
expression
=
formula
.
replace
(
/
\[(\w
+
)\]
/g
,
(
_
,
field
)
=>
{
const
value
=
formData
[
getFieldKey
(
row
.
code
,
field
)]
return
value
!==
undefined
?
`Number(
${
value
}
)`
:
'0'
})
process
.
push
(
`执行公式:
${
expression
}
`
)
const
isValid
=
eval
(
expression
)
// 处理特殊字段类型
if
(
item
.
type
===
'checkboxAndInput'
&&
item
.
optionItemField
)
{
await
fillCheckboxInputFields
(
row
,
item
,
valueMap
)
}
return
{
field
:
fieldName
,
description
:
description
,
formula
:
formula
,
isValid
:
isValid
,
fieldValue
:
fieldValue
,
rowCode
:
row
.
code
if
(
item
.
otherField
)
{
await
fillOtherField
(
row
,
item
,
valueMap
)
}
}
catch
(
error
)
{
process
.
push
(
`公式执行错误:
${
error
instanceof
Error
?
error
.
message
:
String
(
error
)}
`
)
return
null
}
}
// 显示验证公式帮助
const
showValidationHelp
=
(
formulaItem
:
any
,
event
:
MouseEvent
)
=>
{
const
rect
=
(
event
.
target
as
HTMLElement
).
getBoundingClientRect
()
validationTooltipPosition
.
value
=
{
left
:
rect
.
left
+
rect
.
width
/
2
,
top
:
rect
.
top
-
10
}
validationTooltipContent
.
value
=
`验证公式:
${
formulaItem
.
formula
}
\n说明:
${
formulaItem
.
des
}
`
validationTooltipVisible
.
value
=
true
}
const
fillCheckboxInputFields
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
for
(
const
field
of
item
.
optionItemField
||
[])
{
const
key
=
getFieldKey
(
row
.
code
,
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
// 关闭验证公式帮助
const
closeTooltip
=
()
=>
{
validationTooltipVisible
.
value
=
false
}
const
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
// 打开历史填报抽屉
const
handleOpenHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
true
if
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
}
}
// 关闭历史填报抽屉
const
closeHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
false
}
const
fillOtherField
=
async
(
row
:
TableRow
,
item
:
ContentItem
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
key
=
getFieldKey
(
row
.
code
,
item
.
otherField
!
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
return
interface
FormulaItem
{
formula
:
string
des
:
string
[
key
:
string
]:
any
}
const
{
pid
,
itemid
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
interface
InputError
{
message
:
string
formula
:
string
error
?:
string
if
(
dataVal
!==
undefined
)
{
formData
[
key
]
=
dataVal
}
}
const
fieldKeys
=
computed
(()
=>
Object
.
keys
(
formData
))
const
fillAttachTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childAttachTableRefs
.
value
[
row
.
code
]
if
(
!
child
)
return
const
showHelpIcon
=
(
rowCode
:
string
,
field
:
string
,
item
:
any
):
boolean
=>
{
if
(
item
.
hasValidFormula
!==
undefined
)
{
return
item
.
hasValidFormula
===
true
}
const
tableData
:
Record
<
string
,
any
>
=
{}
for
(
const
data
of
row
.
datas
||
[])
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
data
))
{
if
(
typeof
value
===
'object'
&&
value
.
field
)
{
const
fieldKey
=
getFieldKey
(
row
.
code
,
value
.
field
)
const
tplItem
=
tplItemMap
.
value
[
fieldKey
]
if
(
!
tplItem
)
continue
const
key
=
getFieldKey
(
rowCode
,
field
)
const
hasRight
=
userAllocItems
.
value
.
includes
(
key
)
const
{
pid
,
itemid
,
formTp
,
code
}
=
tplItem
const
valueKey
=
`
${
pid
}
_
${
itemid
}
_1`
const
dataVal
=
valueMap
[
valueKey
]
if
(
!
hasRight
)
return
false
if
(
dataVal
)
{
tableData
[
code
]
=
formTp
===
'checkbox'
?
dataVal
.
split
(
','
)
:
dataVal
}
}
}
}
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
field
)
return
!!
formulaResult
.
formula
child
.
setFormData
(
tableData
)
}
const
setFormItemRight
=
()
=>
{
console
.
log
(
'开始设置表单权限...'
)
console
.
log
(
'userAllocItems 数量:'
,
userAllocItems
.
value
.
length
)
console
.
log
(
'validFormula 数量:'
,
validFormula
.
value
.
length
)
const
fillMultiColumnTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMultiTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
content
))
return
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
)
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
console
.
log
(
`字段
${
key
}
: hasRight =
${
item
.
hasRight
}
`
)
for
(
const
item
of
row
.
content
)
{
if
(
!
item
.
field
)
continue
if
(
item
.
hasRight
)
{
const
formulaResult
=
findEarliestFormulaForField
(
validFormula
.
value
,
item
.
field
)
const
key
=
getFieldKey
(
row
.
code
,
item
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
)
continue
item
.
hasValidFormula
=
!!
formulaResult
.
formula
item
.
matchedFormula
=
formulaResult
.
formula
||
null
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
console
.
log
(
`字段
${
key
}
: hasValidFormula =
${
item
.
hasValidFormula
}
`
)
}
else
{
item
.
hasValidFormula
=
false
item
.
matchedFormula
=
null
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{}
}
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
})
}
})
console
.
log
(
'表单权限设置完成'
)
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
const
findEarliestFormulaForField
=
(
formulas
:
FormulaItem
[],
field
:
string
):
{
formula
:
FormulaItem
|
null
,
index
:
number
}
=>
{
let
result
=
{
formula
:
null
,
index
:
Infinity
}
const
fillVxeTable
=
async
(
row
:
TableRow
,
valueMap
:
Record
<
string
,
string
>
)
=>
{
const
child
=
childMyVexTableRefs
.
value
[
row
.
code
]
if
(
!
child
||
!
Array
.
isArray
(
row
.
columns
))
return
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
)
const
rowsMap
:
Record
<
string
,
Record
<
string
,
string
>>
=
{}
if
(
match
&&
match
.
index
<
result
.
index
)
{
result
=
{
formula
:
f
,
index
:
match
.
index
}
}
})
for
(
const
column
of
row
.
columns
)
{
if
(
!
column
.
field
)
continue
return
result
}
const
key
=
getFieldKey
(
row
.
code
,
column
.
field
)
const
tplItem
=
tplItemMap
.
value
[
key
]
if
(
!
tplItem
||
Object
.
keys
(
tplItem
).
length
===
0
)
continue
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
)
const
{
pid
,
itemid
,
code
}
=
tplItem
const
valueKeys
=
Object
.
keys
(
valueMap
).
filter
(
k
=>
k
.
startsWith
(
`
${
pid
}
_
${
itemid
}
_`
)
)
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
for
(
const
valueKey
of
valueKeys
)
{
const
[,
,
rind
]
=
valueKey
.
split
(
'_'
)
if
(
!
rowsMap
[
rind
])
{
rowsMap
[
rind
]
=
{}
}
rowsMap
[
rind
][
code
]
=
valueMap
[
valueKey
]
}
})
}
})
nextTick
(()
=>
{
console
.
log
(
'帮助图标刷新完成'
)
})
const
tableDatas
=
Object
.
values
(
rowsMap
)
child
.
setFormData
(
tableDatas
)
}
// 校验相关
const
validateData
=
()
=>
{
drawerVisible
.
value
=
true
validationDrawerRef
.
value
.
setValidateData
(
validFormula
.
value
,
formData
);
}
const
handleInputBlur
=
(
rowCode
:
string
,
field
:
string
,
formula
?:
string
)
=>
{
...
...
@@ -938,10 +919,26 @@ const buildExpression = (formula: string, rowCode: string): string => {
for
(
const
fieldName
of
uniqueFields
)
{
const
key
=
getFieldKey
(
rowCode
,
fieldName
)
const
value
=
formData
[
key
]
if
(
value
!==
undefined
)
{
// 检查字段类型,如果是日期类型,转换为时间戳进行比较
const
row
=
tableFormData
.
find
((
r
:
TableRow
)
=>
r
.
code
===
rowCode
)
let
processedValue
=
value
if
(
row
&&
row
.
content
&&
Array
.
isArray
(
row
.
content
))
{
const
fieldItem
=
row
.
content
.
find
((
c
:
ContentItem
)
=>
c
.
field
===
fieldName
)
if
(
fieldItem
&&
fieldItem
.
type
===
'date'
)
{
// 将日期字符串转换为时间戳(毫秒)
const
date
=
new
Date
(
value
)
if
(
!
isNaN
(
date
.
getTime
()))
{
processedValue
=
date
.
getTime
()
}
}
}
expression
=
expression
.
replace
(
new
RegExp
(
`\\b
${
fieldName
}
\\b`
,
'g'
),
`Number(
${
v
alue
}
)`
`Number(
${
processedV
alue
}
)`
)
}
}
...
...
@@ -949,6 +946,7 @@ const buildExpression = (formula: string, rowCode: string): string => {
return
expression
}
// 提示工具相关
const
toggleTooltip
=
(
rowCode
:
string
,
field
:
string
)
=>
{
const
key
=
getFieldKey
(
rowCode
,
field
)
if
(
hoveredKey
.
value
===
key
)
{
...
...
@@ -976,285 +974,171 @@ const closeAllTooltips = () => {
hoveredKey
.
value
=
''
showErrorTooltip
.
value
=
false
hoveredErrorKey
.
value
=
''
validationTooltipVisible
.
value
=
false
}
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
VxeUI
.
modal
.
message
({
content
:
message
,
status
})
// 历史填报
const
handleOpenHistoryDrawer
=
()
=>
{
historyFillCheckRef
.
value
?.
onDrawerShow
(
queryParam
.
value
.
tplId
)
historyDrawerVisible
.
value
=
true
}
</
script
>
<
style
lang=
"less"
scoped
>
/* 基础字体和排版 */
.bank-report-table {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
color: #000;
margin: 5px auto;
width: 90%;
}
const
closeHistoryDrawer
=
()
=>
{
historyDrawerVisible
.
value
=
false
/* 表格样式 */
.custom-table,
.attachment-table {
width: 100%;
border: 1px solid #000;
border-collapse: collapse;
}
/* 加载遮罩层样式 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
// 校验结果处理
const
handleValidationResultClick
=
(
result
:
any
)
=>
{
const
message
=
`字段
${
result
.
field
}
的校验结果:
${
result
.
isValid
?
'通过'
:
'失败'
}
`
const
status
=
result
.
isValid
?
'success'
:
'error'
.loading-spinner {
display: flex;
flex-direction: column;
align-items: center;
VxeUI
.
modal
.
message
({
content
:
message
,
status
})
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 2s linear infinite;
}
// 工具函数
const
findEarliestFormulaForField
=
(
formulas
:
FormulaItem
[],
field
:
string
):
{
formula
:
FormulaItem
|
null
,
index
:
number
}
=>
{
let
result
=
{
formula
:
null
,
index
:
Infinity
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
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
)
100%
{
transform: rotate(360deg);
if
(
match
&&
match
.
index
<
result
.
index
)
{
result
=
{
formula
:
f
,
index
:
match
.
index
}
}
}
.loading-text {
margin-top: 10px;
color: #666;
font-size: 14px;
}
/* 校验结果样式 */
.validation-results {
padding: 20px;
height: 600px;
overflow-y: auto;
}
.result-summary {
display: flex;
gap: 20px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.summary-item {
display: flex;
flex-direction: column;
}
.summary-label {
font-size: 12px;
color: #666;
}
.summary-value {
font-size: 16px;
font-weight: bold;
color: #333;
}
.summary-value.success {
color: #52c41a;
}
.summary-value.error {
color: #ff4d4f;
}
})
.results-list {
margin-top: 10px;
return
result
}
.result-item {
padding: 10px;
margin-bottom: 8px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
// 消息提示
const
showSuccessMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'success'
})
}
.result-item:hover
{
background-color: #f5f5f5;
const
showWarningMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'warning'
})
}
.result-item.success {
background-color: #f6ffed;
border-left: 4px solid #52c41a;
const
showErrorMessage
=
(
message
:
string
)
=>
{
VxeUI
.
modal
.
message
({
content
:
message
,
status
:
'error'
})
}
.result-item.error {
background-color: #fff2f0;
border-left: 4px solid #ff4d4f;
// 子组件引用设置
const
setMultiColumnTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMultiTableRefs
.
value
[
code
]
=
el
}
.result-field {
font-weight: bold;
margin-bottom: 5px;
const
setAttachTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childAttachTableRefs
.
value
[
code
]
=
el
}
.result-desc {
font-size: 12px;
color: #666;
margin-bottom: 5px;
const
setMyVxeTableRef
=
(
el
:
any
,
code
:
string
)
=>
{
if
(
el
)
childMyVexTableRefs
.
value
[
code
]
=
el
}
</
script
>
.result-value {
<
style
lang=
"less"
scoped
>
// Base table styles
.bank-report-table {
font-family: "SimSun", "宋体", serif;
font-size: 12px;
color: #999;
}
color: #000;
margin: 5px auto;
width: 90%;
position: relative;
.result-error {
font-size: 12px;
color: #ff4d4f;
margin-top: 5px;
}
// Shared table styles
.custom-table, .attachment-table {
width: 100%;
border: 1px solid #000;
/* 验证公式帮助提示样式 */
.validation-tooltip {
position: absolute;
background-color: #333;
color: #fff;
padding: 10px;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
.vxe-header--column,
.vxe-body--column {
border-right: 1px solid #000;
border-bottom: 1px solid #000;
padding: 5px;
}
}
}
.tooltip-content {
margin: 0;
white-space: pre-wrap
;
// 表格信息文本
.table-info-text {
font-weight: bold
;
}
/* 表格单元格样式 */
.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;
// 项目名称
.project-name {
font-weight: bold;
font-size: 14px;
}
/
* 内容单元格 */
/
/ Cell styles
.content-cell {
line-height: 1.8;
}
/* 输入框包装器 */
.input-wrapper {
position: relative;
// 表单控件组
.radio-group, .checkbox-group {
display: inline-block;
}
margin-left: 10px;
/* 输入框样式 */
.table-input,
.vxe-input {
height: 24px;
.vxe-radio, .vxe-checkbox {
margin-left: 0px;
white-space: nowrap;
line-height: 30px;
min-width: 150px;
margin: 0 2px;
padding: 0 5px;
border: none;
border-bottom: 1px solid #333;
background: transparent;
text-align: center;
}
padding: 0px 10px;
/* 表单元素的标签和图标 */
.vxe-radio--label,
.vxe-checkbox--label {
&--label {
font-size: 12px;
}
}
.vxe-radio--icon,
.vxe-checkbox--icon {
&--icon {
font-size: 12px;
}
}
}
/* 单选框组和复选框组布局 */
.vxe-radio-group,
.vxe-checkbox-group {
display: inline-block;
margin-right: 5px;
max-width: 99%;
}
/* 其他辅助样式 */
.radio-group,
.checkbox-group {
// VXE specific overrides
.vxe {
&-radio-group, &-checkbox-group {
display: inline-block;
margin-right: 5px;
max-width: 99%;
min-width: 50%;
}
.vxe-radio,
.vxe-checkbox {
margin: 5px 5px 5px 30px;
white-space: nowrap;
}
/* 附件部分样式 */
.attachments {
margin-top: 20px;
width: 100%;
}
}
.attachments h4 {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
&-cell {
padding: 5px;
}
}
.attachment-table {
width: 100%;
border: 1px solid #000;
border-collapse: collapse;
.vxe-input {
border-bottom: 1px solid #333;
text-align: center;
margin: 5px 3px;
padding: 0;
border-top: none;
border-left: none;
border-right: none;
background: transparent;
}
/* 其他特殊块 */
blockquote {
padding: 0 5px;
color: black;
cursor: pointer;
// 输入框包装器
.input-wrapper {
position: relative;
display: inline-block;
}
/
* 单位文本 */
/
/ 单位文本
.unit {
margin-left: 5px;
font-size: 12px;
}
/
* 帮助图标 */
/
/ 帮助图标
.help-icon {
margin-left: 5px;
cursor: pointer;
...
...
@@ -1269,29 +1153,13 @@ blockquote {
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);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
/
* 错误图标 */
/
/ 错误图标
.error-icon {
margin-left: 5px;
cursor: pointer;
...
...
@@ -1309,9 +1177,8 @@ blockquote {
}
}
/* 提示框 */
.tooltip,
.error-tooltip {
// 提示框
.tooltip, .error-tooltip {
position: absolute;
top: 100%;
left: 0;
...
...
@@ -1355,4 +1222,42 @@ blockquote {
border-color: transparent transparent #8b0000 transparent;
}
}
// 加载遮罩
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.loading-spinner {
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
font-size: 14px;
color: #666;
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/baosong/review/BaosongTaskReviewList.vue
浏览文件 @
12b89e58
...
...
@@ -3,7 +3,7 @@
<!--引用表格-->
<BasicTable
@
register=
"registerTable"
:rowSelection=
"rowSelection"
>
<template
#
action=
"
{ record }">
<TableAction
:actions=
"getTableAction(record)"
:dropDownActions=
"getDropDownAction(record)"
/>
<TableAction
:actions=
"getTableAction(record)"
/>
</
template
>
</BasicTable>
<!-- 表单区域 -->
...
...
@@ -93,7 +93,6 @@
}
async
function
handlePreview
(
record
)
{
alert
(
JSON
.
stringify
(
record
));
await
gotoPage
(
record
);
}
...
...
@@ -205,7 +204,9 @@ const gotoPage = async (item) => {
path
:
targetPath
,
query
:
{
taskId
:
taskid
,
taskName
:
""
,
tplId
:
tplid
,
tplName
:
""
,
comfrom
:
'archive'
}
});
...
...
zrch-risk-client-39/src/views/demo/form/index.vue
浏览文件 @
12b89e58
...
...
@@ -597,7 +597,7 @@
keyword
.
value
=
value
;
}
function
areaChange
(
value
)
{
alert
(
value
);
//
alert(value);
}
return
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/components/StProblemCheckPlanForm.vue
浏览文件 @
12b89e58
...
...
@@ -42,7 +42,6 @@
let
formData
=
{};
const
queryById
=
'/problem/stProblemCheck/queryById'
;
async
function
initFormData
()
{
alert
(
1
)
let
params
=
{
id
:
props
.
formData
.
dataId
};
const
data
=
await
defHttp
.
get
({
url
:
queryById
,
params
});
formData
=
{
...
data
};
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论