feat(文档库): 补检查类型与关联项目标签并按关联项目编码搜索

按原型 V10.2 卡片上的两条信息补齐(此前完全没有对应字段):

- DocumentRecord / TemplateResponseDto 增加 examType 与 projectCodes(可选)。
  后端当前不返回,声明为可选是为了它补齐后前端不用再改;取不到时整条标签不渲染。
- 卡片渲染「🔬 检查类型」标签(原型 .exam-tag 蓝底)与「关联项目」编码
  (原型 .proj-code 紫底等宽),配色照抄原型。
- 搜索关键词纳入检查类型与关联项目编码,占位文案改为原型的
  「搜索模板名 / 编号 / 科室 / 关联项目编码」。
- 演示种子补 projectCodes: ['PHA-NAR'](原型 library 里这份文书就是这条)。
  刻意不给 examType:麻精同意书是告知类文书,本来就不挂检查类型,
  演示里看不到这条标签是正确结果,不编造取值。
- mock.ts 的 toLocalDocument 是逐字段列出的,同步带上两个新字段,
  否则演示文库保存一次,卡片上的标签就丢了。
This commit is contained in:
yelan
2026-09-26 19:45:31 +08:00
parent 7a94368bea
commit f871613abe
7 changed files with 95 additions and 3 deletions
@@ -110,6 +110,10 @@ function mapDocument(dto: TemplateResponseDto): DocumentRecord {
departmentName: dto.departmentId ? undefined : '全院通用',
campusId: dto.campusId,
description: dto.description ?? '',
// 后端补齐这两个字段后卡片会自动显示「检查类型 / 关联项目」;
// 现在取不到就是 undefined,卡片不渲染对应标签,不伪造取值
examType: dto.examType ?? undefined,
projectCodes: dto.projectCodes ?? undefined,
}
}
+23
View File
@@ -34,6 +34,21 @@ export interface DocumentRecord {
campusId?: string | null
campusName?: string
description?: string
/**
* 检查类型(原型 V10.2 卡片上的「🔬 检查类型:xxx」)。
*
* 只有检查/检验类文书才有值(如「平扫 / 增强」「分子检测」),
* 告知类文书为空。当前后端模板接口不返回它,因此**真实模式下为空时整条标签不渲染**,
* 不为它编一个取值。
*/
examType?: string | null
/**
* 关联项目编码(原型 V10.2 卡片上的「关联项目」编码串)。
*
* 用于把文书与 HIS 的检查/检验项目对应起来,卡片上以 `.proj-code` 小标签展示。
* 同样只在后端提供时才有值,缺省即不显示。
*/
projectCodes?: string[]
contentHtml?: string
contentSha256?: string
/** 当前已发布版本;发起签署必须使用它,不能误用最新草稿版本。 */
@@ -60,6 +75,14 @@ export interface TemplateResponseDto {
campusId: string
departmentId?: string | null
category?: string | null
/**
* 检查类型与关联项目编码(原型 V10.2 卡片上的两条信息)。
*
* 当前后端不返回,声明为可选是为了在它补齐后**不需要再改前端**就能显示出来;
* 取不到时卡片整条标签不渲染,不编造取值。
*/
examType?: string | null
projectCodes?: string[] | null
status?: BackendTemplateStatus | null
currentVersionId?: string | null
latestVersionId?: string | null
@@ -52,6 +52,12 @@ export interface LocalDocument {
sharedDepartments?: string[]
campusId?: string | null
campusName?: string
/**
* 检查类型与关联项目编码(原型 V10.2 卡片上的「🔬 检查类型」「关联项目」)。
* 与 api/management/types.ts 的 DocumentRecord 同名同义,演示文库据此渲染同样的标签。
*/
examType?: string | null
projectCodes?: string[]
/**
* 演示数据的更新时间。
* 演示文库的种子不带时间,页面在这一层会显示成"—"而不是伪造一个时间戳;
@@ -243,6 +249,10 @@ function buildDemoLibrary(): LocalDepartment[] {
status: 'published',
backendStatus: 'PUBLISHED',
description: '麻精药品成瘾性风险、特殊属性告知、身份核验、代办人信息与用药信息登记。',
// 原型 library 里这份文书带 projCodes:['PHA-NAR'],照抄。
// 它**没有** examType —— 告知类文书本来就不挂检查类型,因此演示库里
// 「检查类型」标签不出现是正确结果,不要为了让它可见而编一个取值。
projectCodes: ['PHA-NAR'],
versionId: 'tpl-pha-nar-001-v1',
departmentId: null,
departmentName: DEMO_DEPARTMENT,
@@ -33,8 +33,8 @@ function handleInput(event: Event) {
:value="modelValue"
class="lib-search"
type="search"
placeholder="搜索模板名 / 编号 / 科室 / 分类"
aria-label="搜索模板名称、编号、科室或分类"
placeholder="搜索模板名 / 编号 / 科室 / 关联项目编码"
aria-label="搜索模板名称、编号、科室、分类或关联项目编码"
@input="handleInput"
/>
@@ -64,6 +64,15 @@ const initiateTitle = computed(() =>
const canToggle = computed(() => canToggleStatus(status.value))
const toggleLabel = computed(() => (status.value === '停用' ? '启用' : '停用'))
/**
* 检查类型 / 关联项目编码(原型 V10.2 卡片上的两条信息)。
*
* 两者都只在后端真的给了值时才渲染:告知类文书本来就没有检查类型,
* 编一个取值出来会让卡片看起来比实际更"配置完整"。
*/
const examType = computed(() => props.template.examType?.trim() ?? '')
const projectCodes = computed(() => (props.template.projectCodes ?? []).filter(Boolean))
/** 草稿的「提交审核」只在演示模式露出,真实模式不给这个入口 */
const showSubmitReview = computed(() => props.isMock && status.value === '草稿')
@@ -93,8 +102,14 @@ const togglePermission = computed(() => workflowPermission(toggleAction.value))
<span class="exam-tag" :class="isGeneral ? 'is-general' : 'is-department'">
{{ ownershipLabel }}<template v-if="!isGeneral && sharedCount"> ·+{{ sharedCount }} 科室</template>
</span>
<span v-if="examType" class="exam-tag is-exam">🔬 检查类型:{{ examType }}</span>
</div>
<p v-if="projectCodes.length" class="tpl-projects">
<span>关联项目</span>
<code v-for="code in projectCodes" :key="code" class="proj-code">{{ code }}</code>
</p>
<p class="tpl-desc">{{ template.description || '—' }}</p>
<div class="tpl-ft">
@@ -276,6 +291,33 @@ const togglePermission = computed(() => workflowPermission(toggleAction.value))
background: #e2f5ec;
}
/* 检查类型:原型 .exam-tag 的默认配色(蓝底蓝字),与归属标签的绿底刻意区分 */
.exam-tag.is-exam {
color: #2b6cb0;
background: #eef6fb;
}
/* 关联项目:等宽编码小标签,原型 .proj-code 取紫系 */
.tpl-projects {
display: flex;
flex-wrap: wrap;
gap: 4px;
align-items: center;
margin: 0;
color: var(--mut);
font-size: 11.5px;
}
.proj-code {
padding: 1px 6px;
color: #7a4dbb;
font-family: Consolas, monospace;
font-size: 10.5px;
white-space: nowrap;
background: #f6f1fc;
border-radius: 4px;
}
.tpl-desc {
display: -webkit-box;
flex: 1;
@@ -186,9 +186,18 @@ const filteredTemplates = computed(() => {
const matchesDepartment = !filters.department || template.departmentKey === filters.department
const matchesStatus =
filters.status === 'all' || readLibraryStatus(template) === filters.status
// 搜索范围照原型:模板名 / 编号 / 科室 / 分类,外加检查类型与关联项目编码。
// 后两者是卡片上看得见的字段,搜不到会让人以为这份文书不存在。
const matchesKeyword =
!keyword ||
[template.name, template.code, template.department, template.category]
[
template.name,
template.code ?? '',
template.department,
template.category,
template.examType ?? '',
(template.projectCodes ?? []).join(','),
]
.join(' ')
.toLowerCase()
.includes(keyword)
@@ -84,6 +84,10 @@ function toLocalDocument(document: DocumentRecord): LocalDocument {
sharedDepartments: document.sharedDepartments,
campusId: document.campusId,
campusName: document.campusName,
// 检查类型与关联项目是卡片上要展示的字段,落本地时不能丢,
// 否则刷新一次卡片上的标签就没了
examType: document.examType,
projectCodes: document.projectCodes,
updatedAt: document.updatedAt,
contentHtml: document.contentHtml,
contentSha256: document.contentSha256,