|
@@ -45,43 +45,83 @@
|
|
|
import { computed } from 'vue'
|
|
import { computed } from 'vue'
|
|
|
|
|
|
|
|
import type {
|
|
import type {
|
|
|
- TaskDetailContentField,
|
|
|
|
|
TaskDetailContentItem,
|
|
TaskDetailContentItem,
|
|
|
TaskFormDetailResponse,
|
|
TaskFormDetailResponse,
|
|
|
} from '@/services/modules/task/taskDetail/type'
|
|
} from '@/services/modules/task/taskDetail/type'
|
|
|
|
|
+import type {
|
|
|
|
|
+ TaskContentConfig,
|
|
|
|
|
+ TaskContentConfigByTaskTypeIdResponse,
|
|
|
|
|
+} from '@/services/modules/task/taskFrom/type'
|
|
|
|
|
|
|
|
import { previewImages, toFullUrl } from '../utils'
|
|
import { previewImages, toFullUrl } from '../utils'
|
|
|
import TaskDetailSummary from './TaskDetailSummary.vue'
|
|
import TaskDetailSummary from './TaskDetailSummary.vue'
|
|
|
|
|
|
|
|
-interface TaskDetailContentDisplayItem extends Omit<TaskDetailContentItem, 'label'> {
|
|
|
|
|
- label: string
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-interface TaskDetailContentDisplayField extends Omit<TaskDetailContentField, 'items'> {
|
|
|
|
|
- name: string
|
|
|
|
|
- items: TaskDetailContentDisplayItem[]
|
|
|
|
|
- imageUrls: string[]
|
|
|
|
|
- text: string
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
const IMAGE_FIELD_TYPES = new Set(['img', 'sign'])
|
|
const IMAGE_FIELD_TYPES = new Set(['img', 'sign'])
|
|
|
|
|
+const DICT_FIELD_TYPES = new Set(['select', 'domain', 'multiple_select'])
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
const props = defineProps<{
|
|
|
taskDetail: TaskFormDetailResponse
|
|
taskDetail: TaskFormDetailResponse
|
|
|
|
|
+ taskConfig: TaskContentConfigByTaskTypeIdResponse | null
|
|
|
}>()
|
|
}>()
|
|
|
|
|
|
|
|
|
|
+const taskConfigs = computed<TaskContentConfig[]>(() => {
|
|
|
|
|
+ const source = props.taskConfig
|
|
|
|
|
+ if (!source) return []
|
|
|
|
|
+
|
|
|
|
|
+ const configs =
|
|
|
|
|
+ 'dict' in source ? [source] : Object.values(source).filter((item) => 'dict' in item)
|
|
|
|
|
+
|
|
|
|
|
+ return configs as TaskContentConfig[]
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+/** 将所有表单配置预先整理为「字段名 -> value -> label」索引。 */
|
|
|
|
|
+const dictLabelsByField = computed(() => {
|
|
|
|
|
+ const result = new Map<string, Map<string, string>>()
|
|
|
|
|
+
|
|
|
|
|
+ for (const { config, dict } of taskConfigs.value) {
|
|
|
|
|
+ if (!Array.isArray(config) || !dict) continue
|
|
|
|
|
+
|
|
|
|
|
+ for (const field of config) {
|
|
|
|
|
+ const dictGroupName = field.dictGroupName?.trim()
|
|
|
|
|
+ const dictItems = dictGroupName ? dict[dictGroupName] : undefined
|
|
|
|
|
+ if (!Array.isArray(dictItems) || !dictItems.length) continue
|
|
|
|
|
+
|
|
|
|
|
+ const fieldName = field.taskFiledValue.trim()
|
|
|
|
|
+ const labels = result.get(fieldName) ?? new Map<string, string>()
|
|
|
|
|
+
|
|
|
|
|
+ for (const item of dictItems) {
|
|
|
|
|
+ const value = String(item.value).trim()
|
|
|
|
|
+ const label = String(item.label).trim()
|
|
|
|
|
+
|
|
|
|
|
+ if (value && label && !labels.has(value)) {
|
|
|
|
|
+ labels.set(value, label)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (labels.size) result.set(fieldName, labels)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * wmTaskContent 按 seq 排序;item.label 为空时用 value 回显。
|
|
|
|
|
|
|
+ * wmTaskContent 按 seq 排序;select、domain、multiple_select 类型优先从字段配置
|
|
|
|
|
+ * 对应的字典中获取 label,字典缺失或值无法完整匹配时直接回显 value。
|
|
|
|
|
+ * 其余情况在 item.label 为空时用 value 回显。
|
|
|
* img、sign 类型转成图片列表,其余类型显示标准化后的 label。
|
|
* img、sign 类型转成图片列表,其余类型显示标准化后的 label。
|
|
|
*/
|
|
*/
|
|
|
-const contentFields = computed<TaskDetailContentDisplayField[]>(() => {
|
|
|
|
|
|
|
+const contentFields = computed(() => {
|
|
|
const content = props.taskDetail.wmTaskContent
|
|
const content = props.taskDetail.wmTaskContent
|
|
|
if (!content) return []
|
|
if (!content) return []
|
|
|
|
|
|
|
|
return Object.entries(content)
|
|
return Object.entries(content)
|
|
|
.sort(([, left], [, right]) => left.seq - right.seq)
|
|
.sort(([, left], [, right]) => left.seq - right.seq)
|
|
|
.map(([name, field]) => {
|
|
.map(([name, field]) => {
|
|
|
- const items = field.items.map(normalizeContentItem)
|
|
|
|
|
|
|
+ const items = field.items.map((item) => ({
|
|
|
|
|
+ ...item,
|
|
|
|
|
+ label: resolveItemLabel(name, item),
|
|
|
|
|
+ }))
|
|
|
const imageUrls = items
|
|
const imageUrls = items
|
|
|
.filter((item) => isImageFieldType(item.type))
|
|
.filter((item) => isImageFieldType(item.type))
|
|
|
.flatMap((item) => parseImageUrls(item.label))
|
|
.flatMap((item) => parseImageUrls(item.label))
|
|
@@ -95,18 +135,30 @@ const contentFields = computed<TaskDetailContentDisplayField[]>(() => {
|
|
|
name,
|
|
name,
|
|
|
seq: field.seq,
|
|
seq: field.seq,
|
|
|
required: field.required,
|
|
required: field.required,
|
|
|
- items,
|
|
|
|
|
imageUrls: [...new Set(imageUrls)],
|
|
imageUrls: [...new Set(imageUrls)],
|
|
|
text,
|
|
text,
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const normalizeContentItem = (item: TaskDetailContentItem): TaskDetailContentDisplayItem => {
|
|
|
|
|
- return {
|
|
|
|
|
- ...item,
|
|
|
|
|
- label: item.label?.trim() || item.value,
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const resolveItemLabel = (fieldName: string, item: TaskDetailContentItem): string => {
|
|
|
|
|
+ const fieldType = item.type.trim().toLowerCase()
|
|
|
|
|
+ if (!DICT_FIELD_TYPES.has(fieldType)) return item.label?.trim() || item.value
|
|
|
|
|
+ if (fieldType !== 'multiple_select' && item.label?.trim()) return item.label.trim()
|
|
|
|
|
+
|
|
|
|
|
+ const values =
|
|
|
|
|
+ fieldType === 'multiple_select'
|
|
|
|
|
+ ? item.value
|
|
|
|
|
+ .split(',')
|
|
|
|
|
+ .map((value) => value.trim())
|
|
|
|
|
+ .filter(Boolean)
|
|
|
|
|
+ : [item.value.trim()]
|
|
|
|
|
+ const labels = dictLabelsByField.value.get(fieldName.trim())
|
|
|
|
|
+
|
|
|
|
|
+ if (!values.length || !labels) return item.value
|
|
|
|
|
+
|
|
|
|
|
+ const resolvedLabels = values.map((value) => labels.get(value))
|
|
|
|
|
+ return resolvedLabels.every(Boolean) ? resolvedLabels.join('、') : item.value
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const isImageFieldType = (type: string) => {
|
|
const isImageFieldType = (type: string) => {
|