2025-07-01 16:02:21 +08:00

337 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<x-search-bar class="mb-8-2">
<template #default="{ gutter, colSpan }">
<a-form :label-col="{ style: { width: '40px' } }" :model="searchFormData" layout="inline">
<a-row :gutter="gutter">
<a-col v-bind="colSpan">
<a-form-item label="标题" name="title">
<a-input placeholder="请输入视频动态标题" v-model:value="searchFormData.title"></a-input>
</a-form-item>
</a-col>
<a-col v-bind="colSpan">
<a-form-item label="状态" name="status">
<a-select v-model:value="searchFormData.status" allowClear>
<a-select-option value="">全部</a-select-option>
<a-select-option value="enabled">启用</a-select-option>
<a-select-option value="disabled">停用</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col class="align-right" v-bind="colSpan">
<a-space>
<a-button @click="handleResetSearch">{{ $t('button.reset') }}</a-button>
<a-button ghost type="primary" @click="handleSearch">
{{ $t('button.search') }}
</a-button>
</a-space>
</a-col>
</a-row>
</a-form>
</template>
</x-search-bar>
<a-row :gutter="8" :wrap="false">
<a-col flex="auto">
<a-card type="flex">
<x-action-bar class="mb-8-2">
<a-button type="primary" @click="$refs.editDialogRef.handleCreate()">
<template #icon>
<plus-outlined></plus-outlined>
</template>
新增动态
</a-button>
</x-action-bar>
<a-table :columns="columns" :data-source="listData" bordered="true" :loading="loading"
:pagination="paginationState" :scroll="{ x: 1000 }" @change="onTableChange">
<template #bodyCell="{ column, record }">
<template v-if="'fullImg' === column.dataIndex">
<a-image :width="60" :src="config('http.apiBasic') + record.fullImg || $imageErr.imgErr" />
</template>
<template v-if="'smallImg' === column.dataIndex">
<a-image :width="60" :src="config('http.apiBasic') + record.smallImg || $imageErr.imgErr" />
</template>
<!-- <template v-if="'videoUrl' === column.dataIndex">-->
<!-- <video :width="60" :src="config('http.apiBasic') + record.videoUrl || $imageErr.imgErr"/>-->
<!-- </template>-->
<template v-if="'videoUrl' === column.dataIndex">
<span @click="handlePreview(record.videoUrl)" style="cursor: pointer; color: #1890ff;" >点击预览</span>
</template>
<template v-if="column.dataIndex === 'title'">
<a-tooltip :title="record.title">
<span>{{ record.title }}</span>
</a-tooltip>
</template>
<!-- <template v-if="column.dataIndex === 'subheading'">-->
<!-- <a-tooltip :title="record.subheading">-->
<!-- <span>{{ record.subheading }}</span>-->
<!-- </a-tooltip>-->
<!-- </template>-->
<template v-if="column.dataIndex === 'link'">
<a-tooltip :title="record.link">
<a :href="record.link" target="_blank" style="text-decoration: underline;">{{
record.link }}</a>
</a-tooltip>
</template>
<template v-if="'status' === column.dataIndex">
<a-tag v-if="record.status === 'enabled'" :color="'green'">启用</a-tag>
<a-tag v-if="record.status === 'disabled'" :color="'red'">停用</a-tag>
</template>
<template v-if="'month' === column.dataIndex">
<span>{{ record.month }}{{ record.title }}</span>
</template>
<template v-if="'action' === column.key">
<x-action-button @click="$refs.editDialogRef.handleEdit(record)">
<a-tooltip>
<template #title> {{ $t('pages.system.user.edit') }}</template>
<edit-outlined /> </a-tooltip></x-action-button>
<x-action-button @click="handleDelete(record)">
<a-tooltip>
<template #title>{{ $t('pages.system.delete') }}</template>
<delete-outlined style="color: #ff4d4f" /> </a-tooltip></x-action-button>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
<edit-dialog ref="editDialogRef" @ok="onOk"></edit-dialog>
<!-- 自定义视频预览弹窗 -->
<div v-if="dialogVisible" class="video-modal-overlay" @click.self="handleClose">
<div class="video-modal-content">
<video
v-if="currentVideoUrl"
ref="previewVideoRef"
:src="currentVideoUrl"
controls
autoplay
class="video-modal-player"
></video>
<button class="video-modal-close" @click="handleClose">×</button>
<div class="video-modal-footer">
<button @click="handleClose" class="video-modal-cancel-btn">取消预览</button>
</div>
</div>
</div>
</template>
<script setup>
import { message, Modal } from 'ant-design-vue'
import { ref } from 'vue'
import apis from '@/apis'
import { formatUtcDateTime } from '@/utils/util'
import { config } from '@/config'
import { statusUserTypeEnum } from '@/enums/system'
import { usePagination } from '@/hooks'
import EditDialog from './components/EditDialog.vue'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons-vue'
import { useI18n } from 'vue-i18n'
import { delVideoItem } from '@/apis/modules/videoCenter'
defineOptions({
name: 'videoCenter',
})
const { t } = useI18n() // 解构出t方法
const columns = [
{ title: '大图片', dataIndex: 'fullImg', width: 100, align: 'center' },
{ title: '小图片', dataIndex: 'smallImg', width: 100, align: 'center' },
{ title: '视频', dataIndex: 'videoUrl', width: 100, align: 'center' },
{ title: '标题', dataIndex: 'title', key: 'title' },
// { title: '简介', dataIndex: 'subheading', key: 'subheading' },
{ title: '状态', dataIndex: 'status', width: 120, align: 'center' },
{ title: '发布日期', dataIndex: 'pushAt', width: 120, align: 'center' },
{ title: '链接', dataIndex: 'link', align: 'center' },
// { title: '月份+事件', dataIndex: 'month', key: 'month' },
{ title: t('button.action'), key: 'action', fixed: 'right', width: 100, align: 'center' },
]
const { listData, loading, showLoading, hideLoading, paginationState, resetPagination, searchFormData } =
usePagination()
const editDialogRef = ref()
getPageList()
/**
* 获取用户列表
* @returns {Promise<void>}
*/
async function getPageList() {
try {
showLoading()
const { pageSize, current } = paginationState
const { success, data, total } = await apis.videoCenter
.getDataList({
pageSize,
current:current,
...searchFormData.value,
})
.catch(() => {
throw new Error()
})
hideLoading()
if (config('http.code.success') === success) {
listData.value = data
console.log(this.listData)
paginationState.total = total
}
} catch (error) {
hideLoading()
}
}
/**
* 删除
*/
function handleDelete({ id }) {
Modal.confirm({
title: t('pages.system.user.delTip'),
content: t('button.confirm'),
okText: t('button.confirm'),
onOk: () => {
return new Promise((resolve, reject) => {
;(async () => {
try {
const { success } = await apis.videoCenter.delVideoItem(id).catch(() => {
throw new Error()
})
if (config('http.code.success') === success) {
resolve()
message.success(t('component.message.success.delete'))
await getPageList()
}
} catch (error) {
reject()
}
})()
})
},
})
}
/**
* 分页
*/
function onTableChange({ current, pageSize }) {
paginationState.current = current
paginationState.pageSize = pageSize
getPageList()
}
/**
* 搜索
*/
function handleSearch() {
resetPagination()
getPageList()
}
/**
* 重置
*/
function handleResetSearch() {
searchFormData.value = {}
resetPagination()
getPageList()
}
/**
* 编辑完成
*/
async function onOk() {
await getPageList()
}
/**
* 视频预览
*/
const dialogVisible = ref(false)
const currentVideoUrl = ref('')
const previewVideoRef = ref(null)
// 预览处理
const handlePreview = (videoUrl) => {
if (!videoUrl) {
ElMessage.error('视频地址无效')
return
}
currentVideoUrl.value = config('http.apiBasic') + videoUrl
dialogVisible.value = true
}
// 关闭处理
const handleClose = () => {
if (previewVideoRef.value) {
previewVideoRef.value.pause()
previewVideoRef.value.currentTime = 0
}
currentVideoUrl.value = ''
dialogVisible.value = false
}
</script>
<style lang="less" scoped>
.video-modal-overlay {
position: fixed;
z-index: 2000;
left: 0; top: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.6);
display: flex;
align-items: center;
justify-content: center;
}
.video-modal-content {
position: relative;
background: #fff;
border-radius: 10px;
padding: 40px 40px 56px 24px;
box-shadow: 0 8px 40px rgba(0,0,0,0.25);
min-width: 320px;
max-width: 90vw;
max-height: 80vh;
display: flex;
flex-direction: column;
align-items: center;
}
.video-modal-player {
width: 60vw;
max-width: 800px;
max-height: 60vh;
border-radius: 8px;
background: #000;
}
.video-modal-close {
position: absolute;
right: 16px;
top: 12px;
font-size: 28px;
background: none;
border: none;
color: #888;
cursor: pointer;
transition: color 0.2s;
}
.video-modal-close:hover {
color: #f56c6c;
}
.video-modal-footer {
position: absolute;
left: 0; right: 0; bottom: 12px;
display: flex;
justify-content: center;
}
.video-modal-cancel-btn {
padding: 6px 24px;
border: none;
border-radius: 4px;
background: #f5f5f5;
color: #333;
font-size: 16px;
cursor: pointer;
transition: background 0.2s;
}
.video-modal-cancel-btn:hover {
background: #e6e6e6;
}
</style>