Merge branch 'master' of https://gitee.com/v_cms/zyt
This commit is contained in:
@@ -1,23 +1,28 @@
|
||||
<template>
|
||||
<div class="call-record-panel">
|
||||
<el-alert
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="mb-4"
|
||||
title="视频通话与录制回放"
|
||||
>
|
||||
<!-- <p class="call-record-tip">
|
||||
下列为与本诊单关联的通话记录。医生接通并同步房间号后,服务端会尝试自动发起「云端混流录制」(需配置 CAM 密钥与云点播等);录制完成后由腾讯云回调写入回放地址。同时,管理端浏览器会进行「本地录制」,挂断后自动上传并合并到本条记录。
|
||||
</p>
|
||||
<p class="call-record-tip muted">
|
||||
回调 URL 示例:<code>{{ callbackHint }}</code>
|
||||
(若配置了 <code>trtc.recording_callback_token</code>,请在 URL 后附加
|
||||
<code>?token=你的密钥</code>)
|
||||
</p> -->
|
||||
</el-alert>
|
||||
<div class="call-record-toolbar">
|
||||
<upload
|
||||
ref="toolbarUploadRef"
|
||||
type="video"
|
||||
direct
|
||||
:multiple="false"
|
||||
:limit="1"
|
||||
:show-progress="true"
|
||||
@success="handleToolbarUploadSuccess"
|
||||
>
|
||||
<el-button type="primary">上传视频</el-button>
|
||||
</upload>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="rows" border stripe empty-text="暂无通话记录">
|
||||
<el-table v-loading="loading" :data="rows" border stripe>
|
||||
<template #empty>
|
||||
<div class="call-record-empty">
|
||||
<div class="call-record-empty__title">暂无通话记录</div>
|
||||
<div class="call-record-empty__desc">
|
||||
现在可以直接点击上方“上传视频”。系统会自动生成一条默认通话记录来承载回放。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table-column label="开始时间" width="170" prop="start_time_text" />
|
||||
<el-table-column label="结束时间" width="170" prop="end_time_text" />
|
||||
<el-table-column label="通话类型" width="100">
|
||||
@@ -25,7 +30,7 @@
|
||||
{{ row.call_type === 1 ? '语音' : '视频' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="房间号" width="140">
|
||||
<el-table-column label="房间号" width="180">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.room_id" class="text-primary">{{ row.room_id }}</span>
|
||||
<span v-else class="text-gray-400">—</span>
|
||||
@@ -42,12 +47,26 @@
|
||||
{{ row.recording_status_text || '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="录制回放" min-width="280">
|
||||
<el-table-column label="上传回放" width="180">
|
||||
<template #default="{ row }">
|
||||
<upload
|
||||
type="video"
|
||||
direct
|
||||
:multiple="false"
|
||||
:limit="1"
|
||||
:show-progress="true"
|
||||
@success="(response: any) => handleRowUploadSuccess(row, response)"
|
||||
>
|
||||
<el-button type="primary" plain size="small">上传视频</el-button>
|
||||
</upload>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="录制回放" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div v-if="(row.recording_urls_list || []).length" class="recording-list">
|
||||
<div
|
||||
v-for="(url, idx) in row.recording_urls_list"
|
||||
:key="idx"
|
||||
:key="`${row.id}-${idx}`"
|
||||
class="recording-item"
|
||||
>
|
||||
<video
|
||||
@@ -70,20 +89,30 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { getCallRecords } from '@/api/tcm'
|
||||
import Upload from '@/components/upload/index.vue'
|
||||
import feedback from '@/utils/feedback'
|
||||
import { attachLocalCallRecording, createManualCallRecord, getCallRecords } from '@/api/tcm'
|
||||
import { ref, useTemplateRef, watch } from 'vue'
|
||||
|
||||
type CallRecordRow = {
|
||||
id: number
|
||||
call_type: number
|
||||
room_id?: string
|
||||
status: number
|
||||
recording_status_text?: string
|
||||
recording_urls_list?: string[]
|
||||
start_time_text?: string
|
||||
end_time_text?: string
|
||||
duration_text?: string
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
diagnosisId: number
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref<any[]>([])
|
||||
|
||||
const callbackHint = computed(() => {
|
||||
const origin = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
return `${origin}/api/trtc/recording-notify`
|
||||
})
|
||||
const rows = ref<CallRecordRow[]>([])
|
||||
const toolbarUploadRef = useTemplateRef<InstanceType<typeof Upload>>('toolbarUploadRef')
|
||||
|
||||
const load = async () => {
|
||||
if (!props.diagnosisId) return
|
||||
@@ -101,7 +130,7 @@ const load = async () => {
|
||||
watch(
|
||||
() => props.diagnosisId,
|
||||
() => {
|
||||
load()
|
||||
void load()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
@@ -120,30 +149,93 @@ function statusText(status: number) {
|
||||
|
||||
function isPlayableVideo(url: string) {
|
||||
if (!url || typeof url !== 'string') return false
|
||||
return /\.(mp4|webm|ogg)(\?|$)/i.test(url)
|
||||
return /\.(mp4|webm|ogg|mov|mkv)(\?|$)/i.test(url)
|
||||
}
|
||||
|
||||
async function handleToolbarUploadSuccess(response: any) {
|
||||
const fileUrl = normalizeUploadUrl(response)
|
||||
if (!fileUrl) {
|
||||
feedback.msgError('上传成功但未返回视频地址')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const record = await createManualCallRecord({ diagnosis_id: props.diagnosisId })
|
||||
await attachLocalCallRecording({
|
||||
diagnosis_id: props.diagnosisId,
|
||||
call_record_id: Number(record?.id || 0),
|
||||
file_url: fileUrl
|
||||
} as any)
|
||||
feedback.msgSuccess('视频回放上传成功')
|
||||
await load()
|
||||
} catch (error: any) {
|
||||
feedback.msgError(error?.message || '写入回放失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRowUploadSuccess(row: CallRecordRow, response: any) {
|
||||
const fileUrl = normalizeUploadUrl(response)
|
||||
if (!fileUrl) {
|
||||
feedback.msgError('上传成功但未返回视频地址')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await attachLocalCallRecording({
|
||||
diagnosis_id: props.diagnosisId,
|
||||
call_record_id: Number(row.id || 0),
|
||||
file_url: fileUrl
|
||||
} as any)
|
||||
feedback.msgSuccess('视频回放上传成功')
|
||||
await load()
|
||||
} catch (error: any) {
|
||||
feedback.msgError(error?.message || '写入回放失败')
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUploadUrl(response: any): string {
|
||||
return String(response?.data?.uri || response?.data?.url || '').trim()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.call-record-panel {
|
||||
.call-record-tip {
|
||||
margin: 0 0 8px;
|
||||
line-height: 1.5;
|
||||
font-size: 13px;
|
||||
&.muted {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
code {
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.call-record-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
.call-record-empty {
|
||||
padding: 28px 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.call-record-empty__title {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.call-record-empty__desc {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.recording-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.recording-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.recording-video {
|
||||
max-width: 100%;
|
||||
max-height: 180px;
|
||||
|
||||
Reference in New Issue
Block a user