feat(calls): 前端录音播放本地优先(/api/media/{hash}) + OSS 回退 + drawer
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,8 @@ export interface PhoneCall {
|
||||
sim_phone?: string | null
|
||||
/** OSS URL of the recording — present only when a recording exists. */
|
||||
record_file?: string | null
|
||||
/** sha256 media_hash of the localized recording (NULL until downloaded). */
|
||||
record_media_hash?: string | null
|
||||
/**
|
||||
* Verbatim upstream call record (camelCase keys: id/startCallTime,
|
||||
* callDuration/callSeconds/ringTime, callStatus/incomingCall, userId/
|
||||
|
||||
@@ -211,6 +211,14 @@ export function toDisplayField(c: PhoneCall, camelKey: string): DisplayField {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return { key: camelKey, label, value, render: 'empty', text: '' }
|
||||
}
|
||||
// 录音:优先本地化的 media_hash(/api/media/{hash}),否则原 OSS URL
|
||||
if (camelKey === 'recordFile') {
|
||||
const local = c.record_media_hash ? `/api/media/${c.record_media_hash}` : null
|
||||
const url = local ?? (typeof value === 'string' && value ? value : null)
|
||||
if (url) {
|
||||
return { key: camelKey, label, value: url, render: 'audio', text: '' }
|
||||
}
|
||||
}
|
||||
if (AUDIO_KEYS.has(camelKey) && typeof value === 'string') {
|
||||
return { key: camelKey, label, value, render: 'audio', text: '' }
|
||||
}
|
||||
|
||||
@@ -90,10 +90,11 @@ function timeCell(row: PhoneCall, camelKey: string): number | undefined {
|
||||
}
|
||||
|
||||
function playRecord(row: PhoneCall): void {
|
||||
const local = row.record_media_hash ? `/api/media/${row.record_media_hash}` : null
|
||||
const file =
|
||||
(typeof cell(row, 'recordFile') === 'string'
|
||||
? (cell(row, 'recordFile') as string)
|
||||
: null) ?? row.record_file
|
||||
local ??
|
||||
(typeof cell(row, 'recordFile') === 'string' ? (cell(row, 'recordFile') as string) : null) ??
|
||||
row.record_file
|
||||
if (!file) {
|
||||
ElMessage.info('该通话无录音文件')
|
||||
return
|
||||
@@ -185,7 +186,7 @@ onMounted(load)
|
||||
</el-table-column>
|
||||
<el-table-column label="通话录音" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.record_file || cell(row, 'recordFile')" size="small" type="primary" link @click.stop="playRecord(row)">播放</el-button>
|
||||
<el-button v-if="row.record_media_hash || row.record_file || cell(row, 'recordFile')" size="small" type="primary" link @click.stop="playRecord(row)">播放</el-button>
|
||||
<span v-else style="color:#c0c4cc">--</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -215,7 +216,7 @@ onMounted(load)
|
||||
<el-table-column label="通话时间和录音" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<div>{{ formatTime(row.start_call_time) }}</div>
|
||||
<el-button v-if="row.record_file || cell(row, 'ossFileName')" size="small" type="primary" link @click.stop="playRecord(row)" style="margin-top:2px">播放录音</el-button>
|
||||
<el-button v-if="row.record_media_hash || row.record_file || cell(row, 'ossFileName')" size="small" type="primary" link @click.stop="playRecord(row)" style="margin-top:2px">播放录音</el-button>
|
||||
<span v-else class="yk-cell__sub">无录音</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -251,7 +252,7 @@ onMounted(load)
|
||||
<el-table-column label="录音" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.record_file || cell(row, 'recordFile')"
|
||||
v-if="row.record_media_hash || row.record_file || cell(row, 'recordFile')"
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
|
||||
Reference in New Issue
Block a user