123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- <script setup>
- import { reactive, ref } from 'vue'
- import MD5 from 'js-md5'
- import { customInterence, videoInterface, departInterence, getOSSSign } from '@/api/api.js'
- import * as classifyEnInterface from "@/api/modules/classifyEnApi.js";
- import { videoENInterface } from '@/api/modules/reportEnApi'
- import { ElMessage } from 'element-plus';
- import { useRouter, onBeforeRouteLeave } from 'vue-router';
- let ALOSSINS = null //阿里云上传实例
- let ALOSSAbortCheckpoint = null //阿里云上传实例中断点
- const router = useRouter()
- const pageState = reactive({
- overviewConfig: {
- toolbarButtons: [
- 'textColor',
- 'bold',
- 'italic',
- 'underline',
- 'strikeThrough',
- 'subscript',
- 'superscript',
- 'fontFamily',
- 'fontSize',
- 'color',
- 'inlineClass',
- 'inlineStyle',
- 'paragraphStyle',
- 'lineHeight',
- 'paragraphFormat',
- 'align',
- 'outdent',
- 'indent',
- 'quote',
- 'specialCharacters',
- 'insertHR',
- 'selectAll',
- 'clearFormatting',
- 'undo',
- 'redo',
- ],
- height: 300,
- fontSizeDefaultSelection: "16",
- quickInsertEnabled: false,
- theme: "dark", //主题
- placeholderText: '请输入overview',
- language: "zh_cn",
- //允许粘贴的样式
- pasteAllowedStyleProps: ['font-family', 'font-size', 'color']
- },
- formData: {
- id: 0,
- title: '',
- classify: [],
- des: '',
- videoUrl: '',
- VideoSeconds: 0,//时长
- imgUrl: '',
- overview: '',
- state: 1,
- },
- rules: {
- title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
- classify: [{ required: true, message: '请选择标签', trigger: 'change' }],
- des: [{ required: true, message: '请填写摘要', trigger: 'blur' }],
- videoUrl: [{ required: true, message: '请上传视频', trigger: 'change' }],
- imgUrl: [{ required: true, message: '请上传视频封面', trigger: 'change' }],
- overview: [{ required: true, message: '请填写overview', trigger: 'blur' }],
- },
- options: [],
- percentage: 0,
- startUpload: false,//开始上传
- chooseImgPop: false,
- imgPopPage: 1,
- imgPopPagesize: 20,
- imgPopTotal: 0,
- popImgList: [],
- })
- // 分类变化设置标题
- function handleClassifyChange(e) {
- if (pageState.formData.id) return //编辑情况不改
- let name = ''
- pageState.options.forEach(item => {
- if (item.Id == pageState.formData.classify[0]) {
- item.Child.forEach(_item => {
- if (_item.Id == pageState.formData.classify[1]) {
- _item.Child.forEach(_it => {
- if (_it.Id == pageState.formData.classify[2]) {
- name = _it.ClassifyName
- }
- })
- }
- })
- }
- });
- pageState.formData.title = 'Online Presentation:' + name
- }
- //获取视频时长的promise
- function handleGetDuration(file) {
- return new Promise((resolve, reject) => {
- const fileUrl = URL.createObjectURL(file)
- const audioEl = new Audio(fileUrl)
- audioEl.addEventListener('loadedmetadata', (e) => {
- console.log('e.path', e.path)
- console.log('e.composedPath', e.composedPath())
- console.log('获取视频时长', e.composedPath()[0].duration);
- console.log(audioEl.duration);
- const t = e.composedPath()[0].duration
- resolve(t)
- })
- })
- }
- //上传视频判断格式
- function handelBeforeUploadVideo(e) {
- if (e.type != 'video/mp4') {
- ElMessage.warning('上传失败,上传视频格式不正确')
- return false
- }
- }
- // 上传视频
- async function handleUpload(e) {
- const duration = await handleGetDuration(e.file)
- if (duration > 600 && pageState.navType === 1) {
- ElMessage.warning('视频时长不得超过10分钟')
- return
- }
- console.log(e);
- pageState.formData.VideoSeconds = duration
- const res = await getOSSSign()
- if (res.Ret === 200) {
- let accessKeyId = res.Data.AccessKeyId
- let accessKeySecret = res.Data.AccessKeySecret
- let stsToken = res.Data.SecurityToken
- handleUploadToOSS(e.file, accessKeyId, accessKeySecret, stsToken)
- }
- }
- //上传到阿里云
- async function handleUploadToOSS(file, accessKeyId, accessKeySecret, stsToken) {
- pageState.startUpload = true
- ALOSSINS = new OSS({
- // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
- region: "oss-cn-shanghai",
- // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
- accessKeyId: accessKeyId,
- accessKeySecret: accessKeySecret,
- // 从STS服务获取的安全令牌(SecurityToken)。
- stsToken: stsToken,
- // 填写Bucket名称,例如examplebucket。
- bucket: "hzchart",
- endpoint: 'hzstatic.hzinsights.com',
- cname: true,
- timeout: 600000000000
- });
- // 生成文件名
- const t = new Date().getTime().toString()
- const temName = `static/yb/en/video/${MD5(t)}.${file.type.split('/')[1]}`
- console.log(temName);
- const options = {
- // 获取分片上传进度、断点和返回值。
- progress: (p, cpt, res) => {
- console.log(p);
- ALOSSAbortCheckpoint = cpt
- pageState.percentage = parseInt(p * 100)
- },
- // 设置并发上传的分片数量。
- parallel: 10,
- // 设置分片大小。默认值为1 MB,最小值为100 KB。
- partSize: 1024 * 1024 * 10, // 10MB
- };
- try {
- const res = await ALOSSINS.multipartUpload(temName, file, { ...options })
- console.log('上传结果', res);
- if (res.res.status === 200) {
- pageState.formData.videoUrl = 'https://hzstatic.hzinsights.com/' + res.name
- pageState.startUpload = false
- pageState.percentage = 0
- ALOSSAbortCheckpoint = null
- }
- } catch (error) {
- console.log('上传到阿里云失败', error);
- if (error.name !== "cancel") {//不是取消上传的则给错误提示
- ElMessage.warning('上传失败,请刷新重试')
- }
- pageState.startUpload = false
- pageState.percentage = 0
- ALOSSAbortCheckpoint = null
- }
- }
- onBeforeRouteLeave(() => {
- if (ALOSSAbortCheckpoint) {
- console.log('终止上传');
- ALOSSINS.abortMultipartUpload(ALOSSAbortCheckpoint.name, ALOSSAbortCheckpoint.uploadId)
- }
- })
- //显示选择视频封面列表
- function handleshowChooseImgPop() {
- pageState.imgPopPage = 1
- handleGetPopImgList()
- pageState.chooseImgPop = true
- }
- //弹窗中选择视频封面的列表数据
- async function handleGetPopImgList() {
- const res = await videoENInterface.videoCoverImgList({
- PageSize: pageState.imgPopPagesize,
- CurrentIndex: pageState.imgPopPage,
- })
- if (res.Ret === 200) {
- pageState.imgPopTotal = res.Data.Paging.Totals
- pageState.popImgList = res.Data.List || []
- }
- }
- function handleChooseImg(item) {
- pageState.formData.imgUrl = item.CoverUrl
- pageState.chooseImgPop = false
- }
- //保存数据
- const form = ref(null)
- function handleSave(e) {
- form.value.validate(async (valid) => {
- if (!valid) return
- let params = {
- Id: pageState.formData.id,
- // 传二级和三级分类
- ClassifyIdFirst: pageState.formData.classify[1],
- ClassifyIdSecond: pageState.formData.classify[2],
- Title: pageState.formData.title,
- Abstract: pageState.formData.des,
- State: pageState.formData.state,//状态:1:未发布,2:已发布
- VideoUrl: pageState.formData.videoUrl,
- VideoCoverUrl: pageState.formData.imgUrl,
- VideoSeconds: pageState.formData.VideoSeconds.toString(),
- Overview: pageState.formData.overview.replace(/<p data-f-id=\"pbf\".*?<\/p>/g, '')
- }
- const res = await videoENInterface.roadVideoSave(params)
- if (res.Ret === 200) {
- if (e === 'publish') {
- let res2 = await videoENInterface.roadVideoPublished({ Id: res.Data.Id })
- if (res2.Ret === 200) {
- ElMessage.success('发布成功')
- router.back()
- }
- } else {
- ElMessage.success('保存成功')
- router.back()
- }
- }
- })
- }
- //初始化
- function initPage() {
- const obj = sessionStorage.getItem('videoENItem') ? JSON.parse(sessionStorage.getItem('videoENItem')) : ''
- console.log(obj);
- if (obj) {
- pageState.formData.id = obj.Id
- pageState.formData.title = obj.Title
- pageState.formData.classify = [obj.ClassifyIdRoot, obj.ClassifyIdFirst, obj.ClassifyIdSecond]
- pageState.formData.des = obj.Abstract
- pageState.formData.videoUrl = obj.VideoUrl
- pageState.formData.VideoSeconds = obj.VideoSeconds
- pageState.formData.imgUrl = obj.VideoCoverUrl
- pageState.formData.overview = obj.Overview
- pageState.formData.state = obj.State
- }
- getClassify()
- }
- initPage()
- //获取分类数据
- async function getClassify() {
- const res = await classifyEnInterface.classifyList({ CurrentIndex: 1, PageSize: 1000, ClassifyType: 1 })
- if (res.Ret === 200) {
- const arr = res.Data.List || []
- pageState.options = arr.map(item => {
- if (item.Child && item.Child.length > 0) {
- item.Child.map(it => {
- console.log(it);
- it.disabled = it.Child ? false : true
- })
- }
- return {
- ...item,
- disabled: item.Child ? false : true
- }
- })
- }
- }
- </script>
- <template>
- <div class="video-add-page">
- <el-form
- ref="form"
- :model="pageState.formData"
- label-width="80px"
- :rules="pageState.rules"
- >
- <div style="display: flex">
- <div style="width: 50%">
- <el-form-item label="分类" prop="classify">
- <el-cascader
- style="width: 100%"
- :options="pageState.options"
- :show-all-levels="true"
- :props="{
- value: 'Id',
- label: 'ClassifyName',
- children: 'Child',
- }"
- v-model="pageState.formData.classify"
- placeholder="请选择分类"
- @change="handleClassifyChange"
- ></el-cascader>
- </el-form-item>
- <el-form-item label="标题" prop="title">
- <el-input
- v-model="pageState.formData.title"
- placeholder="请填写标题"
- ></el-input>
- </el-form-item>
- <el-form-item label="摘要" prop="des">
- <el-input
- v-model="pageState.formData.des"
- type="textarea"
- :rows="2"
- placeholder="请填写摘要"
- ></el-input>
- </el-form-item>
- </div>
- <div style="width: 50%">
- <el-form-item label="视频" prop="videoUrl">
- <div style="display: flex">
- <el-input
- readonly
- v-model="pageState.formData.videoUrl"
- placeholder="请上传视频"
- ></el-input>
- <el-upload
- style="display: inline-block; margin-left: 5px"
- action=""
- accept=".mp4"
- :http-request="handleUpload"
- :before-upload="handelBeforeUploadVideo"
- :show-file-list="false"
- :disabled="pageState.startUpload"
- >
- <el-button type="primary" :loading="pageState.startUpload"
- >上传视频</el-button
- >
- </el-upload>
- <el-progress
- type="circle"
- :percentage="pageState.percentage"
- width="40"
- style="margin-left: 10px"
- v-if="pageState.startUpload"
- ></el-progress>
- </div>
- <p style="color: #999">*注:要求视频格式为mp4,编码器为H.264</p>
- </el-form-item>
- <el-form-item label="封面" prop="imgUrl">
- <el-image
- style="width: 120px; height: 120px"
- :src="require('@/assets/img/icons/add-img.png')"
- v-if="!pageState.formData.imgUrl"
- @click="handleshowChooseImgPop"
- />
- <div v-else class="pre-img-box">
- <el-image
- style="width: 120px; height: 120px"
- :src="pageState.formData.imgUrl"
- :preview-src-list="[pageState.formData.imgUrl]"
- >
- </el-image>
- <span class="del" @click="pageState.formData.imgUrl = ''"
- >删除</span
- >
- </div>
- </el-form-item>
- </div>
- </div>
- <el-form-item label="Overview" prop="overview">
- <div id="leftoverview">
- <froala
- :id="`overview-editor`"
- :style="{ display: 'none' }"
- :ref="`overviewEditor`"
- :tag="'textarea'"
- :config="pageState.overviewConfig"
- v-model="pageState.formData.overview"
- >
- </froala>
- </div>
- </el-form-item>
- <div style="text-align: left; margin: 50px 0">
- <el-button type="primary" @click="handleSave">保存</el-button>
- <el-button type="primary" plain @click="handleSave('publish')"
- >发布</el-button
- >
- <el-button @click="$router.back()">取消</el-button>
- </div>
- </el-form>
- <!-- 选择视频封面弹窗 -->
- <el-dialog
- v-model="pageState.chooseImgPop"
- :modal-append-to-body="false"
- draggable
- width="60vw"
- title="视频封面库"
- >
- <div class="choose-img-wrap">
- <div class="list">
- <div
- class="item"
- v-for="item in pageState.popImgList"
- :key="item.CommunityVideoCoverId"
- @click="handleChooseImg(item)"
- >
- <el-image style="width: 120px; height: 120px" :src="item.CoverUrl">
- </el-image>
- <div>{{ item.CoverName }}</div>
- </div>
- </div>
- <div>
- <el-pagination
- layout="total,prev,pager,next"
- background
- :current-page="pageState.imgPopPage"
- @current-change="handleimgPopPageChange"
- :page-size="pageState.imgPopPagesize"
- :total="pageState.imgPopTotal"
- style="float: right; margin-top: 20px"
- >
- </el-pagination>
- </div>
- </div>
- </el-dialog>
- </div>
- </template>
- <style lang="scss" scoped>
- .video-add-page {
- border: 1px solid #dcdfe6;
- background-color: #fff;
- border-radius: 4px;
- padding: 30px;
- .choose-img-wrap {
- padding-bottom: 80px;
- .list {
- display: flex;
- flex-wrap: wrap;
- }
- .item {
- flex-shrink: 0;
- text-align: center;
- margin-right: 20px;
- margin-bottom: 20px;
- border: 1px solid rgb(199, 198, 198);
- padding: 5px;
- border-radius: 4px;
- }
- }
- .pre-img-box {
- position: relative;
- width: 120px;
- height: 120px;
- .del {
- display: none;
- color: #fff;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- text-align: center;
- line-height: 24px;
- background-color: rgba($color: #000000, $alpha: 0.8);
- cursor: pointer;
- }
- &:hover {
- .del {
- display: block;
- }
- }
- }
- }
- </style>
|