feat(aihr): add scoped case detail view

This commit is contained in:
2026-07-14 05:04:01 +08:00
parent e4931753a3
commit 153382cb57
12 changed files with 157 additions and 10 deletions
+63 -4
View File
@@ -77,25 +77,43 @@
</button>
</view>
<view v-if="records.length" class="record-list">
<view v-for="record in records" :key="`${record.title}-${record.time}`" class="record-row">
<view v-for="record in records" :key="`${record.caseId}-${record.title}-${record.time}`" class="record-row">
<view>
<text class="record-title">{{ record.title }}</text>
<text class="record-desc">{{ record.time }} · {{ record.owner }} · {{ record.type }}</text>
<text class="record-summary">{{ record.summary }}</text>
</view>
<text class="record-status">{{ record.status }}</text>
<view class="record-actions">
<text class="record-status">{{ record.status }}</text>
<button class="secondary-button detail-button" :disabled="caseDetailLoading" @click="openCaseDetail(record)">查看详情</button>
</view>
</view>
</view>
<view v-else class="empty">暂无入库案例</view>
</view>
<view v-if="caseDetail" class="panel case-detail-panel">
<view class="section-head">
<view>
<text class="panel-title">案例详情</text>
<text class="record-desc">{{ caseDetail.time }} · {{ caseDetail.owner }} · {{ caseDetail.status }}</text>
</view>
<button class="ghost-button refresh-button" @click="caseDetail = null">关闭</button>
</view>
<text class="detail-label">原始转写</text>
<text class="case-content">{{ caseDetail.transcript || '暂无转写内容' }}</text>
<text class="detail-label">案例摘要</text>
<text class="case-note">{{ caseDetail.summary || '暂无案例摘要' }}</text>
<text class="case-note">当前版本提供转写、摘要和状态详情;视频样片、学习点与主管点评待正式媒体和业务字段接入。</text>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import type { CaseCurateResponse, CaseOrganizeResponse, CaseRecord, CaseSelectedFile, CaseUploadResponse } from '@/types/api';
import { chooseCaseAudio, curateCase, getCaseRecords, organizeCase, uploadCaseAudio } from '@/services/case';
import type { CaseCurateResponse, CaseDetailResponse, CaseOrganizeResponse, CaseRecord, CaseSelectedFile, CaseUploadResponse } from '@/types/api';
import { chooseCaseAudio, curateCase, getCaseDetail, getCaseRecords, organizeCase, uploadCaseAudio } from '@/services/case';
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
import { navigateBackToToday, resetPageScroll } from '@/services/navigation';
import { ensureEmployeePosition } from '@/services/position';
@@ -106,8 +124,10 @@ const uploadResult = ref<CaseUploadResponse | null>(null);
const organizeResult = ref<CaseOrganizeResponse | null>(null);
const curateResult = ref<CaseCurateResponse | null>(null);
const records = ref<CaseRecord[]>([]);
const caseDetail = ref<CaseDetailResponse | null>(null);
const status = ref<'idle' | 'uploading' | 'organizing' | 'curating'>('idle');
const recordsLoading = ref(false);
const caseDetailLoading = ref(false);
const message = ref('');
const busy = computed(() => status.value !== 'idle');
const statusText = computed(() => {
@@ -126,6 +146,7 @@ const resetResult = () => {
const clearCaseData = () => {
selectedFile.value = null;
records.value = [];
caseDetail.value = null;
resetResult();
};
@@ -179,6 +200,19 @@ const refreshRecords = () => {
void loadRecords();
};
const openCaseDetail = async (record: CaseRecord) => {
if (!record.caseId || caseDetailLoading.value) return;
caseDetailLoading.value = true;
message.value = '';
try {
caseDetail.value = await getCaseDetail(record.caseId);
} catch (error) {
handleRequestError(error, '案例详情加载失败');
} finally {
caseDetailLoading.value = false;
}
};
const submitCase = async () => {
if (!selectedFile.value || busy.value) return;
message.value = '';
@@ -310,6 +344,31 @@ onShow(() => {
flex: 1;
}
.record-actions {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
}
.detail-button {
min-width: 84px;
padding: 6px 10px;
font-size: 12px;
}
.case-detail-panel {
display: flex;
flex-direction: column;
gap: 10px;
}
.detail-label {
color: #1a1a1a;
font-size: 14px;
font-weight: 700;
}
.tag-row {
display: flex;
flex-wrap: wrap;
+8
View File
@@ -1,5 +1,6 @@
import type {
CaseCurateResponse,
CaseDetailResponse,
CaseOrganizeResponse,
CaseRecord,
CaseSelectedFile,
@@ -78,3 +79,10 @@ export const getCaseRecords = () =>
method: 'GET',
timeout: 15000
});
export const getCaseDetail = (caseId: string) =>
apiRequest<CaseDetailResponse>({
url: `/api/knowledge/case/records/${encodeURIComponent(caseId)}`,
method: 'GET',
timeout: 15000
});
+13
View File
@@ -363,6 +363,7 @@ export interface CaseSummaryItem {
}
export interface CaseRecord {
caseId: string;
title: string;
type: string;
status: string;
@@ -371,6 +372,18 @@ export interface CaseRecord {
summary: string;
}
export interface CaseDetailResponse {
caseId: string;
fileName: string;
title: string;
type: string;
status: string;
time: string;
owner: string;
transcript: string;
summary: string;
}
export interface CaseUploadResponse {
caseId: string;
fileName: string;