|
@@ -68,22 +68,23 @@
|
|
|
<el-table-column label="完成时间" prop="finishTime" :formatter="dateFormatter" />
|
|
<el-table-column label="完成时间" prop="finishTime" :formatter="dateFormatter" />
|
|
|
<el-table-column label="备注" min-width="200">
|
|
<el-table-column label="备注" min-width="200">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
|
|
+ <!-- 高频:解析 remark JSON,有内容才展示 -->
|
|
|
<template v-if="scope.row.frequencyCategoryType == 1">
|
|
<template v-if="scope.row.frequencyCategoryType == 1">
|
|
|
<template
|
|
<template
|
|
|
- v-for="rv in [getRemarkView(scope.row.remark)]"
|
|
|
|
|
- :key="'rk-' + rv.mode + '-' + (scope.row.id ?? scope.$index)"
|
|
|
|
|
|
|
+ v-for="blocks in [getHighFreqRemarkBlocks(scope.row.remark)]"
|
|
|
|
|
+ :key="'hb-' + blocks.length + '-' + (scope.row.id ?? scope.$index)"
|
|
|
>
|
|
>
|
|
|
- <div v-if="rv.mode === 'blocks'" class="remark-cell-compact">
|
|
|
|
|
|
|
+ <div v-if="blocks.length" class="remark-cell-compact">
|
|
|
<div class="remark-compact-main">
|
|
<div class="remark-compact-main">
|
|
|
<div
|
|
<div
|
|
|
class="remark-summary-line"
|
|
class="remark-summary-line"
|
|
|
- :title="getStructuredRemarkSummaryFull(rv.blocks)"
|
|
|
|
|
|
|
+ :title="getStructuredRemarkSummaryFull(blocks)"
|
|
|
>
|
|
>
|
|
|
- {{ getStructuredRemarkSummary(rv.blocks) }}
|
|
|
|
|
|
|
+ {{ getStructuredRemarkSummary(blocks) }}
|
|
|
</div>
|
|
</div>
|
|
|
<div class="remark-compact-meta">
|
|
<div class="remark-compact-meta">
|
|
|
<el-tag size="small" type="info" effect="plain">
|
|
<el-tag size="small" type="info" effect="plain">
|
|
|
- {{ getStructuredRemarkModuleCount(rv.blocks) }}
|
|
|
|
|
|
|
+ {{ getStructuredRemarkModuleCount(blocks) }}
|
|
|
</el-tag>
|
|
</el-tag>
|
|
|
<el-popover
|
|
<el-popover
|
|
|
placement="left-start"
|
|
placement="left-start"
|
|
@@ -98,7 +99,7 @@
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
<div class="structured-remark structured-remark--popover">
|
|
<div class="structured-remark structured-remark--popover">
|
|
|
- <div v-for="block in rv.blocks" :key="block.title" class="sr-section">
|
|
|
|
|
|
|
+ <div v-for="block in blocks" :key="block.title" class="sr-section">
|
|
|
<div class="sr-title">{{ block.title }}</div>
|
|
<div class="sr-title">{{ block.title }}</div>
|
|
|
<div
|
|
<div
|
|
|
v-for="(line, li) in block.lines"
|
|
v-for="(line, li) in block.lines"
|
|
@@ -131,13 +132,10 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <span v-else-if="rv.mode === 'json-fallback'" class="sr-fallback">{{
|
|
|
|
|
- rv.text
|
|
|
|
|
- }}</span>
|
|
|
|
|
- <span v-else class="remark-plain">{{ rv.text }}</span>
|
|
|
|
|
</template>
|
|
</template>
|
|
|
</template>
|
|
</template>
|
|
|
- <span v-else>{{ scope.row.remark }}</span>
|
|
|
|
|
|
|
+ <!-- 中/低频:直接显示 remark 原文 -->
|
|
|
|
|
+ <span v-else class="remark-plain">{{ scope.row.remark || '-' }}</span>
|
|
|
</template>
|
|
</template>
|
|
|
</el-table-column>
|
|
</el-table-column>
|
|
|
<el-table-column
|
|
<el-table-column
|
|
@@ -479,24 +477,45 @@ function normalizePhotoUrls(raw: unknown): string[] {
|
|
|
.filter(Boolean)
|
|
.filter(Boolean)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function parseRemarkObject(remark: string): Record<string, unknown> | null {
|
|
|
|
|
- const t = remark?.trim()
|
|
|
|
|
- if (!t || t[0] !== '{') return null
|
|
|
|
|
- try {
|
|
|
|
|
- const o = JSON.parse(t) as unknown
|
|
|
|
|
- if (o && typeof o === 'object' && !Array.isArray(o)) return o as Record<string, unknown>
|
|
|
|
|
- } catch {
|
|
|
|
|
- //
|
|
|
|
|
|
|
+function parseRemarkObject(remark: unknown): Record<string, unknown> | null {
|
|
|
|
|
+ if (remark == null) return null
|
|
|
|
|
+ if (typeof remark === 'object' && !Array.isArray(remark)) {
|
|
|
|
|
+ return remark as Record<string, unknown>
|
|
|
}
|
|
}
|
|
|
- return null
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ if (typeof remark !== 'string') return null
|
|
|
|
|
+
|
|
|
|
|
+ let t = remark.trim()
|
|
|
|
|
+ if (!t) return null
|
|
|
|
|
|
|
|
-/** 是否为小程序高频护理整单 JSON(与中/低频纯文本区分) */
|
|
|
|
|
-function isStructuredRemark(remark?: string): boolean {
|
|
|
|
|
- if (!remark || typeof remark !== 'string') return false
|
|
|
|
|
- const o = parseRemarkObject(remark)
|
|
|
|
|
- if (!o) return false
|
|
|
|
|
- return 'food' in o || 'skin' in o || 'bowel' in o
|
|
|
|
|
|
|
+ // 接口/库中可能 JSON.stringify 整段 remark,支持多层解包
|
|
|
|
|
+ for (let depth = 0; depth < 3; depth++) {
|
|
|
|
|
+ if (t[0] === '{') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const o = JSON.parse(t) as unknown
|
|
|
|
|
+ if (o && typeof o === 'object' && !Array.isArray(o)) return o as Record<string, unknown>
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ //
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ if (t[0] === '"') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const unwrapped = JSON.parse(t) as unknown
|
|
|
|
|
+ if (typeof unwrapped === 'string') {
|
|
|
|
|
+ t = unwrapped.trim()
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if (unwrapped && typeof unwrapped === 'object' && !Array.isArray(unwrapped)) {
|
|
|
|
|
+ return unwrapped as Record<string, unknown>
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ //
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ return null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function extractFieldValue(sectionKey: string, field: FormField, root: unknown): unknown {
|
|
function extractFieldValue(sectionKey: string, field: FormField, root: unknown): unknown {
|
|
@@ -550,9 +569,9 @@ function extractFieldValue(sectionKey: string, field: FormField, root: unknown):
|
|
|
if (Array.isArray(v)) return v.filter(Boolean).join('、')
|
|
if (Array.isArray(v)) return v.filter(Boolean).join('、')
|
|
|
return v
|
|
return v
|
|
|
}
|
|
}
|
|
|
- // 扁平:饮食/皮肤/大小便/睡眠/过期/情绪
|
|
|
|
|
|
|
+ // 扁平:饮食/皮肤/大小便/睡眠/过期/情绪 — { key, value, remark?, photo? }
|
|
|
if (field.type === 'select' || field.type === 'datetime') {
|
|
if (field.type === 'select' || field.type === 'datetime') {
|
|
|
- if (r.key === field.key) return r.value
|
|
|
|
|
|
|
+ if (r.key === field.key || r.key == null) return r.value
|
|
|
return undefined
|
|
return undefined
|
|
|
}
|
|
}
|
|
|
if (
|
|
if (
|
|
@@ -599,7 +618,9 @@ function buildSectionLines(sec: FormSection, parsed: Record<string, unknown>): R
|
|
|
return lines
|
|
return lines
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function getStructuredRemarkDisplay(remark: string): Array<{ title: string; lines: RemarkLine[] }> {
|
|
|
|
|
|
|
+function getStructuredRemarkDisplay(
|
|
|
|
|
+ remark: unknown
|
|
|
|
|
+): Array<{ title: string; lines: RemarkLine[] }> {
|
|
|
const parsed = parseRemarkObject(remark)
|
|
const parsed = parseRemarkObject(remark)
|
|
|
if (!parsed) return []
|
|
if (!parsed) return []
|
|
|
const out: Array<{ title: string; lines: RemarkLine[] }> = []
|
|
const out: Array<{ title: string; lines: RemarkLine[] }> = []
|
|
@@ -610,23 +631,14 @@ function getStructuredRemarkDisplay(remark: string): Array<{ title: string; line
|
|
|
return out
|
|
return out
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 单行备注展示:一次解析,供表格列模板使用 */
|
|
|
|
|
-function getRemarkView(remark?: string): {
|
|
|
|
|
- mode: 'plain' | 'blocks' | 'json-fallback'
|
|
|
|
|
- text: string
|
|
|
|
|
- blocks: Array<{ title: string; lines: RemarkLine[] }>
|
|
|
|
|
-} {
|
|
|
|
|
- if (!remark || !String(remark).trim()) {
|
|
|
|
|
- return { mode: 'plain', text: '-', blocks: [] }
|
|
|
|
|
- }
|
|
|
|
|
- if (!isStructuredRemark(remark)) {
|
|
|
|
|
- return { mode: 'plain', text: remark, blocks: [] }
|
|
|
|
|
- }
|
|
|
|
|
- const blocks = getStructuredRemarkDisplay(remark)
|
|
|
|
|
- if (blocks.length) {
|
|
|
|
|
- return { mode: 'blocks', text: remark, blocks }
|
|
|
|
|
|
|
+/** 高频护理 remark:解析 JSON,仅返回有内容的展示块 */
|
|
|
|
|
+function getHighFreqRemarkBlocks(
|
|
|
|
|
+ remark?: unknown
|
|
|
|
|
+): Array<{ title: string; lines: RemarkLine[] }> {
|
|
|
|
|
+ if (remark == null || (typeof remark === 'string' && !remark.trim())) {
|
|
|
|
|
+ return []
|
|
|
}
|
|
}
|
|
|
- return { mode: 'json-fallback', text: remark, blocks: [] }
|
|
|
|
|
|
|
+ return getStructuredRemarkDisplay(remark)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** 表格单元格内一行摘要(前几项 + 等 N 项),控制列宽 */
|
|
/** 表格单元格内一行摘要(前几项 + 等 N 项),控制列宽 */
|