feat(calls): VoiceClipModal 录音异常检查(本地元数据标记+仅看异常筛选)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,42 @@ function durationDisplay(row: VoiceClip): string {
|
|||||||
return row.duration_format || formatCallDuration(row.mp3_duration)
|
return row.duration_format || formatCallDuration(row.mp3_duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----- 录音异常检查(本地元数据判定,无网络/无后端) -----
|
||||||
|
interface Anomaly {
|
||||||
|
type: string
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于本地元数据判定单条 voice_clip 的异常:
|
||||||
|
* - 无录音文件(oss_file_name 缺失)
|
||||||
|
* - 时长为 0(mp3_duration 缺失或为 0)
|
||||||
|
* - 时间缺失(start_time / end_time 缺失)
|
||||||
|
* 纯前端计算,仅作用于当前已加载分页(≤50 条),不涉及服务端查询。
|
||||||
|
*/
|
||||||
|
function clipAnomalies(clip: VoiceClip): Anomaly[] {
|
||||||
|
const anomalies: Anomaly[] = []
|
||||||
|
if (!clip.oss_file_name) anomalies.push({ type: 'no_file', label: '无录音文件' })
|
||||||
|
if (!clip.mp3_duration || clip.mp3_duration === 0) {
|
||||||
|
anomalies.push({ type: 'zero_duration', label: '时长为0' })
|
||||||
|
}
|
||||||
|
if (!clip.start_time || !clip.end_time) {
|
||||||
|
anomalies.push({ type: 'no_time', label: '时间缺失' })
|
||||||
|
}
|
||||||
|
return anomalies
|
||||||
|
}
|
||||||
|
|
||||||
|
// 「仅看异常」开关:开启时仅展示当前分页内命中异常的片段。
|
||||||
|
const onlyAnomaly = ref(false)
|
||||||
|
|
||||||
|
// 当前分页命中异常的片段数量(用于"N 条异常"汇总,不受开关影响)。
|
||||||
|
const anomalyCount = computed(() => items.value.filter((c) => clipAnomalies(c).length > 0).length)
|
||||||
|
|
||||||
|
// 表格实际渲染的数据:开关 ON 时过滤为仅异常项,OFF 时原样展示。
|
||||||
|
const displayItems = computed(() =>
|
||||||
|
onlyAnomaly.value ? items.value.filter((c) => clipAnomalies(c).length > 0) : items.value,
|
||||||
|
)
|
||||||
|
|
||||||
function playClip(row: VoiceClip): void {
|
function playClip(row: VoiceClip): void {
|
||||||
const url = row.oss_file_name
|
const url = row.oss_file_name
|
||||||
if (!url) {
|
if (!url) {
|
||||||
@@ -296,6 +332,17 @@ watch(
|
|||||||
@change="onFilterChange"
|
@change="onFilterChange"
|
||||||
/>
|
/>
|
||||||
<el-button link type="primary" size="small" @click="resetFilters">重置</el-button>
|
<el-button link type="primary" size="small" @click="resetFilters">重置</el-button>
|
||||||
|
<span class="yk-voice-clip__filter-spacer" />
|
||||||
|
<el-switch v-model="onlyAnomaly" size="small" />
|
||||||
|
<span class="yk-voice-clip__filter-label">仅看异常</span>
|
||||||
|
<el-tag
|
||||||
|
:type="anomalyCount > 0 ? 'danger' : 'success'"
|
||||||
|
size="small"
|
||||||
|
effect="plain"
|
||||||
|
class="yk-voice-clip__anomaly-count"
|
||||||
|
>
|
||||||
|
{{ anomalyCount }} 条异常
|
||||||
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-alert
|
<el-alert
|
||||||
@@ -320,7 +367,7 @@ watch(
|
|||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:data="items"
|
:data="displayItems"
|
||||||
border
|
border
|
||||||
size="small"
|
size="small"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@@ -341,6 +388,23 @@ watch(
|
|||||||
<el-table-column label="录音时长" width="100" align="right">
|
<el-table-column label="录音时长" width="100" align="right">
|
||||||
<template #default="{ row }">{{ durationDisplay(row) }}</template>
|
<template #default="{ row }">{{ durationDisplay(row) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="录音异常" width="140" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="clipAnomalies(row).length">
|
||||||
|
<el-tag
|
||||||
|
v-for="a in clipAnomalies(row)"
|
||||||
|
:key="a.type"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
effect="light"
|
||||||
|
style="margin: 2px"
|
||||||
|
>
|
||||||
|
{{ a.label }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<span v-else style="color: #67c23a">✓</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="已匹配客户" min-width="120">
|
<el-table-column label="已匹配客户" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row.matched_friend_id" :title="row.matched_friend_id">
|
<span v-if="row.matched_friend_id" :title="row.matched_friend_id">
|
||||||
@@ -475,6 +539,14 @@ watch(
|
|||||||
color: #606266;
|
color: #606266;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yk-voice-clip__filter-spacer {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yk-voice-clip__anomaly-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.yk-voice-clip__player {
|
.yk-voice-clip__player {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user