fix: improve knowledge review scope controls
This commit is contained in:
@@ -892,7 +892,7 @@
|
||||
<el-descriptions :column="2" border size="small">
|
||||
<el-descriptions-item label="资料">{{ selectedQualityAsset.sourceName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ lifecycleLabel(selectedQualityAsset.lifecycleStatus) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="来源">{{ selectedQualityAsset.sourceAuthority }}</el-descriptions-item>
|
||||
<el-descriptions-item label="来源">{{ sourceAuthorityLabel(selectedQualityAsset.sourceAuthority) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="版本">{{ selectedQualityAsset.sourceVersion }}</el-descriptions-item>
|
||||
<el-descriptions-item label="语义分析">
|
||||
{{ semanticStatusLabel(selectedQualityAsset.semanticAnalysisStatus) }}
|
||||
@@ -907,7 +907,7 @@
|
||||
v-if="reviewPackage"
|
||||
:type="reviewPackage.riskLevel === 'LOW' ? 'success' : reviewPackage.riskLevel === 'MEDIUM' ? 'warning' : 'danger'"
|
||||
>
|
||||
{{ reviewPackage.riskLevel }} 风险
|
||||
{{ riskLevelLabel(reviewPackage.riskLevel) }}风险
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-if="reviewPackage" class="review-assistant-grid">
|
||||
@@ -920,7 +920,9 @@
|
||||
<el-table-column label="版本" width="86"
|
||||
><template #default="{ row }">v{{ row.versionNo }}</template></el-table-column
|
||||
>
|
||||
<el-table-column prop="versionStatus" label="状态" width="130" />
|
||||
<el-table-column label="状态" width="130">
|
||||
<template #default="{ row }">{{ versionStatusLabel(row.versionStatus) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="指针" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.published" size="small" type="success">生效版</el-tag>
|
||||
@@ -995,9 +997,9 @@
|
||||
<div v-loading="qualityDetailLoading" class="quality-issue-list">
|
||||
<h3>检测结果</h3>
|
||||
<div v-for="issue in qualityIssues" :key="issue.id" class="quality-issue">
|
||||
<el-tag :type="issue.gateType === 'HARD' ? 'danger' : 'warning'" size="small">{{ issue.reasonCode }}</el-tag>
|
||||
<span>{{ issueEvidence(issue.evidenceJson) }}</span>
|
||||
<small>{{ issue.recommendedAction }}</small>
|
||||
<el-tag :type="issue.gateType === 'HARD' ? 'danger' : 'warning'" size="small">{{ qualityReasonLabel(issue.reasonCode) }}</el-tag>
|
||||
<span>{{ qualityIssueDescription(issue) }}</span>
|
||||
<small>{{ qualityIssueAction(issue) }}</small>
|
||||
</div>
|
||||
<el-empty v-if="!qualityDetailLoading && !qualityIssues.length" :image-size="56" description="未发现质量问题" />
|
||||
</div>
|
||||
@@ -1089,13 +1091,70 @@
|
||||
<el-input v-model="qualityReviewForm.sourceVersion" />
|
||||
</el-form-item>
|
||||
<el-form-item label="适用岗位">
|
||||
<el-input v-model="qualityReviewForm.applicableRole" placeholder="例如:生活顾问" />
|
||||
<div class="quality-scope-field">
|
||||
<el-segmented
|
||||
v-model="qualityScopeModes.applicableRole"
|
||||
:options="scopeModeOptions"
|
||||
size="small"
|
||||
@change="changeQualityScope('applicableRole', $event)"
|
||||
/>
|
||||
<el-select
|
||||
v-if="qualityScopeModes.applicableRole === 'SPECIFIED'"
|
||||
v-model="qualityReviewForm.applicableRole"
|
||||
filterable
|
||||
allow-create
|
||||
clearable
|
||||
default-first-option
|
||||
:loading="qualityScopeOptionsLoading"
|
||||
placeholder="请选择岗位;无匹配项可输入后回车"
|
||||
>
|
||||
<el-option v-for="item in qualityScopeOptions.applicableRole" :key="item" :label="qualityScopeOptionLabel(item)" :value="item" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="适用区域">
|
||||
<el-input v-model="qualityReviewForm.applicableRegion" placeholder="例如:南京" />
|
||||
<div class="quality-scope-field">
|
||||
<el-segmented
|
||||
v-model="qualityScopeModes.applicableRegion"
|
||||
:options="scopeModeOptions"
|
||||
size="small"
|
||||
@change="changeQualityScope('applicableRegion', $event)"
|
||||
/>
|
||||
<el-select
|
||||
v-if="qualityScopeModes.applicableRegion === 'SPECIFIED'"
|
||||
v-model="qualityReviewForm.applicableRegion"
|
||||
filterable
|
||||
allow-create
|
||||
clearable
|
||||
default-first-option
|
||||
:loading="qualityScopeOptionsLoading"
|
||||
placeholder="请选择区域;无匹配项可输入后回车"
|
||||
>
|
||||
<el-option v-for="item in qualityScopeOptions.applicableRegion" :key="item" :label="qualityScopeOptionLabel(item)" :value="item" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="适用项目">
|
||||
<el-input v-model="qualityReviewForm.applicableProject" placeholder="例如:江北项目" />
|
||||
<div class="quality-scope-field">
|
||||
<el-segmented
|
||||
v-model="qualityScopeModes.applicableProject"
|
||||
:options="scopeModeOptions"
|
||||
size="small"
|
||||
@change="changeQualityScope('applicableProject', $event)"
|
||||
/>
|
||||
<el-select
|
||||
v-if="qualityScopeModes.applicableProject === 'SPECIFIED'"
|
||||
v-model="qualityReviewForm.applicableProject"
|
||||
filterable
|
||||
allow-create
|
||||
clearable
|
||||
default-first-option
|
||||
:loading="qualityScopeOptionsLoading"
|
||||
placeholder="请选择项目;无匹配项可输入后回车"
|
||||
>
|
||||
<el-option v-for="item in qualityScopeOptions.applicableProject" :key="item" :label="qualityScopeOptionLabel(item)" :value="item" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效日期">
|
||||
<el-date-picker v-model="qualityReviewForm.effectiveFrom" type="date" value-format="YYYY-MM-DD" placeholder="开始日期" />
|
||||
@@ -1117,10 +1176,20 @@
|
||||
<el-form-item label="审核意见">
|
||||
<el-input v-model="qualityReviewForm.reason" type="textarea" :rows="3" maxlength="1000" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="openSoftQualityIssues.length" label="确认已人工核对以下软提示">
|
||||
<el-form-item v-if="openSoftQualityIssues.length" label="人工核对确认(需逐项勾选)">
|
||||
<el-alert
|
||||
class="quality-acknowledgement-tip"
|
||||
title="请先对照上方检测结果核查原文;勾选表示已确认该提示不影响本次发布,系统不会自动修改资料。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-checkbox-group v-model="qualityReviewForm.acceptedReasonCodes" class="reason-checks">
|
||||
<el-checkbox v-for="issue in openSoftQualityIssues" :key="issue.id" :value="issue.reasonCode">
|
||||
{{ issue.reasonCode }}
|
||||
<span class="reason-check-content">
|
||||
<strong>已核对:{{ qualityReasonLabel(issue.reasonCode) }}</strong>
|
||||
<small>{{ qualityAcknowledgementText(issue.reasonCode) }}</small>
|
||||
</span>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
@@ -1232,6 +1301,7 @@
|
||||
import { CircleCheck, Document, Files, Operation, Plus, Refresh, Search, Upload, WarningFilled } from '@element-plus/icons-vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { getOrgSnapshot, type OrgSnapshotResponse } from '@/api/aihr/mobile-admin';
|
||||
import {
|
||||
addGoldenSample,
|
||||
approveKnowledgeGlossary,
|
||||
@@ -1437,7 +1507,10 @@ const sampleReviewForm = ref({
|
||||
result: 'CONFIRMED' as NonNullable<RuleReviewSample['reviewResult']>,
|
||||
note: ''
|
||||
});
|
||||
const openSoftQualityIssues = computed(() => qualityIssues.value.filter((issue) => issue.status === 'OPEN' && issue.gateType === 'SOFT'));
|
||||
const openSoftQualityIssues = computed(() => {
|
||||
const openIssues = qualityIssues.value.filter((issue) => issue.status === 'OPEN' && issue.gateType === 'SOFT');
|
||||
return Array.from(new Map(openIssues.map((issue) => [issue.reasonCode, issue])).values());
|
||||
});
|
||||
const hasVersionConflict = computed(() => qualityIssues.value.some((issue) => issue.reasonCode === 'VERSION_CONFLICT'));
|
||||
const unresolvedQualityConflicts = computed(() =>
|
||||
qualityConflicts.value.filter((conflict) => conflict.status === 'PENDING_REVIEW' || conflict.status === 'CONFIRMED')
|
||||
@@ -1446,6 +1519,23 @@ const semanticAnalysisComplete = computed(() => ['COMPLETE', 'NOT_REQUIRED'].inc
|
||||
const privacyDerivativeReady = computed(() => ['CLEAN', 'REDACTED'].includes(qualityPrivacyPreview.value?.privacyStatus || ''));
|
||||
const selectedQualityAsset = ref<QualityAsset | null>(null);
|
||||
const qualityDialogVisible = ref(false);
|
||||
type QualityScopeField = 'applicableRole' | 'applicableRegion' | 'applicableProject';
|
||||
type QualityScopeMode = 'ALL' | 'SPECIFIED';
|
||||
const scopeModeOptions = [
|
||||
{ label: '全部', value: 'ALL' },
|
||||
{ label: '指定', value: 'SPECIFIED' }
|
||||
];
|
||||
const qualityScopeModes = ref<Record<QualityScopeField, QualityScopeMode>>({
|
||||
applicableRole: 'ALL',
|
||||
applicableRegion: 'ALL',
|
||||
applicableProject: 'ALL'
|
||||
});
|
||||
const qualityScopeOptionsLoading = ref(false);
|
||||
const qualityScopeOptions = ref<Record<QualityScopeField, string[]>>({
|
||||
applicableRole: [],
|
||||
applicableRegion: [],
|
||||
applicableProject: []
|
||||
});
|
||||
const qualityReviewForm = ref<QualityReviewRequest>({
|
||||
reason: '',
|
||||
usageType: 'REFERENCE_ONLY',
|
||||
@@ -1543,6 +1633,96 @@ const usageOptions = [
|
||||
{ label: '仅供参考', value: 'REFERENCE_ONLY' }
|
||||
];
|
||||
|
||||
const qualityReasonLabels: Record<string, string> = {
|
||||
PARSE_EMPTY: '未解析到正文',
|
||||
PARSE_FAILED: '内容解析异常',
|
||||
PAGE_COUNT_MISMATCH: '页数不一致',
|
||||
SOURCE_UNAVAILABLE: '来源文件不可用',
|
||||
SOURCE_UNKNOWN: '来源权威不明确',
|
||||
SOURCE_EVIDENCE_INSUFFICIENT: '来源依据不足',
|
||||
SOURCE_VERSION_MISSING: '来源版本缺失',
|
||||
PII_REDACTED: '已执行个人信息脱敏',
|
||||
PII_DETECTED: '检测到未脱敏个人信息',
|
||||
SECRET_DETECTED: '检测到疑似密钥',
|
||||
CROSS_TENANT_REFERENCE: '检测到跨租户引用',
|
||||
PROMPT_INJECTION_SUSPECTED: '检测到疑似提示词注入',
|
||||
POLICY_EXPIRED: '资料已超过有效期',
|
||||
SYNTHETIC_UNDECLARED: '合成内容未声明',
|
||||
SYNTHETIC_UNVERIFIED: '合成内容待核验',
|
||||
EXACT_DUPLICATE: '完全重复资料',
|
||||
OCR_LOW_CONFIDENCE: '图片文字识别可信度低',
|
||||
ASR_LOW_CONFIDENCE: '语音识别可信度低',
|
||||
HEADER_FOOTER_NOISE: '页眉页脚噪声',
|
||||
PAGINATION_NOISE: '页码噪声',
|
||||
CHUNK_CONTEXT_BROKEN: '片段上下文不完整',
|
||||
DOMAIN_IRRELEVANT: '业务相关性较低',
|
||||
CONTEXT_INCOMPLETE: '案例要素不完整',
|
||||
NEAR_DUPLICATE: '高度相似资料',
|
||||
SEMANTIC_DUPLICATE: '语义重复资料',
|
||||
SEMANTIC_ANALYSIS_DEGRADED: '语义分析已降级',
|
||||
SEMANTIC_ANALYSIS_FAILED: '语义分析失败',
|
||||
VERSION_CONFLICT: '资料版本冲突',
|
||||
EXPERT_CONFLICT: '专家观点冲突',
|
||||
DOWNSTREAM_ANSWER_DISPUTED: '引用回答存在争议',
|
||||
HUMAN_REVIEW_REQUIRED: '需要人工审核'
|
||||
};
|
||||
|
||||
const qualityReasonDescriptions: Record<string, string> = {
|
||||
PARSE_EMPTY: '系统没有从文件中提取到可审核的正文。',
|
||||
PARSE_FAILED: '解析结果存在异常字符或内容损坏。',
|
||||
PAGE_COUNT_MISMATCH: '文件声明的页数与实际解析页数不一致。',
|
||||
SOURCE_UNAVAILABLE: '原始文件当前无法读取或验证。',
|
||||
SOURCE_UNKNOWN: '尚未确认资料的发布主体或权威级别。',
|
||||
SOURCE_EVIDENCE_INSUFFICIENT: '资料明确缺少事实来源或支撑依据。',
|
||||
SOURCE_VERSION_MISSING: '尚未填写可追溯的资料版本。',
|
||||
PII_REDACTED: '系统已对识别到的个人信息进行脱敏处理。',
|
||||
PII_DETECTED: '正文仍包含可能识别到个人的信息。',
|
||||
SECRET_DETECTED: '正文包含疑似账号、口令或访问密钥。',
|
||||
CROSS_TENANT_REFERENCE: '资料引用了其他租户的数据。',
|
||||
PROMPT_INJECTION_SUSPECTED: '正文包含可能影响模型行为的指令性内容。',
|
||||
POLICY_EXPIRED: '资料已不在标注的有效期内。',
|
||||
SYNTHETIC_UNDECLARED: '系统发现合成内容,但资料没有如实声明。',
|
||||
SYNTHETIC_UNVERIFIED: '合成内容尚未由业务人员核实。',
|
||||
EXACT_DUPLICATE: '检测到内容完全相同的历史资料,不会自动合并。',
|
||||
OCR_LOW_CONFIDENCE: '图片文字识别结果的可信度不足。',
|
||||
ASR_LOW_CONFIDENCE: '语音转写结果的可信度不足。',
|
||||
HEADER_FOOTER_NOISE: '多页重复出现短文本,可能是页眉或页脚。',
|
||||
PAGINATION_NOISE: '正文中检测到独立的页码行。',
|
||||
CHUNK_CONTEXT_BROKEN: '部分切片过短或依赖前后文,单独引用时可能失真。',
|
||||
DOMAIN_IRRELEVANT: '没有检测到已配置的物业服务或企业学习领域内容。',
|
||||
CONTEXT_INCOMPLETE: '案例的场景、事实、行动、依据或结果信息不完整。',
|
||||
NEAR_DUPLICATE: '检测到内容高度相似的历史资料。',
|
||||
SEMANTIC_DUPLICATE: '检测到表达不同但含义接近的历史资料。',
|
||||
SEMANTIC_ANALYSIS_DEGRADED: '当前仅完成降级分析,重复判断能力有限。',
|
||||
SEMANTIC_ANALYSIS_FAILED: '语义重复分析没有成功完成。',
|
||||
VERSION_CONFLICT: '同名资料已有已发布版本,且本次内容发生变化。',
|
||||
EXPERT_CONFLICT: '资料中的结论与已记录的专家结论存在冲突。',
|
||||
DOWNSTREAM_ANSWER_DISPUTED: '使用该资料生成的回答曾被标记为有争议。',
|
||||
HUMAN_REVIEW_REQUIRED: '上传、提取和生成的内容在人工批准前均不视为可信。'
|
||||
};
|
||||
|
||||
const qualityAcknowledgements: Record<string, string> = {
|
||||
PAGINATION_NOISE: '已确认页码不属于正文,不影响内容含义。',
|
||||
HEADER_FOOTER_NOISE: '已确认重复页眉页脚不影响正文理解。',
|
||||
HUMAN_REVIEW_REQUIRED: '已人工阅读资料,并确认内容可进入生产检索。',
|
||||
EXACT_DUPLICATE: '已对照重复资料,确认保留当前版本且不自动合并。',
|
||||
NEAR_DUPLICATE: '已对照相似资料,确认两份资料的保留方式。',
|
||||
SEMANTIC_DUPLICATE: '已对照语义相近资料,确认不会造成错误召回。',
|
||||
SOURCE_UNKNOWN: '已核实并补充正确的来源权威级别。',
|
||||
SOURCE_EVIDENCE_INSUFFICIENT: '已核查缺失依据,并确认当前发布结论可追溯。',
|
||||
SOURCE_VERSION_MISSING: '已补充并核对可追溯的来源版本。',
|
||||
SYNTHETIC_UNVERIFIED: '已人工核实合成内容与正式业务事实一致。',
|
||||
OCR_LOW_CONFIDENCE: '已对照原图复核识别文字。',
|
||||
ASR_LOW_CONFIDENCE: '已回听原音频复核转写内容。',
|
||||
CHUNK_CONTEXT_BROKEN: '已抽查相关片段,确认单独引用不会改变原意。',
|
||||
DOMAIN_IRRELEVANT: '已确认资料属于本次知识库的业务范围。',
|
||||
CONTEXT_INCOMPLETE: '已确认缺失要素不影响该资料的预期用途。',
|
||||
SEMANTIC_ANALYSIS_DEGRADED: '已人工扩大重复资料核查范围并确认可发布。',
|
||||
VERSION_CONFLICT: '已确认新旧版本关系和适用范围。',
|
||||
EXPERT_CONFLICT: '已完成业务裁决并确认采用的权威结论。',
|
||||
DOWNSTREAM_ANSWER_DISPUTED: '已复核争议回答及引用原文,确认本资料可继续使用。'
|
||||
};
|
||||
|
||||
const seedOverview: ProcessingOverview = {
|
||||
metrics: [
|
||||
{ key: 'total', label: '资料总量', value: '3.2 GB', note: '已接入 1,286 个文件', type: 'danger' },
|
||||
@@ -2623,15 +2803,23 @@ async function openQualityReview(asset: QualityAsset) {
|
||||
acceptedReasonCodes: [],
|
||||
supersedesAssetId: undefined
|
||||
};
|
||||
qualityScopeModes.value = {
|
||||
applicableRole: asset.applicableRole?.trim() ? 'SPECIFIED' : 'ALL',
|
||||
applicableRegion: asset.applicableRegion?.trim() ? 'SPECIFIED' : 'ALL',
|
||||
applicableProject: asset.applicableProject?.trim() ? 'SPECIFIED' : 'ALL'
|
||||
};
|
||||
buildQualityScopeOptions(asset, [], undefined);
|
||||
qualityScopeOptionsLoading.value = true;
|
||||
try {
|
||||
const [issues, privacyPreview, conflicts, published, versions, impact, review] = await Promise.all([
|
||||
const [issues, privacyPreview, conflicts, published, versions, impact, review, orgSnapshot] = await Promise.all([
|
||||
listQualityIssues(asset.id),
|
||||
getQualityPrivacyPreview(asset.id),
|
||||
listQualityConflicts(asset.id),
|
||||
listQualityAssets('PUBLISHED', 200),
|
||||
listAssetVersions(asset.id),
|
||||
getDeletionImpact(asset.id),
|
||||
asset.lifecycleStatus === 'REVIEW_PENDING' ? getAssetReviewPackage(asset.id) : Promise.resolve({ data: null })
|
||||
asset.lifecycleStatus === 'REVIEW_PENDING' ? getAssetReviewPackage(asset.id) : Promise.resolve({ data: null }),
|
||||
getOrgSnapshot({ status: 'active', pageNum: 1, pageSize: 500 }).catch(() => undefined)
|
||||
]);
|
||||
if (selectedQualityAsset.value?.id !== asset.id) return;
|
||||
qualityIssues.value = issues.data || [];
|
||||
@@ -2641,6 +2829,7 @@ async function openQualityReview(asset: QualityAsset) {
|
||||
assetVersions.value = versions.data || [];
|
||||
deletionImpact.value = impact.data || null;
|
||||
reviewPackage.value = review.data || null;
|
||||
buildQualityScopeOptions(asset, published.data || [], orgSnapshot?.data);
|
||||
} catch {
|
||||
if (selectedQualityAsset.value?.id !== asset.id) return;
|
||||
qualityIssues.value = [];
|
||||
@@ -2652,10 +2841,37 @@ async function openQualityReview(asset: QualityAsset) {
|
||||
deletionImpact.value = null;
|
||||
ElMessage.error('质量原因加载失败');
|
||||
} finally {
|
||||
if (selectedQualityAsset.value?.id === asset.id) qualityDetailLoading.value = false;
|
||||
if (selectedQualityAsset.value?.id === asset.id) {
|
||||
qualityScopeOptionsLoading.value = false;
|
||||
qualityDetailLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeQualityScope(field: QualityScopeField, mode: string | number | boolean | undefined) {
|
||||
if (mode === 'ALL') qualityReviewForm.value[field] = '';
|
||||
}
|
||||
|
||||
function buildQualityScopeOptions(asset: QualityAsset, history: QualityAsset[], snapshot?: OrgSnapshotResponse) {
|
||||
const assets = [...qualityAssets.value, ...history, asset];
|
||||
const distinct = (values: Array<string | undefined>) =>
|
||||
Array.from(new Set(values.map((value) => value?.trim()).filter((value): value is string => Boolean(value)))).sort((left, right) =>
|
||||
left.localeCompare(right, 'zh-CN')
|
||||
);
|
||||
qualityScopeOptions.value = {
|
||||
applicableRole: distinct([...assets.map((item) => item.applicableRole), ...(snapshot?.rows.map((item) => item.positionName) || [])]),
|
||||
applicableRegion: distinct(assets.map((item) => item.applicableRegion)),
|
||||
applicableProject: distinct([
|
||||
...assets.map((item) => item.applicableProject),
|
||||
...(snapshot?.projects.map((item) => item.projectName || item.projectCode) || [])
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
function qualityScopeOptionLabel(value: string) {
|
||||
return ({ employee: '员工' } as Record<string, string>)[value.toLowerCase()] || value;
|
||||
}
|
||||
|
||||
async function reviewConflict(conflict: QualityConflict, decision: 'CONFIRMED' | 'FALSE_POSITIVE' | 'RESOLVED') {
|
||||
const labels = {
|
||||
CONFIRMED: '说明为何判定为真实冲突',
|
||||
@@ -2688,6 +2904,13 @@ async function publishAsset() {
|
||||
ElMessage.warning('请填写来源权威级别、来源版本和审核意见');
|
||||
return;
|
||||
}
|
||||
const missingScope = (Object.keys(qualityScopeModes.value) as QualityScopeField[]).find(
|
||||
(field) => qualityScopeModes.value[field] === 'SPECIFIED' && !form[field]?.trim()
|
||||
);
|
||||
if (missingScope) {
|
||||
ElMessage.warning(`请选择${{ applicableRole: '适用岗位', applicableRegion: '适用区域', applicableProject: '适用项目' }[missingScope]}`);
|
||||
return;
|
||||
}
|
||||
const openReasons = openSoftQualityIssues.value.map((item) => item.reasonCode);
|
||||
if (openReasons.some((reason) => !form.acceptedReasonCodes.includes(reason))) {
|
||||
ElMessage.warning('请逐项确认所有软质量提示');
|
||||
@@ -2699,7 +2922,13 @@ async function publishAsset() {
|
||||
}
|
||||
qualityPublishing.value = true;
|
||||
try {
|
||||
await publishQualityAsset(asset.id, { ...form, reason: form.reason.trim() });
|
||||
await publishQualityAsset(asset.id, {
|
||||
...form,
|
||||
reason: form.reason.trim(),
|
||||
applicableRole: form.applicableRole?.trim() || undefined,
|
||||
applicableRegion: form.applicableRegion?.trim() || undefined,
|
||||
applicableProject: form.applicableProject?.trim() || undefined
|
||||
});
|
||||
ElMessage.success('已批准并发布到生产检索');
|
||||
qualityDialogVisible.value = false;
|
||||
await Promise.all([loadQualityAssets(), loadOverview(), loadIndexHealth(), loadQualityMonitoring()]);
|
||||
@@ -2817,14 +3046,67 @@ function usageLabel(usage: string) {
|
||||
return usageOptions.find((item) => item.value === usage)?.label || usage;
|
||||
}
|
||||
|
||||
function issueEvidence(value: string) {
|
||||
function sourceAuthorityLabel(value: string) {
|
||||
const labels: Record<string, string> = {
|
||||
FORMAL_POLICY: '正式制度',
|
||||
COMPANY_POLICY: '公司正式制度',
|
||||
APPROVED_SOP: '已批准 SOP',
|
||||
REGULATION: '法规',
|
||||
EMPLOYEE_SUBMISSION: '员工经验投稿',
|
||||
UNKNOWN: '来源待确认'
|
||||
};
|
||||
if (!value) return '未填写';
|
||||
return labels[value] || (/^[A-Z0-9_]+$/.test(value) ? '其他来源(待确认)' : value);
|
||||
}
|
||||
|
||||
function riskLevelLabel(value: string) {
|
||||
return ({ LOW: '低', MEDIUM: '中', HIGH: '高', CRITICAL: '关键' } as Record<string, string>)[value] || '未知';
|
||||
}
|
||||
|
||||
function versionStatusLabel(value: string) {
|
||||
return (
|
||||
(
|
||||
{
|
||||
REVIEW_PENDING: '待审核',
|
||||
QUARANTINED: '已隔离',
|
||||
APPROVED: '已批准',
|
||||
PUBLISHED: '已发布',
|
||||
DEPRECATED: '已撤回'
|
||||
} as Record<string, string>
|
||||
)[value] || '未知状态'
|
||||
);
|
||||
}
|
||||
|
||||
function qualityReasonLabel(reasonCode: string) {
|
||||
return qualityReasonLabels[reasonCode] || '其他质量提示';
|
||||
}
|
||||
|
||||
function qualityIssueSummary(value: string) {
|
||||
try {
|
||||
return JSON.parse(value || '{}').summary || '已命中质量规则';
|
||||
return String(JSON.parse(value || '{}').summary || '');
|
||||
} catch {
|
||||
return '已命中质量规则';
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function qualityIssueDescription(issue: QualityIssue) {
|
||||
const summary = qualityIssueSummary(issue.evidenceJson);
|
||||
const count = summary.match(/(?:Detected|covers)\s+(\d+)/i)?.[1];
|
||||
if (issue.reasonCode === 'PAGINATION_NOISE' && count) return `正文中检测到 ${count} 行独立页码。`;
|
||||
if (issue.reasonCode === 'CHUNK_CONTEXT_BROKEN' && count) return `检测到 ${count} 个过短或依赖前后文的片段。`;
|
||||
if (issue.reasonCode === 'CONTEXT_INCOMPLETE' && count) return `案例目前覆盖 5 类必要要素中的 ${count} 类。`;
|
||||
return qualityReasonDescriptions[issue.reasonCode] || '系统命中了一项需要人工核对的质量规则。';
|
||||
}
|
||||
|
||||
function qualityIssueAction(issue: QualityIssue) {
|
||||
if (issue.gateType === 'HARD') return '请修订原资料并重新上传,解决后才能发布。';
|
||||
return qualityAcknowledgements[issue.reasonCode] || '请对照原资料人工核查,确认不影响发布后再勾选接受。';
|
||||
}
|
||||
|
||||
function qualityAcknowledgementText(reasonCode: string) {
|
||||
return qualityAcknowledgements[reasonCode] || '已对照原资料完成核查,并确认该提示不影响本次发布。';
|
||||
}
|
||||
|
||||
function formatLag(seconds: number) {
|
||||
if (seconds < 60) return `${seconds} 秒`;
|
||||
if (seconds < 3600) return `${Math.floor(seconds / 60)} 分钟`;
|
||||
@@ -4003,10 +4285,54 @@ async function requestAssetPurge(asset: QualityAsset) {
|
||||
gap: 0 16px;
|
||||
}
|
||||
|
||||
.quality-scope-field {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
|
||||
.el-segmented {
|
||||
justify-self: start;
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.quality-acknowledgement-tip {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.reason-checks {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px 12px;
|
||||
gap: 8px 12px;
|
||||
width: 100%;
|
||||
|
||||
.el-checkbox {
|
||||
align-items: flex-start;
|
||||
height: auto;
|
||||
min-height: 62px;
|
||||
margin-right: 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.reason-check-content {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
line-height: 1.45;
|
||||
|
||||
strong {
|
||||
color: #303846;
|
||||
}
|
||||
|
||||
small {
|
||||
color: #667085;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
@@ -4057,6 +4383,10 @@ async function requestAssetPurge(asset: QualityAsset) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.quality-scope-field {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.quality-metrics {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user