对接
This commit is contained in:
@@ -25,7 +25,7 @@
|
||||
<el-table-column label="状态" min-width="82"><template #default="{ row }"><el-tag :type="statusType(row.status)" effect="light">{{ statusText(row.status) }}</el-tag></template></el-table-column>
|
||||
<el-table-column v-if="!compactTable" label="观看人数" min-width="82" prop="viewerCount" align="right" />
|
||||
<el-table-column v-if="!compactTable" label="创建时间" min-width="150"><template #default="{ row }">{{ format(row.createdAt) }}</template></el-table-column>
|
||||
<el-table-column label="操作" width="260" fixed="right"><template #default="{ row }"><el-button link type="primary" :icon="VideoCamera" @click="openStudio(row)">开播</el-button><el-button link type="primary" :icon="Connection" @click="openDiagnostics(row)">测试</el-button><el-button link type="primary" :icon="Edit" @click="openEdit(row)">编辑</el-button><el-dropdown trigger="click" @command="handleRoomAction($event, row)"><el-button link type="primary" :icon="MoreFilled" title="更多操作" aria-label="更多操作" /><template #dropdown><el-dropdown-menu><el-dropdown-item command="share" :icon="Share">复制分享链接</el-dropdown-item><el-dropdown-item command="delete" :icon="Delete" divided class="room-delete-action">删除直播间</el-dropdown-item></el-dropdown-menu></template></el-dropdown></template></el-table-column>
|
||||
<el-table-column label="操作" width="260" fixed="right"><template #default="{ row }"><el-button link type="primary" :icon="VideoCamera" @click="openStudio(row)">开播</el-button><el-button link type="primary" :icon="Connection" @click="openDiagnostics(row)">测试</el-button><el-button link type="primary" :icon="Edit" @click="openEdit(row)">编辑</el-button><el-dropdown trigger="click" @command="handleRoomAction($event, row)"><el-button link type="primary" :icon="MoreFilled" title="更多操作" aria-label="更多操作" /><template #dropdown><el-dropdown-menu><el-dropdown-item command="studio" :icon="CopyDocument">复制主播链接</el-dropdown-item><el-dropdown-item command="viewer" :icon="Share">复制观众链接</el-dropdown-item><el-dropdown-item command="links" :icon="Connection">查看链接</el-dropdown-item><el-dropdown-item command="delete" :icon="Delete" divided class="room-delete-action">删除直播间</el-dropdown-item></el-dropdown-menu></template></el-dropdown></template></el-table-column>
|
||||
</el-table>
|
||||
<div class="kra-pagination"><el-pagination v-model:current-page="page" v-model:page-size="pageSize" :total="total" :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" @change="load" /></div>
|
||||
</div>
|
||||
@@ -46,18 +46,30 @@
|
||||
<el-form-item label="启用直播间"><el-switch v-model="form.enabled" /></el-form-item>
|
||||
</el-form>
|
||||
</el-drawer>
|
||||
<el-dialog v-model="linksDialog" title="直播间链接" width="min(640px, calc(100vw - 32px))">
|
||||
<p class="room-links-name">{{ linkedRoom?.name }} <span>#{{ linkedRoom?.id }}</span></p>
|
||||
<div v-for="item in linkItems" :key="item.key" class="room-link-row">
|
||||
<label :for="`room-link-${item.key}`">{{ item.label }}</label>
|
||||
<el-input :id="`room-link-${item.key}`" :model-value="roomLinks[item.key]" readonly @focus="$event.target.select()" />
|
||||
<el-tooltip :content="`复制${item.label}`"><el-button :icon="CopyDocument" :aria-label="`复制${item.label}`" @click="copyLink(linkedRoom, item.key)" /></el-tooltip>
|
||||
<el-tooltip :content="`打开${item.label}`"><el-button :icon="TopRight" :aria-label="`打开${item.label}`" @click="openLink(roomLinks[item.key])" /></el-tooltip>
|
||||
</div>
|
||||
<template #footer><el-button @click="linksDialog = false">关闭</el-button></template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { Connection, Delete, Edit, MoreFilled, Plus, Refresh, Search, Share, Sort, VideoCamera } from '@element-plus/icons-vue'
|
||||
import { Connection, CopyDocument, Delete, Edit, MoreFilled, Plus, Refresh, Search, Share, Sort, TopRight, VideoCamera } from '@element-plus/icons-vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getUrl } from '@/utils/image'
|
||||
import ImageUploader from '../components/ImageUploader.vue'
|
||||
import VideoUploader from '../components/VideoUploader.vue'
|
||||
import { createRoom, deleteRoom, getAssetUsers, getCategories, getRooms, saveRoomSort, updateRoom } from '../api/live'
|
||||
import { getRoomLinks } from '../utils/roomLinks'
|
||||
import { copyLiveText } from '../utils/clipboard'
|
||||
|
||||
defineOptions({ name: 'LiveRooms' })
|
||||
const query = reactive({ name: '', anchorName: '', categoryId: undefined, status: '', orderBy: 'sort_asc' })
|
||||
@@ -67,6 +79,8 @@ const compactTable = computed(() => width.value < 1180)
|
||||
const page = ref(1), pageSize = ref(10), total = ref(0), editingId = ref(0), editingStatus = ref('')
|
||||
const blank = () => ({ name: '', categoryId: undefined, coverUrl: '', intro: '', sort: 0, anchorUserId: undefined, streamUrl: '', replayUrl: '', enabled: true })
|
||||
const form = reactive(blank())
|
||||
const linksDialog = ref(false), linkedRoom = ref(null), roomLinks = ref({ viewer: '', studio: '' })
|
||||
const linkItems = [{ key: 'studio', label: '主播链接' }, { key: 'viewer', label: '观众链接' }]
|
||||
const rules = { name: [{ required: true, message: '请输入直播间名称', trigger: 'blur' }], categoryId: [{ required: true, message: '请选择分类', trigger: 'change' }], anchorUserId: [{ required: true, message: '请选择主播', trigger: 'change' }], coverUrl: [{ required: true, message: '请上传直播封面', trigger: 'change' }] }
|
||||
const statusText = (value) => ({ live: '直播中', offline: '未开播', disabled: '已禁用' }[value] || value)
|
||||
const statusType = (value) => ({ live: 'success', offline: 'info', disabled: 'danger' }[value] || 'info')
|
||||
@@ -77,14 +91,48 @@ const search = () => { page.value = 1; load() }
|
||||
const reset = () => { Object.assign(query, { name: '', anchorName: '', categoryId: undefined, status: '', orderBy: 'sort_asc' }); search() }
|
||||
const openCreate = () => { editingId.value = 0; editingStatus.value = ''; Object.assign(form, blank()); drawer.value = true }
|
||||
const openEdit = (row) => { editingId.value = row.id; editingStatus.value = row.status; Object.assign(form, { name: row.name, categoryId: row.categoryId, coverUrl: row.coverUrl, intro: row.intro, sort: row.sort, anchorUserId: row.anchorUserId, streamUrl: row.streamUrl, replayUrl: row.replayUrl, enabled: row.enabled }); drawer.value = true }
|
||||
const submit = async () => { await formRef.value?.validate(); saving.value = true; try { const res = editingId.value ? await updateRoom(editingId.value, form) : await createRoom(form); if (res.code === 0) { ElMessage.success('直播间已保存'); drawer.value = false; load() } } finally { saving.value = false } }
|
||||
const submit = async () => {
|
||||
await formRef.value?.validate()
|
||||
saving.value = true
|
||||
try {
|
||||
const res = editingId.value ? await updateRoom(editingId.value, form) : await createRoom(form)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('直播间已保存')
|
||||
drawer.value = false
|
||||
showLinks(res.data || { ...form, id: editingId.value })
|
||||
load()
|
||||
}
|
||||
} finally { saving.value = false }
|
||||
}
|
||||
const removeRow = async (row) => { await ElMessageBox.confirm(`确认删除直播间“${row.name}”吗?`, '删除直播间', { type: 'warning' }); const res = await deleteRoom(row.id); if (res.code === 0) { ElMessage.success('已删除'); load() } }
|
||||
const persistSort = async () => { const values = Object.fromEntries(rows.value.map((item) => [item.id, item.sort])); const res = await saveRoomSort(values); if (res.code === 0) { ElMessage.success('排序已保存'); load() } }
|
||||
const shareUrl = (row, studio = false) => `${window.location.origin}${window.location.pathname}#/live/${studio ? 'studio' : 'room'}/${row.id}`
|
||||
const copyLink = async (row) => { await navigator.clipboard.writeText(shareUrl(row)); ElMessage.success('观看链接已复制') }
|
||||
const openStudio = (row) => window.open(shareUrl(row, true), '_blank')
|
||||
const showLinks = (row) => {
|
||||
try {
|
||||
roomLinks.value = getRoomLinks(row.id)
|
||||
linkedRoom.value = row
|
||||
linksDialog.value = true
|
||||
} catch { ElMessage.error('无法生成直播间链接,请检查 PC 站点地址配置') }
|
||||
}
|
||||
const copyLink = async (row, kind) => {
|
||||
try {
|
||||
await copyLiveText(getRoomLinks(row.id)[kind])
|
||||
ElMessage.success(kind === 'studio' ? '主播链接已复制' : '观众链接已复制')
|
||||
} catch {
|
||||
showLinks(row)
|
||||
ElMessage.warning('复制失败,请在链接窗口中选中地址复制')
|
||||
}
|
||||
}
|
||||
const openLink = (url) => window.open(url, '_blank', 'noopener,noreferrer')
|
||||
const openStudio = (row) => {
|
||||
try { openLink(getRoomLinks(row.id).studio) }
|
||||
catch { ElMessage.error('无法生成主播链接,请检查 PC 站点地址配置') }
|
||||
}
|
||||
const openDiagnostics = (row) => window.open(`${window.location.origin}${window.location.pathname}#/live/diagnostics/${row.id}`, '_blank', 'noopener')
|
||||
const handleRoomAction = (command, row) => command === 'share' ? copyLink(row) : removeRow(row)
|
||||
const handleRoomAction = (command, row) => {
|
||||
if (command === 'studio' || command === 'viewer') return copyLink(row, command)
|
||||
if (command === 'links') return showLinks(row)
|
||||
if (command === 'delete') return removeRow(row)
|
||||
}
|
||||
onMounted(() => { loadOptions(); load() })
|
||||
</script>
|
||||
|
||||
@@ -94,4 +142,7 @@ onMounted(() => { loadOptions(); load() })
|
||||
.anchor-cell { display: flex; align-items: center; gap: 8px; min-width: 0; }.anchor-cell span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.image-slot { display: grid; width: 100%; height: 100%; place-items: center; color: var(--el-text-color-placeholder); font-size: 11px; }
|
||||
.room-delete-action { color: var(--el-color-danger); }
|
||||
.room-links-name { margin: 0 0 18px; overflow-wrap: anywhere; }.room-links-name span { margin-left: 6px; color: var(--el-text-color-secondary); }
|
||||
.room-link-row { display: grid; grid-template-columns: 64px minmax(0, 1fr) 32px 32px; gap: 8px; align-items: center; margin: 12px 0; }.room-link-row .el-button { width: 32px; margin: 0; }.room-link-row label { font-size: 12px; }
|
||||
@media (max-width: 480px) { .room-link-row { grid-template-columns: minmax(0, 1fr) 32px 32px; }.room-link-row label { grid-column: 1 / -1; } }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user