|
|
|
|
@ -77,6 +77,50 @@
|
|
|
|
|
clearable
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<!-- 高级查询(仅在选择了子流程时显示) -->
|
|
|
|
|
<template v-if="selectedSubprocess && allModelFields.length > 0">
|
|
|
|
|
<el-form-item label="字段">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="filterForm.field_id"
|
|
|
|
|
placeholder="请选择字段"
|
|
|
|
|
style="width: 180px"
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="field in allModelFields"
|
|
|
|
|
:key="field.id"
|
|
|
|
|
:value="field.id"
|
|
|
|
|
:label="field.label"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="类型">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="filterForm.operator"
|
|
|
|
|
placeholder="请选择类型"
|
|
|
|
|
style="width: 150px"
|
|
|
|
|
clearable
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="op in operatorOptions"
|
|
|
|
|
:key="op.id"
|
|
|
|
|
:value="op.id"
|
|
|
|
|
:label="op.label"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="关键词">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="filterForm.field_keyword"
|
|
|
|
|
placeholder="请输入关键词"
|
|
|
|
|
style="width: 200px"
|
|
|
|
|
clearable
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" @click="handleSearch">
|
|
|
|
|
<el-icon><Search /></el-icon>
|
|
|
|
|
@ -104,23 +148,34 @@
|
|
|
|
|
:key="field.id"
|
|
|
|
|
:prop="`data.${field.name}`"
|
|
|
|
|
:label="field.label"
|
|
|
|
|
:min-width="getFieldWidth(field.type)"
|
|
|
|
|
:min-width="field.type === 'relation' ? 300 : getFieldWidth(field.type)"
|
|
|
|
|
>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ formatFieldValue(scope.row.data, field) }}
|
|
|
|
|
<div v-if="field.type === 'relation'" class="relation-field-renderer">
|
|
|
|
|
<el-table
|
|
|
|
|
:data="getRelationFieldData(scope.row.data, field)"
|
|
|
|
|
border
|
|
|
|
|
size="small"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
:show-header="true"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column
|
|
|
|
|
v-for="subField in getSubFormFields(field.name)"
|
|
|
|
|
:key="subField.id"
|
|
|
|
|
:prop="subField.name"
|
|
|
|
|
:label="subField.label"
|
|
|
|
|
:min-width="100"
|
|
|
|
|
>
|
|
|
|
|
<template #default="subScope">
|
|
|
|
|
{{ formatSubFieldValue(subScope.row, subField) }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
<span v-else>{{ formatFieldValue(scope.row.data, field) }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table-column prop="custom_model" label="流程类型" min-width="150">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<div class="type-cell">
|
|
|
|
|
<el-icon :size="16" v-if="scope.row.custom_model?.icon || scope.row.customModel?.icon">
|
|
|
|
|
<component :is="getIcon(scope.row.custom_model?.icon || scope.row.customModel?.icon)" />
|
|
|
|
|
</el-icon>
|
|
|
|
|
<span>{{ scope.row.custom_model?.name || scope.row.customModel?.name || '-' }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<div class="title-cell">
|
|
|
|
|
@ -190,7 +245,7 @@
|
|
|
|
|
查看
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="scope.row.status === 0"
|
|
|
|
|
v-if="hasGlobalFlowSupervisionRole"
|
|
|
|
|
type="success"
|
|
|
|
|
link
|
|
|
|
|
size="small"
|
|
|
|
|
@ -218,7 +273,7 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, onMounted, watch } from 'vue'
|
|
|
|
|
import { ref, onMounted, watch, computed } from 'vue'
|
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import {
|
|
|
|
|
@ -233,14 +288,19 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
|
|
|
|
import { preApprovalProcessConfigAPI, oaFlowAPI } from '@/utils/api'
|
|
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
|
|
import config from '@/config'
|
|
|
|
|
import { useUserStore } from '@/store/user'
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
const userStore = useUserStore()
|
|
|
|
|
|
|
|
|
|
// 加载状态
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const subprocessLoading = ref(false)
|
|
|
|
|
const loadingYears = ref(false)
|
|
|
|
|
|
|
|
|
|
// 请求序列号,用于防止竞态条件
|
|
|
|
|
let requestSequence = 0
|
|
|
|
|
|
|
|
|
|
// 可用子流程列表
|
|
|
|
|
const availableSubprocesses = ref([])
|
|
|
|
|
// 选中的子流程
|
|
|
|
|
@ -251,6 +311,10 @@ const queryType = ref('not-linked')
|
|
|
|
|
|
|
|
|
|
// 动态字段列表(show_in_list=1的字段)
|
|
|
|
|
const dynamicFields = ref([])
|
|
|
|
|
// 子表单字段映射(用于 relation 类型字段)
|
|
|
|
|
const subFormFieldsMap = ref({})
|
|
|
|
|
// 选项数据映射(用于 select 类型字段,key 为 selection_model,value 为选项数组)
|
|
|
|
|
const selectionOptionsMap = ref({})
|
|
|
|
|
|
|
|
|
|
// 表格数据
|
|
|
|
|
const tableData = ref([])
|
|
|
|
|
@ -261,9 +325,22 @@ const selectedYear = ref(null) // 选中的年份
|
|
|
|
|
|
|
|
|
|
// 筛选表单
|
|
|
|
|
const filterForm = ref({
|
|
|
|
|
keyword: ''
|
|
|
|
|
keyword: '',
|
|
|
|
|
field_id: '',
|
|
|
|
|
operator: '',
|
|
|
|
|
field_keyword: ''
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 操作符选项
|
|
|
|
|
const operatorOptions = [
|
|
|
|
|
{ id: 'eq', label: '等于' },
|
|
|
|
|
{ id: 'neq', label: '不等于' },
|
|
|
|
|
{ id: 'like', label: '包含' }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// 所有模型字段(用于高级查询,不仅仅是show_in_list=1的字段)
|
|
|
|
|
const allModelFields = ref([])
|
|
|
|
|
|
|
|
|
|
// 分页
|
|
|
|
|
const pagination = ref({
|
|
|
|
|
currentPage: 1,
|
|
|
|
|
@ -343,10 +420,104 @@ const getFieldWidth = (fieldType) => {
|
|
|
|
|
return widthMap[fieldType] || 120
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取子表单字段
|
|
|
|
|
const getSubFormFields = (fieldName) => {
|
|
|
|
|
return subFormFieldsMap.value[fieldName] || []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取 relation 字段的数据(转换为数组格式)
|
|
|
|
|
const getRelationFieldData = (data, field) => {
|
|
|
|
|
if (!data || !field || !field.name) return []
|
|
|
|
|
|
|
|
|
|
let value = data[field.name]
|
|
|
|
|
|
|
|
|
|
// 如果 value 是字符串,尝试解析为 JSON
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
try {
|
|
|
|
|
value = JSON.parse(value)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('解析 relation 字段 JSON 失败:', value, e)
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保是数组
|
|
|
|
|
if (!Array.isArray(value)) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化子表单字段值
|
|
|
|
|
const formatSubFieldValue = (rowData, subField) => {
|
|
|
|
|
if (!rowData || !subField || !subField.name) return '-'
|
|
|
|
|
|
|
|
|
|
let value = rowData[subField.name]
|
|
|
|
|
|
|
|
|
|
if (value === null || value === undefined || value === '') return '-'
|
|
|
|
|
|
|
|
|
|
switch (subField.type) {
|
|
|
|
|
case 'number':
|
|
|
|
|
case 'money':
|
|
|
|
|
return typeof value === 'number' ? value.toLocaleString('zh-CN') : value
|
|
|
|
|
case 'date':
|
|
|
|
|
if (value) {
|
|
|
|
|
try {
|
|
|
|
|
const date = new Date(value)
|
|
|
|
|
if (!isNaN(date.getTime())) {
|
|
|
|
|
return date.toLocaleDateString('zh-CN')
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('日期格式化失败:', value, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return '-'
|
|
|
|
|
case 'datetime':
|
|
|
|
|
if (value) {
|
|
|
|
|
try {
|
|
|
|
|
return formatDateTime(value)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('日期时间格式化失败:', value, e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return '-'
|
|
|
|
|
case 'select':
|
|
|
|
|
case 'radio':
|
|
|
|
|
// 如果有 selection_model,从 selectionOptionsMap 中查找
|
|
|
|
|
if (subField.selection_model && selectionOptionsMap.value[subField.selection_model]) {
|
|
|
|
|
const options = selectionOptionsMap.value[subField.selection_model]
|
|
|
|
|
const option = options.find(opt =>
|
|
|
|
|
opt.id == value ||
|
|
|
|
|
opt.value == value ||
|
|
|
|
|
String(opt.id) === String(value) ||
|
|
|
|
|
String(opt.value) === String(value)
|
|
|
|
|
)
|
|
|
|
|
if (option) {
|
|
|
|
|
return option.label || option.name || value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有 selection_model 或找不到,尝试使用 subField.options
|
|
|
|
|
if (subField.options && Array.isArray(subField.options)) {
|
|
|
|
|
const option = subField.options.find(opt => opt.value === value || opt.id === value)
|
|
|
|
|
return option ? (option.label || option.name || value) : value
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
default:
|
|
|
|
|
return String(value)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 格式化字段值
|
|
|
|
|
const formatFieldValue = (data, field) => {
|
|
|
|
|
if (!data || !field || !field.name) return '-'
|
|
|
|
|
|
|
|
|
|
// relation 类型已经在模板中特殊处理,这里不需要处理
|
|
|
|
|
if (field.type === 'relation') {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 尝试多种方式获取值
|
|
|
|
|
let value = data[field.name]
|
|
|
|
|
|
|
|
|
|
@ -385,7 +556,21 @@ const formatFieldValue = (data, field) => {
|
|
|
|
|
return '-'
|
|
|
|
|
case 'select':
|
|
|
|
|
case 'radio':
|
|
|
|
|
// 如果是选项类型,尝试显示选项文本
|
|
|
|
|
// 如果有 selection_model,从 selectionOptionsMap 中查找
|
|
|
|
|
if (field.selection_model && selectionOptionsMap.value[field.selection_model]) {
|
|
|
|
|
const options = selectionOptionsMap.value[field.selection_model]
|
|
|
|
|
const option = options.find(opt =>
|
|
|
|
|
opt.id == value ||
|
|
|
|
|
opt.value == value ||
|
|
|
|
|
String(opt.id) === String(value) ||
|
|
|
|
|
String(opt.value) === String(value)
|
|
|
|
|
)
|
|
|
|
|
if (option) {
|
|
|
|
|
return option.label || option.name || value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果没有 selection_model 或找不到,尝试使用 field.options
|
|
|
|
|
if (field.options && Array.isArray(field.options)) {
|
|
|
|
|
const option = field.options.find(opt => opt.value === value || opt.id === value)
|
|
|
|
|
return option ? (option.label || option.name || value) : value
|
|
|
|
|
@ -404,18 +589,30 @@ const formatFieldValue = (data, field) => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载年份列表
|
|
|
|
|
const loadAvailableYears = async () => {
|
|
|
|
|
const loadAvailableYears = async (expectedCustomModelId = null) => {
|
|
|
|
|
if (!selectedSubprocess.value || !selectedSubprocess.value.custom_model_id) {
|
|
|
|
|
availableYears.value = []
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const currentCustomModelId = selectedSubprocess.value.custom_model_id
|
|
|
|
|
|
|
|
|
|
// 如果传入了期望的 custom_model_id,检查是否仍然匹配
|
|
|
|
|
if (expectedCustomModelId && currentCustomModelId !== expectedCustomModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadingYears.value = true
|
|
|
|
|
try {
|
|
|
|
|
const response = await oaFlowAPI.getFlowYears({
|
|
|
|
|
custom_model_id: selectedSubprocess.value.custom_model_id
|
|
|
|
|
custom_model_id: currentCustomModelId
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 再次检查 custom_model_id 是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== currentCustomModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.code === 0 && Array.isArray(response.data)) {
|
|
|
|
|
availableYears.value = response.data
|
|
|
|
|
|
|
|
|
|
@ -435,20 +632,29 @@ const loadAvailableYears = async () => {
|
|
|
|
|
selectedYear.value = new Date().getFullYear()
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载年份列表失败:', error)
|
|
|
|
|
availableYears.value = []
|
|
|
|
|
// 出错时默认使用当年
|
|
|
|
|
selectedYear.value = new Date().getFullYear()
|
|
|
|
|
// 只有在 custom_model_id 仍然匹配时才处理错误
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === currentCustomModelId) {
|
|
|
|
|
console.error('加载年份列表失败:', error)
|
|
|
|
|
availableYears.value = []
|
|
|
|
|
// 出错时默认使用当年
|
|
|
|
|
selectedYear.value = new Date().getFullYear()
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
loadingYears.value = false
|
|
|
|
|
// 只有在 custom_model_id 仍然匹配时才更新加载状态
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === currentCustomModelId) {
|
|
|
|
|
loadingYears.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 年份切换
|
|
|
|
|
const handleYearChange = () => {
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
// 年份切换时只清空关键词,保留高级查询条件
|
|
|
|
|
filterForm.value.keyword = ''
|
|
|
|
|
loadFlowList()
|
|
|
|
|
// 增加请求序列号
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 选择子流程
|
|
|
|
|
@ -458,24 +664,77 @@ const handleSubprocessSelect = async (subprocess) => {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 增加请求序列号,取消之前的请求
|
|
|
|
|
requestSequence++
|
|
|
|
|
const currentSequence = requestSequence
|
|
|
|
|
|
|
|
|
|
// 重置分页和筛选
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
filterForm.value = {
|
|
|
|
|
keyword: '',
|
|
|
|
|
field_id: '',
|
|
|
|
|
operator: '',
|
|
|
|
|
field_keyword: ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectedSubprocess.value = subprocess
|
|
|
|
|
|
|
|
|
|
// 加载模型字段
|
|
|
|
|
await loadModelFields(subprocess.custom_model_id)
|
|
|
|
|
// 清空子表单字段映射、选项数据映射和所有字段
|
|
|
|
|
subFormFieldsMap.value = {}
|
|
|
|
|
selectionOptionsMap.value = {}
|
|
|
|
|
allModelFields.value = []
|
|
|
|
|
|
|
|
|
|
// 先加载年份列表
|
|
|
|
|
await loadAvailableYears()
|
|
|
|
|
// 清空表格数据,避免显示旧数据
|
|
|
|
|
tableData.value = []
|
|
|
|
|
pagination.value.total = 0
|
|
|
|
|
|
|
|
|
|
// 年份加载完成后,再加载流程列表
|
|
|
|
|
if (selectedYear.value) {
|
|
|
|
|
await loadFlowList()
|
|
|
|
|
try {
|
|
|
|
|
// 加载模型字段(传入期望的 custom_model_id 用于验证)
|
|
|
|
|
await loadModelFields(subprocess.custom_model_id, subprocess.custom_model_id)
|
|
|
|
|
|
|
|
|
|
// 检查是否仍然是最新的请求
|
|
|
|
|
if (currentSequence !== requestSequence) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 再次检查 selectedSubprocess 是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== subprocess.custom_model_id) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 先加载年份列表(传入期望的 custom_model_id 用于验证)
|
|
|
|
|
await loadAvailableYears(subprocess.custom_model_id)
|
|
|
|
|
|
|
|
|
|
// 检查是否仍然是最新的请求
|
|
|
|
|
if (currentSequence !== requestSequence) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 再次检查 selectedSubprocess 是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== subprocess.custom_model_id) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 年份加载完成后,再加载流程列表
|
|
|
|
|
if (selectedYear.value && selectedSubprocess.value?.custom_model_id === subprocess.custom_model_id) {
|
|
|
|
|
await loadFlowList(currentSequence)
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 如果请求被取消或出错,不显示错误(可能是用户快速切换导致的)
|
|
|
|
|
if (currentSequence === requestSequence) {
|
|
|
|
|
console.error('加载子流程数据失败:', error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询类型改变
|
|
|
|
|
const handleQueryTypeChange = () => {
|
|
|
|
|
if (selectedSubprocess.value) {
|
|
|
|
|
loadFlowList()
|
|
|
|
|
// 重置分页
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
// 增加请求序列号
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -534,32 +793,163 @@ const loadAvailableSubprocesses = async () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载模型字段
|
|
|
|
|
const loadModelFields = async (customModelId) => {
|
|
|
|
|
const loadModelFields = async (customModelId, expectedCustomModelId = null) => {
|
|
|
|
|
// 如果传入了期望的 custom_model_id,检查是否仍然匹配
|
|
|
|
|
if (expectedCustomModelId && customModelId !== expectedCustomModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res = await oaFlowAPI.getCustomModelFields(customModelId)
|
|
|
|
|
|
|
|
|
|
// 再次检查 custom_model_id 是否仍然匹配
|
|
|
|
|
if (expectedCustomModelId && customModelId !== expectedCustomModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查当前选中的子流程是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res.code === 0 && res.data?.customModel?.fields) {
|
|
|
|
|
// 确保 fields 是数组
|
|
|
|
|
const fieldsArray = Array.isArray(res.data.customModel.fields)
|
|
|
|
|
? res.data.customModel.fields
|
|
|
|
|
: []
|
|
|
|
|
|
|
|
|
|
// 筛选 show_in_list = 1 的字段,并按 myindex 排序
|
|
|
|
|
// 筛选 show_in_list = 1 的字段,并按 myindex 排序(用于列表显示)
|
|
|
|
|
const fields = fieldsArray
|
|
|
|
|
.filter(field => field && field.show_in_list === 1)
|
|
|
|
|
.sort((a, b) => (a.myindex || 0) - (b.myindex || 0))
|
|
|
|
|
|
|
|
|
|
// 所有字段(用于高级查询,过滤掉没有name的字段)
|
|
|
|
|
const allFields = fieldsArray
|
|
|
|
|
.filter(field => field && field.name)
|
|
|
|
|
.sort((a, b) => (a.myindex || 0) - (b.myindex || 0))
|
|
|
|
|
|
|
|
|
|
// 再次检查是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dynamicFields.value = fields
|
|
|
|
|
allModelFields.value = allFields
|
|
|
|
|
|
|
|
|
|
// 加载 select 类型字段的选项数据(如果有 selection_model)
|
|
|
|
|
const selectFields = fields.filter(field =>
|
|
|
|
|
(field.type === 'select' || field.type === 'radio') &&
|
|
|
|
|
field.selection_model
|
|
|
|
|
)
|
|
|
|
|
for (const selectField of selectFields) {
|
|
|
|
|
// 检查是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果还没有加载过这个 selection_model 的选项,则加载
|
|
|
|
|
if (!selectionOptionsMap.value[selectField.selection_model]) {
|
|
|
|
|
try {
|
|
|
|
|
const optionsRes = await oaFlowAPI.getSelectionOptions(selectField.selection_model)
|
|
|
|
|
|
|
|
|
|
// 再次检查是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (optionsRes.code === 0 && Array.isArray(optionsRes.data)) {
|
|
|
|
|
selectionOptionsMap.value[selectField.selection_model] = optionsRes.data
|
|
|
|
|
} else {
|
|
|
|
|
selectionOptionsMap.value[selectField.selection_model] = []
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 只有在仍然匹配时才记录错误
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === customModelId) {
|
|
|
|
|
console.error(`加载选项数据失败 (${selectField.selection_model}):`, error)
|
|
|
|
|
selectionOptionsMap.value[selectField.selection_model] = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载 relation 类型字段的子表单字段
|
|
|
|
|
const relationFields = fields.filter(field => field.type === 'relation' && field.sub_custom_model_id)
|
|
|
|
|
for (const relationField of relationFields) {
|
|
|
|
|
// 检查是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (relationField.sub_custom_model_id && !subFormFieldsMap.value[relationField.name]) {
|
|
|
|
|
try {
|
|
|
|
|
const subRes = await oaFlowAPI.getCustomModelFields(relationField.sub_custom_model_id)
|
|
|
|
|
|
|
|
|
|
// 再次检查是否仍然匹配
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id !== customModelId) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (subRes.code === 0 && subRes.data?.customModel?.fields) {
|
|
|
|
|
const subFields = Array.isArray(subRes.data.customModel.fields)
|
|
|
|
|
? subRes.data.customModel.fields
|
|
|
|
|
: []
|
|
|
|
|
// 按 myindex 排序
|
|
|
|
|
subFormFieldsMap.value[relationField.name] = subFields.sort((a, b) => (a.myindex || 0) - (b.myindex || 0))
|
|
|
|
|
|
|
|
|
|
// 加载子表单中 select 类型字段的选项数据(如果有 selection_model)
|
|
|
|
|
const subSelectFields = subFields.filter(field =>
|
|
|
|
|
(field.type === 'select' || field.type === 'radio') &&
|
|
|
|
|
field.selection_model
|
|
|
|
|
)
|
|
|
|
|
for (const subSelectField of subSelectFields) {
|
|
|
|
|
// 如果还没有加载过这个 selection_model 的选项,则加载
|
|
|
|
|
if (!selectionOptionsMap.value[subSelectField.selection_model]) {
|
|
|
|
|
try {
|
|
|
|
|
const optionsRes = await oaFlowAPI.getSelectionOptions(subSelectField.selection_model)
|
|
|
|
|
if (optionsRes.code === 0 && Array.isArray(optionsRes.data)) {
|
|
|
|
|
selectionOptionsMap.value[subSelectField.selection_model] = optionsRes.data
|
|
|
|
|
} else {
|
|
|
|
|
selectionOptionsMap.value[subSelectField.selection_model] = []
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`加载子表单选项数据失败 (${subSelectField.selection_model}):`, error)
|
|
|
|
|
selectionOptionsMap.value[subSelectField.selection_model] = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// 只有在仍然匹配时才记录错误
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === customModelId) {
|
|
|
|
|
console.error(`加载子表单字段失败 (${relationField.name}):`, error)
|
|
|
|
|
subFormFieldsMap.value[relationField.name] = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
dynamicFields.value = []
|
|
|
|
|
// 只有在仍然匹配时才更新
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === customModelId) {
|
|
|
|
|
dynamicFields.value = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取模型字段失败:', error)
|
|
|
|
|
dynamicFields.value = []
|
|
|
|
|
// 只有在仍然匹配时才记录错误
|
|
|
|
|
if (selectedSubprocess.value?.custom_model_id === customModelId) {
|
|
|
|
|
console.error('获取模型字段失败:', error)
|
|
|
|
|
dynamicFields.value = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 加载流程列表
|
|
|
|
|
const loadFlowList = async () => {
|
|
|
|
|
const loadFlowList = async (sequence = null) => {
|
|
|
|
|
// 如果没有传入序列号,使用当前序列号
|
|
|
|
|
if (sequence === null) {
|
|
|
|
|
requestSequence++
|
|
|
|
|
sequence = requestSequence
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!selectedSubprocess.value || !selectedSubprocess.value.custom_model_id) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
@ -571,16 +961,26 @@ const loadFlowList = async () => {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存当前请求的关键参数,用于验证返回结果是否仍然有效
|
|
|
|
|
const currentCustomModelId = selectedSubprocess.value.custom_model_id
|
|
|
|
|
const currentYear = selectedYear.value
|
|
|
|
|
const currentQueryType = queryType.value
|
|
|
|
|
const currentPage = pagination.value.currentPage
|
|
|
|
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
|
|
|
|
const params = {
|
|
|
|
|
custom_model_id: selectedSubprocess.value.custom_model_id,
|
|
|
|
|
year: selectedYear.value, // 必填:年份参数
|
|
|
|
|
page: pagination.value.currentPage,
|
|
|
|
|
custom_model_id: currentCustomModelId,
|
|
|
|
|
year: currentYear, // 必填:年份参数
|
|
|
|
|
page: currentPage,
|
|
|
|
|
page_size: pagination.value.pageSize,
|
|
|
|
|
is_simple: 0, // 完整版本,包含data字段
|
|
|
|
|
payment_link_status: queryType.value, // 关联支付状态:not-linked 或 linked
|
|
|
|
|
...filterForm.value
|
|
|
|
|
payment_link_status: currentQueryType, // 关联支付状态:not-linked 或 linked
|
|
|
|
|
keyword: filterForm.value.keyword, // 流程编号关键词
|
|
|
|
|
// 高级查询参数
|
|
|
|
|
field_id: filterForm.value.field_id || undefined,
|
|
|
|
|
operator: filterForm.value.operator || undefined,
|
|
|
|
|
field_keyword: filterForm.value.field_keyword || undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 移除空值
|
|
|
|
|
@ -592,6 +992,24 @@ const loadFlowList = async () => {
|
|
|
|
|
|
|
|
|
|
// 使用 "all" 类型,因为我们现在通过 payment_link_status 参数来过滤
|
|
|
|
|
const res = await oaFlowAPI.getFlowList('all', params)
|
|
|
|
|
|
|
|
|
|
// 检查请求是否仍然有效(序列号是否匹配,关键参数是否改变)
|
|
|
|
|
if (sequence !== requestSequence) {
|
|
|
|
|
// 请求已被新的请求取代,忽略此结果
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证关键参数是否仍然匹配
|
|
|
|
|
if (
|
|
|
|
|
selectedSubprocess.value?.custom_model_id !== currentCustomModelId ||
|
|
|
|
|
selectedYear.value !== currentYear ||
|
|
|
|
|
queryType.value !== currentQueryType ||
|
|
|
|
|
pagination.value.currentPage !== currentPage
|
|
|
|
|
) {
|
|
|
|
|
// 关键参数已改变,忽略此结果
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res.code === 0) {
|
|
|
|
|
// 后端返回结构:res.data.data 是分页对象(Laravel Paginator)
|
|
|
|
|
// 分页对象包含:{ data: [...], total: 100, current_page: 1, per_page: 10, ... }
|
|
|
|
|
@ -633,33 +1051,47 @@ const loadFlowList = async () => {
|
|
|
|
|
console.warn('数据为空但总数不为0,可能数据格式不匹配')
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ElMessage.error(res.msg || res.message || '获取流程列表失败')
|
|
|
|
|
// 只有在请求仍然有效时才显示错误
|
|
|
|
|
if (sequence === requestSequence) {
|
|
|
|
|
ElMessage.error(res.msg || res.message || '获取流程列表失败')
|
|
|
|
|
}
|
|
|
|
|
tableData.value = []
|
|
|
|
|
pagination.value.total = 0
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
ElMessage.error('获取流程列表失败:' + error.message)
|
|
|
|
|
// 只有在请求仍然有效时才显示错误
|
|
|
|
|
if (sequence === requestSequence) {
|
|
|
|
|
ElMessage.error('获取流程列表失败:' + error.message)
|
|
|
|
|
}
|
|
|
|
|
tableData.value = []
|
|
|
|
|
pagination.value.total = 0
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
// 只有在请求仍然有效时才更新加载状态
|
|
|
|
|
if (sequence === requestSequence) {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
loadFlowList()
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 重置
|
|
|
|
|
const handleReset = () => {
|
|
|
|
|
filterForm.value = {
|
|
|
|
|
keyword: ''
|
|
|
|
|
keyword: '',
|
|
|
|
|
field_id: '',
|
|
|
|
|
operator: '',
|
|
|
|
|
field_keyword: ''
|
|
|
|
|
}
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
// 年份不重置,保持当前选择
|
|
|
|
|
loadFlowList()
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查看支付详情
|
|
|
|
|
@ -676,9 +1108,10 @@ const handleViewPayment = (payment) => {
|
|
|
|
|
const handleView = (row) => {
|
|
|
|
|
// 跳转到OA流程详情页面
|
|
|
|
|
const token = getToken()
|
|
|
|
|
const baseUrl = '/oa/#/flow/view'
|
|
|
|
|
const baseUrl = '/oa/#/flow/detail'
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
id: row.id.toString(),
|
|
|
|
|
module_id: (row.custom_model_id || row.custom_model?.id || row.customModel?.id || '').toString(),
|
|
|
|
|
flow_id: row.id.toString(),
|
|
|
|
|
isSinglePage: '1',
|
|
|
|
|
module_name: 'oa',
|
|
|
|
|
form_canal: 'budget'
|
|
|
|
|
@ -692,16 +1125,19 @@ const handleView = (row) => {
|
|
|
|
|
window.open(fullUrl, '_blank')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查用户是否有"全局流程监管"角色
|
|
|
|
|
const hasGlobalFlowSupervisionRole = computed(() => {
|
|
|
|
|
return userStore.roles && userStore.roles.includes('全局流程监管')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 编辑
|
|
|
|
|
const handleEdit = (row) => {
|
|
|
|
|
// 跳转到OA流程编辑页面
|
|
|
|
|
const token = getToken()
|
|
|
|
|
const baseUrl = '/oa/#/flow/deal'
|
|
|
|
|
const baseUrl = '/oa/#/flow/edit'
|
|
|
|
|
const params = new URLSearchParams({
|
|
|
|
|
id: row.id.toString(),
|
|
|
|
|
isSinglePage: '1',
|
|
|
|
|
module_name: 'oa',
|
|
|
|
|
form_canal: 'budget'
|
|
|
|
|
flow_id: row.id.toString(),
|
|
|
|
|
isSinglePage: '1'
|
|
|
|
|
})
|
|
|
|
|
if (token) {
|
|
|
|
|
params.set('auth_token', token)
|
|
|
|
|
@ -716,13 +1152,15 @@ const handleEdit = (row) => {
|
|
|
|
|
const handleSizeChange = (val) => {
|
|
|
|
|
pagination.value.pageSize = val
|
|
|
|
|
pagination.value.currentPage = 1
|
|
|
|
|
loadFlowList()
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 当前页改变
|
|
|
|
|
const handleCurrentChange = (val) => {
|
|
|
|
|
pagination.value.currentPage = val
|
|
|
|
|
loadFlowList()
|
|
|
|
|
requestSequence++
|
|
|
|
|
loadFlowList(requestSequence)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 页面加载
|
|
|
|
|
@ -883,6 +1321,25 @@ onMounted(() => {
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.relation-field-renderer {
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.relation-field-renderer :deep(.el-table) {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.relation-field-renderer :deep(.el-table th) {
|
|
|
|
|
padding: 8px 4px;
|
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.relation-field-renderer :deep(.el-table td) {
|
|
|
|
|
padding: 6px 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.subprocess-grid {
|
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
|
|
|
|