You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1323 lines
38 KiB

<template>
<div class="app-container">
<!-- <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">-->
<!-- <el-form-item>-->
<!-- <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>-->
<!-- <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>-->
<!-- </el-form-item>-->
<!-- </el-form>-->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['vet:training:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-s-promotion"
size="mini"
:disabled="single"
@click="handleSubmitAudit"
v-hasPermi="['vet:training:submit']"
>提交审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-finished"
size="mini"
:disabled="single"
@click="handleAuditDialog"
v-hasPermi="['vet:training:audit']"
>审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-check"
size="mini"
:disabled="single"
@click="handlePublish"
v-hasPermi="['vet:training:publish']"
>上架视频</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['vet:training:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
:disabled="single"
@click="handleOffline"
v-hasPermi="['vet:training:offline']"
>下架视频</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['vet:training:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="trainingList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="发布者" align="center" prop="publisherName" width="120">
<template slot-scope="scope">
<span>{{ scope.row.publisherName || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="视频标题" align="center" prop="title" show-overflow-tooltip/>
<el-table-column label="视频描述" align="center" prop="description" show-overflow-tooltip/>
<el-table-column label="视频地址" align="center" prop="videoUrl" width="180" />
<el-table-column label="封面图片" align="center" prop="coverImage" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.coverImage" :width="50" :height="50"/>
</template>
</el-table-column>
<el-table-column label="分类" align="center" prop="category">
<template slot-scope="scope">
<dict-tag :options="dict.type.video_category" :value="scope.row.category" />
</template>
</el-table-column>
<el-table-column label="观看次数" align="center" prop="viewCount" />
<el-table-column label="发布时间" align="center" prop="publishTime" min-width="200" show-overflow-tooltip />
<!-- 状态列 -->
<el-table-column label="上架状态" align="center" prop="status" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.status)" size="small">
{{ getStatusText(scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<!-- 审核状态列 -->
<el-table-column label="审核状态" align="center" prop="auditStatus" width="100">
<template slot-scope="scope">
<el-tag :type="getAuditStatusType(scope.row.auditStatus)" size="small">
{{ getAuditStatusText(scope.row.auditStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="审核意见" align="center" prop="auditOpinion" width="150" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.auditOpinion">{{ truncateText(scope.row.auditOpinion, 15) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="300">
<template slot-scope="scope">
<!-- 修改按钮 -->
<el-button
size="mini"
type="text"
icon="el-icon-edit"
style="color: #42B983"
@click="handleUpdate(scope.row)"
v-hasPermi="['vet:training:edit']"
v-if="scope.row.status === '0' && (scope.row.auditStatus === '3' || scope.row.auditStatus === '0')"
class="info-btn alter-btn"
>修改</el-button>
<!-- 提交审核按钮:审核拒绝或无需审核状态 -->
<el-button
size="mini"
type="text"
icon="el-icon-s-promotion"
style="color: #dab708"
@click="handleSubmitAudit(scope.row.id)"
v-hasPermi="['vet:training:submit']"
v-if="scope.row.status === '0' && scope.row.auditStatus === '0'"
class="info-btn submit-btn"
>提交审核</el-button>
<!-- 审核按钮:待审核状态(管理员) -->
<el-button
size="mini"
type="text"
icon="el-icon-finished"
@click="handleAuditDialog(scope.row.id)"
style="color: #072eed"
v-hasPermi="['vet:training:audit']"
v-if="scope.row.auditStatus === '0' && isAdmin"
class="info-btn audit-btn"
>审核</el-button>
<!-- 上架按钮:审核通过且未上架 -->
<el-button
size="mini"
type="text"
icon="el-icon-top"
@click="handlePublish(scope.row.id)"
style="color: #f46a0c"
v-hasPermi="['vet:training:publish']"
v-if="scope.row.auditStatus === '2' && scope.row.status === '0'"
class="info-btn publish-btn"
>上架</el-button>
<!-- 下架按钮:已上架状态 -->
<el-button
size="mini"
type="text"
icon="el-icon-bottom"
style="color: #636361"
@click="handleOffline(scope.row.id)"
v-hasPermi="['vet:training:offline']"
v-if="scope.row.status === '1'"
class="info-btn offline-btn"
>下架</el-button>
<!-- 取消审核按钮 -->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-close"-->
<!-- style="color: #5607b3"-->
<!-- @click="handleCancelAudit(scope.row.id)"-->
<!-- v-hasPermi="['vet:training:edit']"-->
<!-- v-if="scope.row.auditStatus === '1' && !isAdmin"-->
<!-- class="info-btn cancel-btn"-->
<!-- >取消审核</el-button>-->
<el-button
size="mini"
type="text"
icon="el-icon-close"
style="color: #5607b3"
@click="handleCancelAudit(scope.row.id)"
v-hasPermi="['vet:training:edit']"
v-if="scope.row.auditStatus === '1'"
class="info-btn cancel-btn"
>取消审核</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-s-promotion"
style="color: #c108af"
@click="handleResubmitAudit(scope.row)"
class="info-btn resubmit-btn"
v-hasPermi="['vet:training:submit']"
v-if="scope.row.status === '0' && scope.row.auditStatus === '3'"
>
重新提交
</el-button>
<!-- 删除按钮 -->
<el-button
size="mini"
type="text"
icon="el-icon-delete"
style="color: #f56c6c"
@click="handleDelete(scope.row)"
v-hasPermi="['vet:training:remove']"
class="info-btn delete-btn"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagestyle">
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<!-- 添加或修改兽医培训视频对话框 -->
<el-dialog :title="title" :visible.sync="open" width="80%" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="视频标题" prop="title">
<el-input v-model="form.title" placeholder="请输入视频标题" />
</el-form-item>
<el-form-item label="视频描述" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="发布时间" prop="publishTime">
<el-date-picker
v-model="form.publishTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择时间"
style="width: 100%;"
/>
</el-form-item>
<el-form-item label="视频" required>
<div style="display: flex; flex-direction: column; gap: 12px;">
<!-- 简单的文件选择方式 -->
<div v-if="!form.videoUrl">
<input
type="file"
ref="videoInput"
accept=".mp4,.avi,.mov,.wmv,.flv,.mkv"
@change="handleFileSelect"
style="display: none"
/>
<el-button
type="primary"
@click="$refs.videoInput.click()"
:loading="uploading"
>
<el-icon><Upload /></el-icon>
{{ uploading ? '上传中...' : '点击上传视频' }}
</el-button>
<div class="el-upload__tip" style="margin-top: 7px; color: #909399; font-size: 12px;">
支持 MP4、AVI、MOV、WMV、FLV、MKV 格式,大小不超过200MB
</div>
</div>
<!-- 预览区域(上传后显示) -->
<div v-if="form.videoUrl" class="video-preview">
<div style="display: flex; align-items: flex-start; gap: 10px;">
<video
:src="getVideoUrl(form.videoUrl)"
controls
style="max-width: 300px; max-height: 150px; border-radius: 4px; background: #000;"
></video>
<div style="display: flex; flex-direction: column; gap: 5px;">
<el-button
type="danger"
size="small"
@click="removeVideo"
circle
>
<el-icon><Delete /></el-icon>
</el-button>
<el-button
type="primary"
size="small"
@click="previewVideo"
>
预览
</el-button>
</div>
</div>
</div>
<!-- 地址输入框(可编辑) -->
<el-form-item prop="videoUrl" style="margin-bottom: 0;">
<el-input
v-model="form.videoUrl"
type="textarea"
:rows="2"
placeholder="请上传视频或直接输入视频地址"
clearable
/>
</el-form-item>
</div>
</el-form-item>
<el-form-item label="封面图片" prop="coverImage">
<image-upload v-model="form.coverImage"/>
</el-form-item>
<el-form-item label="分类" prop="category">
<el-select v-model="form.category" placeholder="请选择分类" clearable>
<el-option v-for="dict in dict.type.video_category" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
<!-- ================= 审核对话框 ================= -->
<el-dialog
title="视频审核"
:visible.sync="auditOpen"
width="80%"
append-to-body
>
<el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="80px">
<el-form-item label="审核结果" prop="auditStatus" required>
<el-radio-group v-model="auditForm.auditStatus">
<el-radio
v-for="item in auditStatusOptions.filter(item => ['2', '3'].includes(item.dictValue))"
:key="item.dictValue"
:label="item.dictValue"
>
{{ item.dictLabel }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见" prop="auditOpinion">
<el-input
v-model="auditForm.auditOpinion"
type="textarea"
:rows="4"
placeholder="请输入审核意见(审核拒绝时必须填写)"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitAudit">确 定</el-button>
<el-button @click="cancelAudit">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listTraining, getTraining, delTraining, addTraining, updateTraining, uploadVideo, resubmitAudit} from "@/api/vet/training"
import {
submitForAudit,
cancelAudit,
auditVideo,
publishVideo,
offlineVideo,
batchSubmitAudit,
batchAuditVideo,
batchPublishVideo,
batchOfflineVideo
} from "@/api/vet/training"
import { getToken } from "@/utils/auth"
export default {
name: "Training",
dicts: ['video_category'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 兽医培训视频表格数据
trainingList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 上传相关变量
uploading: false,
// 是否为管理员
isAdmin: false,
// 字典数据
statusOptions: [
{ dictValue: '0', dictLabel: '未上架', listClass: 'info' },
{ dictValue: '1', dictLabel: '已上架', listClass: 'success' }
],
auditStatusOptions: [
{ dictValue: '0', dictLabel: '待审核', listClass: 'info' },
{ dictValue: '1', dictLabel: '审核中', listClass: 'warning' },
{ dictValue: '2', dictLabel: '审核通过', listClass: 'success' },
{ dictValue: '3', dictLabel: '审核驳回', listClass: 'danger' }
],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
title: null,
description: null,
videoUrl: null,
coverImage: null,
category: null,
tags: null,
duration: null,
fileSize: null,
viewCount: null,
status: null,
auditStatus: null,
auditOpinion: null,
auditUserId: null,
auditTime: null
},
// 表单参数
form: {
id: undefined,
userId: undefined,
title: "",
description: "",
videoUrl: "",
coverImage: "",
category: "",
tags: "",
duration: undefined,
fileSize: undefined,
viewCount: undefined,
status: "0",
createTime: undefined,
updateTime: undefined,
delFlag: undefined,
auditStatus: "0",
auditOpinion: "",
auditUserId: undefined,
auditTime: undefined
},
// 表单校验
rules: {
title: [
{ required: true, message: "视频标题不能为空", trigger: "blur" }
],
videoUrl: [
{ required: true, message: "视频地址不能为空", trigger: "blur" }
],
category: [
{ required: true, message: "分类不能为空", trigger: "blur" }
]
},
// ========== 审核相关 ==========
auditOpen: false,
auditForm: {
id: null,
auditStatus: '1',
auditOpinion: ''
},
auditRules: {
auditOpinion: [
{
validator: (rule, value, callback) => {
if (this.auditForm.auditStatus === '3' && (!value || value.trim() === '')) {
callback(new Error('审核拒绝时必须填写审核意见'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
}
},
created() {
this.getList()
this.checkAdminRole()
},
methods: {
/** 检查是否是管理员 */
checkAdminRole() {
const userInfo = this.$store.getters.userInfo || {}
this.isAdmin = userInfo.roles && userInfo.roles.includes('admin')
},
/** 查询兽医培训视频列表 */
getList() {
this.loading = true
listTraining(this.queryParams).then(response => {
this.trainingList = response.rows
this.total = response.total
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** ============ 字典工具方法 ============ */
/** 通过字典值获取标签 */
getDictLabel(options, value) {
if (!options || !value) return ''
const item = options.find(item => item.dictValue === String(value))
return item ? item.dictLabel : String(value)
},
/** 通过字典值获取样式类型 */
getDictType(options, value) {
if (!options || !value) return 'info'
const item = options.find(item => item.dictValue === String(value))
const type = item ? (item.listClass || 'info') : 'info'
const tagTypeMap = {
'info': 'info',
'warning': 'warning',
'success': 'success',
'danger': 'danger',
// 'primary': 'primary',
}
return tagTypeMap[type] || 'info'
},
/** 获取上架状态文本 */
getStatusText(status) {
return this.getDictLabel(this.statusOptions, status)
},
/** 获取上架状态类型 */
getStatusType(status) {
return this.getDictType(this.statusOptions, status)
},
/** 获取审核状态文本 */
getAuditStatusText(auditStatus) {
return this.getDictLabel(this.auditStatusOptions, auditStatus)
},
/** 获取审核状态类型 */
getAuditStatusType(auditStatus) {
return this.getDictType(this.auditStatusOptions, auditStatus)
},
/** 截断文本 */
truncateText(text, maxLength) {
if (!text) return ''
if (text.length <= maxLength) return text
return text.substring(0, maxLength) + '...'
},
/** ============ 视频URL处理相关方法 ============ */
// 截短URL显示
truncateUrl(url) {
if (!url) return ''
if (url.includes('/')) {
const parts = url.split('/')
const filename = parts[parts.length - 1]
if (filename.length > 15) {
return filename.substring(0, 12) + '...'
}
return filename
}
if (url.length > 15) {
return url.substring(0, 12) + '...'
}
return url
},
// 表格中的视频预览
previewTableVideo(url) {
if (!url) {
this.$modal.msgWarning("视频地址为空")
return
}
const fullUrl = this.getVideoUrl(url)
const htmlContent = `
<div style="text-align: center; padding: 20px;">
<iframe
src="${fullUrl}"
style="width: 100%; height: 400px; border: none; border-radius: 4px; background: #000;"
frameborder="0"
allowfullscreen
allow="autoplay; encrypted-media"
>
</iframe>
<div style="margin-top: 10px; color: #666; font-size: 12px; word-break: break-all;">
视频地址: ${url}
</div>
</div>
`
this.$modal.alert({
title: '视频预览',
message: htmlContent,
dangerouslyUseHTMLString: true,
customClass: 'video-preview-modal',
width: '600px',
showConfirmButton: false,
showCancelButton: true,
cancelButtonText: '关闭'
})
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
id: undefined,
userId: undefined,
title: "",
description: "",
videoUrl: "",
coverImage: "",
category: "",
tags: "",
duration: undefined,
fileSize: undefined,
viewCount: undefined,
status: "0",
createTime: undefined,
updateTime: undefined,
delFlag: undefined,
auditStatus: "0",
auditOpinion: "",
auditUserId: undefined,
auditTime: undefined
}
if (this.$refs.form) {
this.$nextTick(() => {
this.$refs.form.clearValidate()
})
}
this.uploading = false
},
/** ============ 视频上传相关方法 ============ */
// 处理文件选择
handleFileSelect(event) {
const file = event.target.files[0]
if (file) {
this.uploadFile(file)
}
event.target.value = ''
},
// 上传文件
async uploadFile(file) {
const fileName = file.name.toLowerCase()
const validExtensions = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv']
const hasValidExtension = validExtensions.some(ext => fileName.endsWith(ext))
if (!hasValidExtension) {
this.$modal.msgError('请上传 MP4、AVI、MOV、WMV、FLV、MKV 格式的视频文件')
return false
}
const maxSize = 200 * 1024 * 1024
if (file.size > maxSize) {
this.$modal.msgError('视频大小不能超过 200MB')
return false
}
this.uploading = true
try {
const formData = new FormData()
formData.append('file', file)
const response = await uploadVideo(formData)
if (response.code === 200) {
this.form.videoUrl = response.data
this.$modal.msgSuccess("视频上传成功")
return true
} else {
this.$modal.msgError(response.msg || '上传失败')
return false
}
} catch (error) {
console.error('上传失败:', error)
this.$modal.msgError('上传失败,请重试')
return false
} finally {
this.uploading = false
}
},
// 移除视频
removeVideo() {
this.form.videoUrl = ''
this.$modal.msgSuccess("已移除视频")
},
// 预览视频
previewVideo() {
if (this.form.videoUrl) {
let url = this.form.videoUrl
if (url && url.startsWith('/')) {
url = process.env.VUE_APP_BASE_API + url
}
if (url) {
window.open(url, '_blank')
}
} else {
this.$modal.msgWarning("请先上传或输入视频地址")
}
},
// 获取完整的视频URL
getVideoUrl(url) {
if (!url) return ''
if (url.startsWith('/')) {
return process.env.VUE_APP_BASE_API + url
}
return url
},
/** ============ 审核相关方法 ============ */
/** 提交审核(批量或单个) */
async handleSubmitAudit(videoId) {
let id
if (videoId && typeof videoId !== 'object' && videoId !== '') {
id = videoId
} else if (this.ids.length === 1) {
id = this.ids[0]
} else if (typeof videoId === 'object') {
if (this.ids.length === 1) {
id = this.ids[0]
} else {
this.$message.warning('请先选择一个视频')
return
}
} else {
this.$message.warning('请选择需要提交审核的视频')
return
}
if (!id) {
this.$message.warning('请选择需要提交审核的视频')
return
}
try {
await this.$confirm('是否确认提交审核?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await submitForAudit(id)
this.$message.success('提交审核成功')
this.getList()
} catch (error) {
if (error !== 'cancel') {
console.error('提交审核失败:', error)
this.$message.error(error.message || '提交审核失败')
}
}
},
/** 取消审核 */
async handleCancelAudit(videoId) {
const id = videoId || this.ids[0]
if (!id) {
this.$message.warning('请选择需要取消审核的视频')
return
}
try {
await this.$confirm('确定要取消审核吗?取消后可以重新提交审核', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await cancelAudit(id)
this.$message.success('取消审核成功')
this.getList()
} catch (error) {
if (error !== 'cancel') {
console.error('取消审核失败:', error)
this.$message.error(error.message || '取消审核失败')
}
}
},
/** 审核(批量或单个) */
handleAuditDialog(videoId) {
let id
if (videoId && typeof videoId !== 'object' && videoId !== '') {
id = videoId
} else if (this.ids.length === 1) {
id = this.ids[0]
} else if (typeof videoId === 'object') {
if (this.ids.length === 1) {
id = this.ids[0]
} else {
this.$message.warning('请先选择一个视频')
return
}
} else {
this.$message.warning('请选择需要审核的视频')
return
}
if (!id) {
this.$message.warning('请选择需要审核的视频')
return
}
this.auditForm.id = id
this.auditForm.auditStatus = '1'
this.auditForm.auditOpinion = ''
this.auditOpen = true
},
/** 取消审核对话框 */
cancelAudit() {
this.auditOpen = false
this.resetAuditForm()
},
/** 重置审核表单 */
resetAuditForm() {
this.auditForm = {
id: null,
auditStatus: '1',
auditOpinion: ''
}
if (this.$refs.auditFormRef) {
this.$refs.auditFormRef.resetFields()
}
},
/** 提交审核(管理员审核) */
async submitAudit() {
try {
const valid = await this.$refs.auditFormRef.validate()
if (!valid) return
const message = this.auditForm.auditStatus === '2'
? '是否确认审核通过?'
: '是否确认审核拒绝?'
await this.$confirm(message, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
// 方式1:传递对象参数
const response = await auditVideo({
id: this.auditForm.id,
auditStatus: this.auditForm.auditStatus,
auditOpinion: this.auditForm.auditOpinion || ''
})
// 或者方式2:如果不想改API函数,可以直接调用request
// const response = await request({
// url: `/vet/training/audit/${this.auditForm.id}`,
// method: 'post',
// data: {
// auditStatus: this.auditForm.auditStatus,
// auditOpinion: this.auditForm.auditOpinion || ''
// }
// })
if (response.code === 200) {
this.$message.success('审核成功')
this.auditOpen = false
this.getList()
this.resetAuditForm()
} else {
this.$message.error(response.msg || '审核失败')
}
} catch (error) {
if (error === 'cancel') return
console.error('审核错误:', error)
this.$message.error('审核失败,请检查参数')
}
},
/** ============ 上架/下架相关方法 ============ */
/** 上架视频(批量或单个) */
async handlePublish(videoId) {
let id
if (videoId && typeof videoId !== 'object' && videoId !== '') {
id = videoId
} else if (this.ids.length === 1) {
id = this.ids[0]
} else if (typeof videoId === 'object') {
if (this.ids.length === 1) {
id = this.ids[0]
} else {
this.$message.warning('请先选择一个视频')
return
}
} else {
this.$message.warning('请选择需要上架的视频')
return
}
try {
await this.$confirm('是否确认上架视频?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await publishVideo(id)
this.$message.success('上架成功')
this.getList()
} catch (error) {
if (error !== 'cancel') {
console.error('上架失败:', error)
this.$message.error(error.message || '上架失败')
}
}
},
/** 下架视频(批量或单个) */
async handleOffline(videoId) {
let id
if (videoId && typeof videoId !== 'object' && videoId !== '') {
id = videoId
} else if (this.ids.length === 1) {
id = this.ids[0]
} else if (typeof videoId === 'object') {
if (this.ids.length === 1) {
id = this.ids[0]
} else {
this.$message.warning('请先选择一个视频')
return
}
} else {
this.$message.warning('请选择需要下架的视频')
return
}
try {
await this.$confirm('是否确认下架视频?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await offlineVideo(id)
this.$message.success('下架成功')
this.getList()
} catch (error) {
if (error !== 'cancel') {
console.error('下架失败:', error)
this.$message.error(error.message || '下架失败')
}
}
},
// handleResubmitAudit重新提交审核
handleResubmitAudit(row) {
const id = row.id || this.ids[0];
if (!id) {
this.$modal.msgWarning('请选择需要重新提交审核的视频');
return;
}
this.$confirm('是否确认重新提交审核?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const response = await resubmitAudit(id);
if (response.code === 200) {
this.$modal.msgSuccess("重新提交审核成功");
this.getList();
} else {
this.$modal.msgError(response.msg || "重新提交审核失败");
}
} catch (error) {
console.error('重新提交审核失败:', error);
this.$modal.msgError('重新提交审核失败:' + (error.message || '网络异常'));
}
}).catch(() => {
});
},
/** ============ 其他方法 ============ */
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加兽医培训视频"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getTraining(id).then(response => {
const data = response.data || {}
this.form = {
id: data.id || undefined,
userId: data.userId || undefined,
title: data.title || "",
description: data.description || "",
videoUrl: data.videoUrl || "",
coverImage: data.coverImage || "",
category: data.category || "",
tags: data.tags || "",
duration: data.duration || undefined,
fileSize: data.fileSize || undefined,
viewCount: data.viewCount || undefined,
status: data.status || "0",
createTime: data.createTime || undefined,
updateTime: data.updateTime || undefined,
delFlag: data.delFlag || undefined,
auditStatus: data.auditStatus || "0",
auditOpinion: data.auditOpinion || "",
auditUserId: data.auditUserId || undefined,
auditTime: data.auditTime || undefined
}
this.open = true
this.title = "修改兽医培训视频"
}).catch(() => {
this.$modal.msgError("获取数据失败")
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
const submitData = {
...this.form,
title: (this.form.title || "").trim(),
category: (this.form.category || "").trim(),
videoUrl: this.form.videoUrl || "",
description: this.form.description || "",
status: this.form.status || "0",
auditStatus: this.form.auditStatus || "0"
}
if (submitData.id) {
updateTraining(submitData).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
}).catch((error) => {
console.error('修改失败:', error)
this.$modal.msgError("修改失败: " + (error.message || "未知错误"))
})
} else {
addTraining(submitData).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
}).catch((error) => {
console.error('新增失败:', error)
this.$modal.msgError("新增失败: " + (error.message || "未知错误"))
})
}
} else {
return false
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除兽医培训视频编号为"' + ids + '"的数据项?').then(() => {
return delTraining(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('vet/training/export', {
...this.queryParams
}, `training_${new Date().getTime()}.xlsx`)
}
}
}
</script>
<style scoped>
::v-deep .pagestyle .el-input{
width: auto !important;
}
</style>
<style lang="scss" scoped>
.video-url-cell {
display: flex;
align-items: center;
justify-content: center;
}
.url-text {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 操作按钮样式
.info-btn {
padding: 6px 10px;
border-radius: 4px;
margin: 0 10px;
transition: all 0.3s ease;
}
.view-btn:hover {
background-color: rgb(216, 238, 248);
transform: translateY(-1px);
}
.alter-btn:hover{
background-color: rgb(230, 255, 238);
transform: translateY(-1px);
}
.delete-btn:hover {
background-color: rgba(245, 108, 108, 0.1);
transform: translateY(-1px);
}
.submit-btn:hover {
background-color: rgb(253, 250, 232);
transform: translateY(-1px);
}
.publish-btn:hover {
background-color: rgb(253, 238, 228);
transform: translateY(-1px);
}
.offline-btn:hover {
background-color: rgb(237, 237, 235);
transform: translateY(-1px);
}
.cancel-btn:hover {
background-color: rgb(244, 237, 251);
transform: translateY(-1px);
}
.audit-btn:hover {
background-color: rgb(215, 223, 246);
transform: translateY(-1px);
}
.resubmit-btn:hover {
background-color: rgb(248, 232, 250);
transform: translateY(-1px);
}
// 新增/修改的弹窗
::v-deep .el-dialog {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
animation: dialogFadeIn 0.3s ease;
}
::v-deep .el-dialog__header {
background: linear-gradient(135deg, #42b983 0%, #83df92 100%);
padding: 18px 24px;
border-bottom: none;
position: relative;
}
::v-deep .el-dialog__title {
font-size: 17px;
font-weight: 600;
color: white;
letter-spacing: 0.5px;
}
::v-deep .el-dialog__headerbtn:hover .el-dialog__close {
color: #ffd04b;
transform: rotate(90deg);
}
::v-deep .el-dialog__body {
padding: 28px 24px 20px;
background-color: #f8fafc;
max-height: 70vh;
overflow-y: auto;
}
::v-deep .el-form-item {
margin-bottom: 20px;
transition: all 0.3s;
}
::v-deep .el-form-item__label {
font-weight: 500;
color: #2d3748;
font-size: 14px;
transition: color 0.3s;
}
::v-deep .el-input,
::v-deep .el-textarea,
::v-deep .el-select {
width: 100%;
}
::v-deep .el-input__inner,
::v-deep .el-textarea__inner {
border-radius: 8px;
border: 1px solid #dcdfe6;
font-size: 14px;
transition: all 0.3s;
background-color: #fcfdfe;
}
::v-deep .el-input__inner:focus,
::v-deep .el-textarea__inner:focus {
border-color: #42B983;
box-shadow: 0 0 0 3px rgb(230, 255, 238);
background-color: white;
}
::v-deep .el-select .el-input__inner {
padding-right: 35px;
}
::v-deep .el-dialog__footer {
padding: 20px 24px;
background-color: #f8fafc;
border-top: 1px solid #eef2f7;
border-radius: 0 0 12px 12px;
}
</style>