fix(aihr): protect case media playback

This commit is contained in:
2026-07-15 01:23:41 +08:00
parent 05ba1a249b
commit 09c3cbecb0
10 changed files with 146 additions and 31 deletions
+33 -5
View File
@@ -110,7 +110,7 @@
<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>
<button class="ghost-button refresh-button" @click="closeCaseDetail">关闭</button>
</view>
<text class="detail-label">原始转写</text>
<text class="case-content">{{ caseDetail.transcript || '暂无转写内容' }}</text>
@@ -127,10 +127,12 @@
<text class="detail-label">主管点评</text>
<text class="case-note">{{ caseDetail.supervisorComment }}</text>
</view>
<view v-if="caseDetail.mediaUrl" class="case-media-block">
<view v-if="caseMediaUrl" class="case-media-block">
<text class="detail-label">原始音频</text>
<audio class="case-audio" :src="caseDetail.mediaUrl" controls />
<audio class="case-audio" :src="caseMediaUrl" controls />
</view>
<text v-else-if="caseMediaLoading" class="case-note">音频加载中…</text>
<text v-else-if="caseDetail.mediaOssId" class="case-note">音频加载失败,请重试或联系管理员</text>
<text class="case-note">当前版本已保留原始音频回放、学习点和主管点评;预渲染视频样片仍待正式媒体资源接入。</text>
</view>
</view>
@@ -138,9 +140,10 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import { onShow, onUnload } from '@dcloudio/uni-app';
import type { CaseCurateResponse, CaseDetailResponse, CaseOrganizeResponse, CaseRecord, CaseSelectedFile, CaseUploadResponse } from '@/types/api';
import { chooseCaseAudio, curateCase, getCaseCapabilities, getCaseDetail, getCaseRecords, organizeCase, uploadCaseAudio } from '@/services/case';
import { chooseCaseAudio, curateCase, getCaseCapabilities, getCaseDetail, getCaseMediaUrl, getCaseRecords, organizeCase, uploadCaseAudio } from '@/services/case';
import { releaseAuthenticatedFileUrl } from '@/services/api';
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
import { navigateBackToToday, resetPageScroll } from '@/services/navigation';
import { ensureEmployeePosition } from '@/services/position';
@@ -152,6 +155,8 @@ const organizeResult = ref<CaseOrganizeResponse | null>(null);
const curateResult = ref<CaseCurateResponse | null>(null);
const records = ref<CaseRecord[]>([]);
const caseDetail = ref<CaseDetailResponse | null>(null);
const caseMediaUrl = ref('');
const caseMediaLoading = ref(false);
const canSubmitCases = ref(false);
const status = ref<'idle' | 'uploading' | 'organizing' | 'curating'>('idle');
const recordsLoading = ref(false);
@@ -175,10 +180,16 @@ const clearCaseData = () => {
selectedFile.value = null;
records.value = [];
caseDetail.value = null;
clearCaseMediaUrl();
canSubmitCases.value = false;
resetResult();
};
const clearCaseMediaUrl = () => {
releaseAuthenticatedFileUrl(caseMediaUrl.value);
caseMediaUrl.value = '';
};
const refreshAuthState = () => {
loggedIn.value = isLoggedIn();
};
@@ -246,16 +257,31 @@ const refreshRecords = () => {
const openCaseDetail = async (record: CaseRecord) => {
if (!record.caseId || caseDetailLoading.value) return;
caseDetailLoading.value = true;
caseMediaLoading.value = true;
message.value = '';
clearCaseMediaUrl();
try {
caseDetail.value = await getCaseDetail(record.caseId);
if (caseDetail.value.mediaOssId) {
try {
caseMediaUrl.value = await getCaseMediaUrl(record.caseId);
} catch {
caseMediaUrl.value = '';
}
}
} catch (error) {
handleRequestError(error, '案例详情加载失败');
} finally {
caseMediaLoading.value = false;
caseDetailLoading.value = false;
}
};
const closeCaseDetail = () => {
caseDetail.value = null;
clearCaseMediaUrl();
};
const submitCase = async () => {
if (!selectedFile.value || busy.value) return;
message.value = '';
@@ -305,6 +331,8 @@ const handleShow = async () => {
onShow(() => {
void handleShow();
});
onUnload(clearCaseMediaUrl);
</script>
<style scoped>
+4 -1
View File
@@ -7,7 +7,7 @@ import type {
CaseSelectedFile,
CaseUploadResponse
} from '@/types/api';
import { apiRequest, apiUrl, authHeaders, readTextPayload } from './api';
import { apiRequest, apiUrl, authHeaders, authenticatedFileUrl, readTextPayload } from './api';
const audioExtensions = ['.mp3', '.wav', '.m4a', '.webm', '.aac', '.ogg'];
@@ -94,3 +94,6 @@ export const getCaseDetail = (caseId: string) =>
method: 'GET',
timeout: 15000
});
export const getCaseMediaUrl = (caseId: string) =>
authenticatedFileUrl(`/api/knowledge/case/records/${encodeURIComponent(caseId)}/media`);