fix(aihr): protect case media playback
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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`);
|
||||
|
||||
Reference in New Issue
Block a user