PixelAI-admin/src/views/video/component/upload.vue

342 lines
9.8 KiB
Vue
Raw Normal View History

<template>
<div class="layout-pd">
<el-card v-loading="state.loading" shadow="hover" header="视频详情">
<el-form ref="videoFormRef" :model="state.data" :rules="state.rules" size="large" label-width="100px" class="mt20 mb20">
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="模块名称:" prop="moduleName">
<el-select size="default" @change="handleChangeModule" v-model="state.data.moduleName" placeholder="请选择模块" clearable>
<el-option
:data-op="item.id"
v-for="(item, index) in moduleList"
:key="index"
:label="item.moduleName"
:value="item.moduleName"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="标签名称:" prop="labelName">
<el-select size="default" v-model="state.data.labelName" placeholder="请选择标签" clearable class="ml10">
<el-option v-for="(item, index) in labelList" :key="index" :label="item.name" :value="item.name"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="视频名称:" prop="name">
<el-input v-model="state.data.name" placeholder="请输入标题" clearable></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="视频:" prop="path" class="coverImg">
<!-- 上传文件 -->
<el-upload
ref="uploadRef"
class="h100 personal-user-left-upload"
action="#"
list-type="picture-card"
:file-list="state.fileArray"
:auto-upload="false"
:limit="1"
:class="{ hide: state.coverHide }"
@change="handleUploadChange"
accept="video/*"
>
<template #default>
<el-icon><Plus /></el-icon>
</template>
<template #file="{ file }">
<div>
<!-- class="el-upload-list__item-thumbnail" -->
<video style="width: 100%; height: auto; border-radius: 4px" :src="file.url"></video>
<span class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<el-icon><zoom-in /></el-icon>
</span>
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<el-row class="flex mt15">
<div class="flex-margin" style="width: 100%; display: flex; justify-content: flex-end">
<el-button size="larger" type="info" @click="cancel">
<SvgIcon name="ele-RefreshLeft" />
取消
</el-button>
<el-button size="larger" type="primary" @click="onSubmitForm">
<SvgIcon name="iconfont icon-shuxing" />
保存
</el-button>
</div>
</el-row>
</el-form>
</el-card>
<!-- 视频预览弹窗 -->
<el-dialog v-model="dialogVisible">
<video style="width: 100%; height: auto; border-radius: 4px" :src="dialogVideoUrl" controls></video>
</el-dialog>
</div>
</template>
2024-12-05 14:58:48 +08:00
<script setup lang="ts" name="editVideo">
import { onMounted, reactive, ref } from 'vue';
import { articleApi } from '/@/api/article';
import { videoApi } from '/@/api/video';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage, UploadFile, UploadFiles } from 'element-plus';
import { Delete, Plus, ZoomIn } from '@element-plus/icons-vue';
import mittBus from '/@/utils/mitt';
// 视频基本路径
const viteUrl = import.meta.env.VITE_API_URL;
// 引入 api 请求接口
const artApi = articleApi();
const vidApi = videoApi();
// 模块列表
const moduleList = ref<{ id: number; moduleName: string }[]>([]);
// 标签列表
const labelList = ref<{ name: string }[]>([]);
// 获取视频id
const route = useRoute();
const router = useRouter();
const moduleName: any = route.query.moduleName;
const labelName: any = route.query.labelName;
const id: any = route.query.id;
const state = reactive({
data: {
labelName: '',
moduleName: '',
name: '',
path: '',
},
loading: false,
rules: {
moduleName: { required: true, message: '请选择模块', trigger: 'blur' },
labelName: { required: true, message: '请输入标签名称', trigger: 'blur' },
name: { required: true, message: '请输入标题', trigger: 'blur' },
path: { required: true, message: '请上传视频', trigger: 'blur' },
},
fileArray: [], // 编辑进来时,如果已经上传了视频,则保存视频地址
coverFile: {},
coverHide: false,
});
// 模块选择
const handleChangeModule = async (val: any) => {
try {
state.data.labelName = '';
if (!val) {
labelList.value = [];
return;
}
const op: any = event?.currentTarget;
let res = await artApi.getLabelList(op.dataset.op);
if (res?.success) {
labelList.value = res.data;
} else {
ElMessage.error('标签列表获取失败!');
}
} catch (error) {
ElMessage.error('标签列表获取失败!');
} finally {
}
};
// 获取视频详情
const getVideoDetail = async () => {
try {
state.loading = true;
let res = await vidApi.getVideoList({
moduleName: moduleName,
labelName: labelName,
size: 1,
current: 1,
});
if (res?.success) {
state.data = res.data.records[0];
// 下面这里处理一下因为后端返回的是0和1而前端需要的是true和false到时候提交的时候再转换回来
// 这个报错没关系的只是ts的语法检查不影响运行
if (state.data.path) {
const never: any = [{ name: state.data.name, url: viteUrl + state.data.path }];
state.fileArray = never;
state.coverHide = true;
}
}
} catch (error) {
ElMessage.error('获取视频详情失败');
} finally {
state.loading = false;
}
};
// 获取视频详情-根据id
const getVideoDetailById = async (id: number) => {
try {
state.loading = true;
let res = await vidApi.getVideoDetail(id);
if (res?.success) {
state.data = res.data;
// 下面这里处理一下因为后端返回的是0和1而前端需要的是true和false到时候提交的时候再转换回来
// 这个报错没关系的只是ts的语法检查不影响运行
state.data.moduleName = moduleName;
state.data.labelName = labelName;
if (state.data.path) {
const never: any = [{ name: state.data.name, url: viteUrl + state.data.path }];
state.fileArray = never;
state.coverHide = true;
}
}
} catch (error) {
ElMessage.error('获取视频详情失败');
} finally {
state.loading = false;
}
};
// 获取模块列表
const getModuleList = async () => {
try {
let res = await artApi.getModuleList();
if (res?.success) {
moduleList.value = res.data;
} else {
ElMessage.error('模块列表获取失败!');
}
} catch (error) {
ElMessage.error('模块列表获取失败!');
} finally {
}
};
const videoFormRef = ref();
// 保存视频,在新增视频时不会调用以下函数
const onSubmitForm = () => {
videoFormRef.value.validate((valid: boolean) => {
if (valid && state.coverHide) {
const form = { ...state.data };
if (state.coverFile.type === undefined) {
realSubmit(form);
} else {
uploadFile(state.coverFile, form);
}
} else {
ElMessage.error('请完善信息!');
}
});
};
// 返回
const cancel = () => {
mittBus.emit('onCurrentContextmenuClick', Object.assign({}, { contextMenuClickId: 1, ...route }));
router.push('/video');
};
// 上传文件
const uploadFile = async (file: any, form: any) => {
try {
state.loading = true;
const formdata = new FormData();
formdata.append('file', file);
formdata.append('type', 'video');
let res = await vidApi.uploadFileType(formdata);
if (res?.success) {
form.path = res.data;
realSubmit(form);
} else {
ElMessage.error('视频上传失败!');
}
} catch (error) {
state.loading = false;
ElMessage.error('视频上传失败!');
}
};
// 视频上传-真正的上传操作
const realSubmit = async (form: any) => {
try {
state.loading = true;
let res = route.path == '/video/add' ? await vidApi.saveVideo(form) : await vidApi.updateVideo(form);
if (res?.success) {
ElMessage.success('视频保存成功!');
cancel();
} else {
ElMessage.error('视频保存失败!');
}
} catch (error) {
ElMessage.error('处理失败!');
} finally {
state.loading = false;
}
};
// 视频上传禁用
const disabled = ref(false);
// 视频弹窗
const dialogVideoUrl = ref('');
const dialogVisible = ref(false);
const uploadRef = ref();
// 视频删除
const handleRemove = (file: UploadFile) => {
state.coverFile = {};
uploadRef.value.clearFiles();
state.coverHide = false;
};
// 视频预览
const handlePictureCardPreview = (file: UploadFile) => {
dialogVideoUrl.value = file.url!;
dialogVisible.value = true;
};
// 视频更改
const handleUploadChange = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
if (uploadFile.raw && uploadFile.raw.type.includes('video')) {
const file: any = uploadFile.raw;
state.coverFile = file;
state.data.path = file.name;
state.coverHide = true;
} else {
ElMessage.error('请上传视频文件!');
uploadRef.value.clearFiles();
}
};
onMounted(() => {
if (moduleName && labelName && route.path == '/video/edit') {
if (moduleName && moduleName !== '人才模块' && moduleName !== '简历模块') {
getVideoDetailById(id);
} else {
getVideoDetail();
}
}
getModuleList();
});
</script>
<style scoped lang="scss">
.coverImg :deep(.hide .el-upload--picture-card) {
display: none;
}
:deep(.el-upload-list--picture-card .el-upload-list__item){
width: 100%;
}
</style>