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
|
sim_phone?: string | null
|
||||||
/** OSS URL of the recording — present only when a recording exists. */
|
/** OSS URL of the recording — present only when a recording exists. */
|
||||||
record_file?: string | null
|
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,
|
* Verbatim upstream call record (camelCase keys: id/startCallTime,
|
||||||
* callDuration/callSeconds/ringTime, callStatus/incomingCall, userId/
|
* callDuration/callSeconds/ringTime, callStatus/incomingCall, userId/
|
||||||
|
|||||||
@@ -211,6 +211,14 @@ export function toDisplayField(c: PhoneCall, camelKey: string): DisplayField {
|
|||||||
if (value === null || value === undefined || value === '') {
|
if (value === null || value === undefined || value === '') {
|
||||||
return { key: camelKey, label, value, render: 'empty', text: '' }
|
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') {
|
if (AUDIO_KEYS.has(camelKey) && typeof value === 'string') {
|
||||||
return { key: camelKey, label, value, render: 'audio', text: '' }
|
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 {
|
function playRecord(row: PhoneCall): void {
|
||||||
|
const local = row.record_media_hash ? `/api/media/${row.record_media_hash}` : null
|
||||||
const file =
|
const file =
|
||||||
(typeof cell(row, 'recordFile') === 'string'
|
local ??
|
||||||
? (cell(row, 'recordFile') as string)
|
(typeof cell(row, 'recordFile') === 'string' ? (cell(row, 'recordFile') as string) : null) ??
|
||||||
: null) ?? row.record_file
|
row.record_file
|
||||||
if (!file) {
|
if (!file) {
|
||||||
ElMessage.info('该通话无录音文件')
|
ElMessage.info('该通话无录音文件')
|
||||||
return
|
return
|
||||||
@@ -185,7 +186,7 @@ onMounted(load)
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="通话录音" width="110" align="center">
|
<el-table-column label="通话录音" width="110" align="center">
|
||||||
<template #default="{ row }">
|
<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>
|
<span v-else style="color:#c0c4cc">--</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -215,7 +216,7 @@ onMounted(load)
|
|||||||
<el-table-column label="通话时间和录音" min-width="180">
|
<el-table-column label="通话时间和录音" min-width="180">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div>{{ formatTime(row.start_call_time) }}</div>
|
<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>
|
<span v-else class="yk-cell__sub">无录音</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -251,7 +252,7 @@ onMounted(load)
|
|||||||
<el-table-column label="录音" width="100" align="center">
|
<el-table-column label="录音" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.record_file || cell(row, 'recordFile')"
|
v-if="row.record_media_hash || row.record_file || cell(row, 'recordFile')"
|
||||||
size="small"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
|
|||||||
Reference in New Issue
Block a user