123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- <template>
- <!-- 新增编辑视频 -->
- <div class="modify-video-page-wrap">
- <t-form :data="form" :rules="rules" ref="formRules">
- <t-form-item label="所属分类" name="ClassifyId">
- <t-cascader placeholder="选择所属分类"
- v-model="form.ClassifyId"
- :options="classifyList"
- style="width: 300px;"
- clearable
- :keys="{
- emitPath:false,
- label:'ClassifyName',
- value:'ClassifyId',
- children:'Children'}">
- </t-cascader>
- </t-form-item>
- <t-form-item label="视频名称" name="Title">
- <t-input style="width: 300px;" v-model="form.Title" placeholder="请输入视频名称"></t-input>
- </t-form-item>
- <t-form-item label="视频简介" name="Introduce">
- <t-textarea v-model="form.Introduce"
- placeholder="请输入视频简介"
- maxlength="50" show-word-limit :autosize="{ minRows: 5, maxRows: 5 }"></t-textarea >
- </t-form-item>
- <t-form-item label="上传封面" name="CoverImg">
- <div style="display: block;">
- <t-upload accept="image/*"
- :http-request="handleUploadImg" :show-file-list="false" :disabled="isImageUploading">
- <t-button theme="primary" :loading="isImageUploading">点击上传</t-button>
- <span style="color:#999999;margin-left: 5px;" @click.stop>建议尺寸比例3:2,支持png、jpg、gif、jpeg格式</span>
- </t-upload>
- <div class="img-box">
- <img :src="form.CoverImg" v-if="form.CoverImg">
- <span v-else style="color:#999999;line-height: 100px;">请上传封面图</span>
- </div>
- </div>
- </t-form-item>
- <t-form-item label="上传视频" name="VideoUrl">
- <div style="display: block;">
- <t-upload accept=".mp4"
- :request-method="handleUploadVideo" :show-file-list="false" :disabled="isVideoUploading">
- <t-button theme="primary" :loading="isVideoUploading">点击上传</t-button>
- <span style="color:#999999;margin-left: 5px;" @click.stop>仅支持mp4格式</span>
- </t-upload>
-
- <div class="img-box">
- <t-progress type="circle" :percentage="percentage" width="40" v-if="isVideoUploading"></t-progress>
- <span v-if="form.VideoUrl&&!form.VideoId" class="duration">{{timeDuration}}</span>
- <img :src="form.CoverImg" v-if="form.VideoUrl" @click="handlePreviewVideo">
- <span v-else style="color:#999999;line-height: 100px;">请上传视频</span>
- </div>
- </div>
-
- </t-form-item>
- <t-form-item label="视频标签" name="TagIds">
- <t-button variant="text" theme="primary" @click="isModifyDialogShow = true">选择标签</t-button>
- <div class="tag-list" :key="tagIdKey">
- <span class="tag-item" v-for="tag in form.TagIds" :key="tag.TagId">
- <span>{{tag.TagName}}</span>
- <span @click.stop="removeTag(tag)"><i class="t-icon-close"></i></span>
- </span>
- </div>
- </t-form-item>
- </t-form>
- <div class="btn-box">
- <t-button variant="outline" plain @click="changeRoute">取消</t-button>
- <t-button theme="primary" @click="modifyVideo" :loading="isImageUploading||isVideoUploading">保存</t-button>
- <t-button theme="primary" @click="publishVideo" :loading="isImageUploading||isVideoUploading">发布</t-button>
- </div>
- <!-- 选择标签弹窗 -->
- <AddTags
- :isModifyDialogShow="isModifyDialogShow"
- :Tags="form.TagIds"
- :tagList="tagList"
- :getTagList="getTagList"
- @modify="modifyTags"
- @close="isModifyDialogShow=false"
- />
- <!-- 预览视频弹窗 -->
- <t-dialog
- :visible.sync="previewPop"
- :modal-append-to-body='false'
- width="60vw"
- :title="previewPopTitle"
- @close="endingPreview"
- >
- <video style="width: 100%;height: 100%;max-height: 70vh;outline: none;"
- controls :src="previewVideoUrl" autoplay ref="previewVideo">
- 您的浏览器暂不支持,请更换浏览器
- </video>
- </t-dialog>
- </div>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue';
- import MD5 from 'js-md5';
- import { VideoInterface, TagInterface, ClassifyInterface } from '@/api/modules/trainingApi';
- import AddTags from './components/addTags.vue';
- import _ from 'lodash';
- import { useRoute, useRouter } from 'vue-router'
- const router=useRouter()
- const route=useRoute()
- const form = ref({
- Title: '',
- Introduce: '',
- ClassifyId: '',
- TagIds: [],
- CoverImg: '',
- VideoUrl: ''
- });
- const rules = {
- ClassifyId: [{ required: true, message: '请选择所属分类' }],
- Title: [{ required: true, message: '请输入视频名称' }],
- CoverImg: [{ required: true, message: '请选择视频封面' }],
- VideoUrl: [{ required: true, message: '请上传视频' }],
- TagIds: [{
- required: true,
- validator: (rule, value, callback) => {
- if (!value.length) {
- return callback(new Error("请至少选择一个标签"));
- } else {
- return callback();
- }
- }
- }]
- }
- const tagIdKey = ref(0);
- const isModifyDialogShow = ref(false);
- const isImageUploading = ref(false);
- const isVideoUploading = ref(false);
- const percentage = ref(0);
- const timeDuration = ref('');
- const previewVideoUrl = ref('');
- const previewPop = ref(false);
- const previewPopTitle = ref('');
- const tagList = ref([]);
- const classifyList = ref([]);
- const previewVideo = ref(null);
- const formRules = ref(null);
- // 定义全局变量
- let ALOSSINS = null;
- let ALOSSAbortCheckpoint = null;
- // 获取分类列表
- const getClassifyList = async (type = '') => {
- try {
- const res = await ClassifyInterface.getClassifyList({});
- if (res.Ret !== 200) return;
- classifyList.value = (res.Data && res.Data.List) || [];
- filterNodes(classifyList.value);
- classifyList.value = classifyList.value.map(item => {
- if (!item.Children) {
- item.disabled = true;
- }
- return item;
- });
- } catch (error) {
- console.error('获取分类列表失败:', error);
- }
- };
- // 过滤节点
- const filterNodes = (arr) => {
- if (arr.length) {
- arr.forEach(item => {
- if (item.Children && item.Children.length) {
- filterNodes(item.Children);
- }
- if (item.Children && !item.Children.length) {
- delete item.Children;
- }
- });
- }
- };
- // 获取标签列表
- const getTagList = async (keyword = '') => {
- try {
- const res = await TagInterface.getTagList({
- Keyword: keyword,
- PageSize: 1000,
- CurrentIndex: 1
- });
- if (res.Ret !== 200) return;
- tagList.value = (res.Data && res.Data.List) || [];
- } catch (error) {
- console.error('获取标签列表失败:', error);
- }
- };
- // 检查图片是否合法
- const handleUploadImg = (file) => {
- isImageUploading.value = true;
- const { type } = file.file;
- if (!['image/png', 'image/jpeg'].includes(type)) {
- MessagePlugin.warning('仅支持png、jpg格式的图片');
- isImageUploading.value = false;
- return;
- }
- uploadImg(file);
- };
- // 上传图片
- const uploadImg = (file) => {
- const formData = new FormData();
- formData.append('file', file.file);
- VideoInterface.bannerupload(formData).then(res => {
- isImageUploading.value = false;
- if (res.Ret !== 200) return;
- form.value.CoverImg = res.Data.ResourceUrl;
- });
- };
- // 检查视频是否合法,并获取视频时长
- const handleUploadVideo = async (file) => {
- if (file.type !== 'video/mp4') {
- MessagePlugin.warning('上传失败,上传视频格式不正确');
- return;
- }
- const duration = await handleGetDuration(file);
- timeDuration.value = `${String(parseInt(duration / 60)).padStart(2, '0')}:${String(parseInt(duration % 60)).padStart(2, '0')}`;
- uploadVideo(file);
- isVideoUploading.value = true;
- };
- // 获取视频时长的Promise
- const handleGetDuration = (file) => {
- console.log(file);
-
- return new Promise((resolve, reject) => {
- const fileUrl = URL.createObjectURL(file.raw);
- const audioEl = new Audio(fileUrl);
- audioEl.addEventListener('loadedmetadata', (e) => {
- const t = e.composedPath()[0].duration;
- resolve(t);
- });
- });
- };
- // 上传视频
- const uploadVideo = async (file) => {
- const res = await VideoInterface.getOSSSign();
- if (res.Ret === 200) {
- handleUploadToOSS(file.raw, res.Data);
- }
- };
- // 上传到阿里云
- const handleUploadToOSS = async (file, { AccessKeyId, AccessKeySecret, SecurityToken }) => {
- ALOSSINS = new OSS({
- region: 'oss-cn-shanghai',
- accessKeyId: AccessKeyId,
- accessKeySecret: AccessKeySecret,
- stsToken: SecurityToken,
- bucket: 'hzchart',
- endpoint: 'hzstatic.hzinsights.com',
- cname: true,
- timeout: 600000000000
- });
- const t = new Date().getTime().toString();
- const temName = `static/yb/video/${MD5(t)}.${file.type.split('/')[1]}`;
- const options = {
- progress: (p, cpt, res) => {
- ALOSSAbortCheckpoint = cpt;
- percentage.value = parseInt(p * 100);
- },
- parallel: 10,
- partSize: 1024 * 1024 * 10
- };
- try {
- console.log(temName, file, { ...options });
-
- const res = await ALOSSINS.multipartUpload(temName, file, { ...options });
- console.log('上传结果', res);
- if (res.res.status === 200) {
- form.value.VideoUrl = `https://hzstatic.hzinsights.com/${res.name}`;
- percentage.value = 0;
- ALOSSAbortCheckpoint = null;
- isVideoUploading.value = false;
- }
- } catch (error) {
- if (error.name !== 'cancel') {
- MessagePlugin.warning('上传失败,请刷新重试');
- }
- percentage.value = 0;
- ALOSSAbortCheckpoint = null;
- isVideoUploading.value = false;
- }
- };
- // 删除所选标签
- const removeTag = (tag) => {
- const index = form.value.TagIds.findIndex(i => i.TagId === tag.TagId);
- if (index !== -1) {
- form.value.TagIds.splice(index, 1);
- }
- tagIdKey.value++;
- };
- // 改变所选标签
- const modifyTags = (tags) => {
- form.value.TagIds = _.cloneDeep(tags);
- isModifyDialogShow.value = false;
- };
- // 获取视频信息
- const getVideoDetail = () => {
- const { VideoId } = route.query;
- if (!Number(VideoId)) return;
- VideoInterface.getVideoDetail({ VideoId: Number(VideoId) }).then(res => {
- if (res.Ret !== 200) return;
- form.value = res.Data || {};
- if (form.value.Classify) {
- const classifyArr = getDataClassify(form.value.Classify);
- form.value.ClassifyId = classifyArr[classifyArr.length - 1];
- delete form.value.Classify;
- }
- if (form.value.Tags) {
- form.value.TagIds = form.value.Tags;
- delete form.value.Tags;
- }
- });
- };
- // 获取视频分类路径
- const getDataClassify = (classify, classifyArr = []) => {
- classifyArr.push(classify.ClassifyId);
- if (classify.Children && classify.Children.length) {
- return getDataClassify(classify.Children[0], classifyArr);
- }
- return classifyArr;
- };
- // 添加/编辑视频
- const modifyVideo = async (type = 'modify') => {
- const valid = await formRules.value.validate()
- if (valid !== true) return
- let res = null;
- const params = { ...form.value, TagIds: form.value.TagIds.map(t => t.TagId) };
- if (!form.value.VideoId) {
- res = await VideoInterface.addVideo(params);
- } else {
- res = await VideoInterface.editVideo(params);
- }
- if (res.Ret !== 200) return;
- if (type !== 'publish') {
- MessagePlugin.success(`${form.value.VideoId ? '编辑' : '添加'}成功`);
- changeRoute();
- }
- if (!form.value.VideoId) {
- form.value.VideoId = res.Data || '';
- }
- };
- // 发布视频
- const publishVideo = async () => {
- let res = {};
- await modifyVideo('publish');
- if (form.value.VideoId) {
- res = await VideoInterface.publishVideo({ VideoId: Number(form.value.VideoId), PublishState: 1 });
- if (res.Ret !== 200) return;
- MessagePlugin.success('发布成功');
- }
- changeRoute();
- };
- // 改变路由
- const changeRoute = () => {
- if (ALOSSAbortCheckpoint) {
- console.log('终止上传');
- ALOSSINS.abortMultipartUpload(ALOSSAbortCheckpoint.name, ALOSSAbortCheckpoint.uploadId);
- }
- router.push('/training/trainingVideo');
- };
- // 预览视频
- const handlePreviewVideo = () => {
- if (isVideoUploading.value || !form.value.VideoUrl) return;
- previewVideo.play();
- previewPopTitle.value = form.value.Title || '暂无标题';
- previewVideoUrl.value = form.value.VideoUrl;
- previewPop.value = true;
- };
- // 结束预览弹窗关闭回调 -- 暂停视频
- const endingPreview = () => {
- previewVideo.pause();
- };
- // 生命周期钩子 - 组件挂载时执行
- onMounted(() => {
- getClassifyList('leaf');
- getTagList();
- getVideoDetail();
- });
- </script>
- <style lang="scss">
- .modify-video-page-wrap{
- .t-textarea__inner{
- resize: none;
- }
- .t-form-item{
- .t-form-item__content{
- display: flex;
- flex-direction: column;
- align-items:flex-start;
- }
- }
- }
- </style>
- <style scoped lang="scss">
- .modify-video-page-wrap{
- box-sizing: border-box;
- padding:40px;
- background-color: #fff;
- border-radius: 4px;
- .t-form{
- .t-input,.t-select,.t-textarea{
- width:500px;
- }
- .img-box{
- /* background-color: #D9D9D9; */
- border:1px dashed #d9d9d9;
- width:150px;
- height:100px;
- text-align: center;
- margin-top: 10px;
- position: relative;
- line-height: normal;
- img{
- width:100%;
- height:100%;
- }
- .duration{
- position:absolute;
- right:0;
- bottom:0;
- background-color: black;
- color:#fff;
- padding:4px;
- }
- .t-progress{
- position: absolute;
- left:50%;
- top:50%;
- transform: translate(-50%,-50%);
- background-color: white;
- border-radius: 50%;
- }
- }
- .tag-list{
- display: flex;
- gap:10px;
- line-height:0;
- .tag-item{
- cursor: pointer;
- text-align: center;
- box-sizing: border-box;
- padding:12px;
- min-width:78px;
- color: #409EFF;
- background-color: #EAF3FE;
- border:1px solid #409EFF;
- border-radius: 2px;
- }
- }
- }
- .btn-box{
- margin-top: 20px;
- text-align: center;
- .t-button{
- margin-right: 50px;
- width:120px;
- text-align: center;
- }
- }
- }
- </style>
|