场景视频的上传编辑与播放
This commit is contained in:
parent
e9facb70f5
commit
f4e0552a61
@ -46,6 +46,7 @@
|
||||
"vue-i18n": "^8.12.0",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.1.6",
|
||||
"vue-video-player": "^5.0.1",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuex": "^3.1.0",
|
||||
"wangeditor": "^4.6.17",
|
||||
|
@ -332,6 +332,31 @@ export function publishContextSence(data) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 场景视频保存
|
||||
* @param {Object} data
|
||||
* @param {String} data.name 场景名称
|
||||
* @param {String} data.type Video=视频
|
||||
* @param {Object} data.scene 大赛场景
|
||||
* @param {String} data.scene.url 地址
|
||||
* @param {String} data.scene.fileName 文件名
|
||||
*/
|
||||
export function saveSceneVideo(data) {
|
||||
return request({
|
||||
url: `/api/exercise/race/scene/custom/edit`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 场景视频编辑 */
|
||||
export function editSceneVideo(sceneId, data) {
|
||||
return request({
|
||||
url: `/api/exercise/race/scene/custom/${sceneId}/edit`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 竞赛试卷测试模块任务集合
|
||||
* @param {String} paperId 试卷id
|
||||
* @param {String} moduleId 模块id
|
||||
|
@ -28,6 +28,11 @@ import '@/directive/verticalDrag/index.js';
|
||||
import '@/directive/waves/index.js';
|
||||
import messages from '@/i18n/index';
|
||||
|
||||
import VideoPlayer from 'vue-video-player/src';
|
||||
import 'vue-video-player/src/custom-theme.css';
|
||||
import 'video.js/dist/video-js.css';
|
||||
|
||||
Vue.use(VideoPlayer);
|
||||
Vue.use(ElementUI);
|
||||
Vue.use(VueI18n);
|
||||
Vue.config.devtools = true; // 开发环境显示vue控制台
|
||||
|
122
src/views/contest/PlayVideo.vue
Normal file
122
src/views/contest/PlayVideo.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<el-dialog v-dialogDrag :title="sceneName" :visible.sync="dialogVisible" width="50%" :before-close="handleClose" center :close-on-click-modal="false">
|
||||
<div v-loading="loading">
|
||||
<video-player
|
||||
v-show="!loading"
|
||||
ref="videoPlayer"
|
||||
:playsinline="false"
|
||||
:options="playOptions"
|
||||
@ready="onPlayerReady"
|
||||
@play="onPlayerPlay($event)"
|
||||
@timeupdate="onPlayerTimeupdate($event)"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getContextSenceDetail } from '@/api/contest';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
dialogVisible: false,
|
||||
sceneName:'播放视频',
|
||||
playedTime: '',
|
||||
currentTime: 0,
|
||||
maxTime: 0,
|
||||
playOptions: {
|
||||
height: '200px',
|
||||
width: '100%',
|
||||
playbackRates: [1.0],
|
||||
autoplay: false,
|
||||
muted: false,
|
||||
loop: false,
|
||||
preload: 'auto',
|
||||
language: 'zh-CN',
|
||||
aspectRatio: '16:9',
|
||||
fluid: true,
|
||||
sources: [
|
||||
{
|
||||
type: 'video/mp4',
|
||||
src: ''
|
||||
}
|
||||
],
|
||||
poster: '',
|
||||
notSupportedMessage: '此视频暂无法播放,请稍后再试',
|
||||
controlBar: {
|
||||
currentTimeDisplay: true,
|
||||
progressControl: true,
|
||||
playbackRateMenuButton: true,
|
||||
timeDivider: true,
|
||||
durationDisplay: true,
|
||||
remainingTimeDisplay: true,
|
||||
fullscreenToggle: true
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
urlPrefix() {
|
||||
return this.$store.state.user.ossUrl;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doShow(sceneId) {
|
||||
this.dialogVisible = true;
|
||||
this.loading = true;
|
||||
getContextSenceDetail(sceneId).then((res) => {
|
||||
this.playOptions.sources[0].src = `${this.urlPrefix}/${res.data.scene.url}`;
|
||||
this.sceneName = res.data.name;
|
||||
this.loading = false;
|
||||
}).catch(error => {
|
||||
this.$message.error(error.message);
|
||||
});
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
onPlayerPlay(player) {
|
||||
let playTime = 0;
|
||||
if (
|
||||
Number(Math.floor(this.playedTime)) ===
|
||||
Number(Math.floor(player.duration()))
|
||||
) {
|
||||
this.playedTime = 0;
|
||||
playTime = 0;
|
||||
} else if (
|
||||
Number(Math.floor(player.currentTime())) !==
|
||||
Number(Math.floor(this.playedTime))
|
||||
) {
|
||||
playTime = this.playedTime;
|
||||
player.currentTime(playTime);
|
||||
}
|
||||
},
|
||||
onPlayerPause(player) {
|
||||
this.playedTime = player.currentTime();
|
||||
},
|
||||
onPlayerTimeupdate(player) {
|
||||
const timeDisplay = player.currentTime();
|
||||
if (timeDisplay - this.currentTime > 1) {
|
||||
player.currentTime(this.currentTime > this.maxTime ? this.currentTime : this.maxTime);
|
||||
}
|
||||
this.currentTime = player.currentTime();
|
||||
this.maxTime = this.currentTime > this.maxTime ? this.currentTime : this.maxTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/deep/{
|
||||
.el-dialog__title {
|
||||
color:#fff;
|
||||
}
|
||||
.el-dialog__header{
|
||||
background: linear-gradient(to bottom, #01468B, #00172E);
|
||||
}
|
||||
.el-dialog__body{
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -44,12 +44,14 @@
|
||||
</div>
|
||||
<div style="text-align: center;margin-top: 15px;">
|
||||
<el-button v-show="nowData.ruleId" v-loading="loading" type="primary" @click="showScoreRule">评分表</el-button>
|
||||
<el-button v-show="nowData.sceneId" v-loading="loading" type="primary" @click="startTask">开始任务</el-button>
|
||||
<el-button v-show="nowData.sceneId&&nowData.scenetype!=='Video'" v-loading="loading" type="primary" @click="startTask">开始任务</el-button>
|
||||
<el-button v-show="nowData.sceneId&&nowData.scenetype=='Video'" type="primary" @click="playSceneVideo">播放视频</el-button>
|
||||
<el-button v-loading="loading" type="primary" :disabled="nowKey === taskList.length-1" @click="nextTask">下一任务</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<score-rule ref="scoreRule" />
|
||||
<play-Video ref="playVideo" />
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@ -58,10 +60,12 @@ import { getTaskTree, getContextSenceDetail} from '@/api/contest';
|
||||
import { createSimulationNoFunction } from '@/api/simulation';
|
||||
import { getPublishMapInfo } from '@/api/jmap/map';
|
||||
import ScoreRule from './scoreRule';
|
||||
import PlayVideo from './PlayVideo';
|
||||
export default {
|
||||
name: 'ContestDetail',
|
||||
components: {
|
||||
ScoreRule
|
||||
ScoreRule,
|
||||
PlayVideo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -145,6 +149,9 @@ export default {
|
||||
this.nowKey++;
|
||||
this.nowData = this.taskList[this.nowKey];
|
||||
},
|
||||
playSceneVideo() {
|
||||
this.$refs.playVideo.doShow(this.nowData.sceneId);
|
||||
},
|
||||
startTask() {
|
||||
this.loading = true;
|
||||
try {
|
||||
|
@ -1,13 +1,18 @@
|
||||
<template>
|
||||
<div>
|
||||
<query-list-page ref="user" :card-padding="10" :pager-config="pagerConfig" :query-form="queryForm" :query-list="queryList" />
|
||||
<upload-Video ref="uploadVideo" @reloadTable="reloadTable" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { queryContestSencePaged, deleteContestSence } from '@/api/contest';
|
||||
import UploadVideo from './UploadVideo';
|
||||
export default {
|
||||
name: 'ContestSeasonManage',
|
||||
components: {
|
||||
UploadVideo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pagerConfig: {
|
||||
@ -28,7 +33,7 @@ export default {
|
||||
type: 'select',
|
||||
label: '类 型',
|
||||
config: {
|
||||
data: [{label: '二维', value: 'Local'}, {label: '三维', value: 'Link'}]
|
||||
data: [{label: '二维', value: 'Local'}, {label: '三维', value: 'Link'}, {label: '视频', value: 'Video'}]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,7 +55,15 @@ export default {
|
||||
title: '类 型',
|
||||
prop: 'type',
|
||||
type: 'tag',
|
||||
columnValue: (row) => { return row.type === 'Local' ? '二维' : '三维'; },
|
||||
columnValue: (row) => {
|
||||
if (row.type === 'Local') {
|
||||
return '二维';
|
||||
} else if (row.type === 'Link') {
|
||||
return '三维';
|
||||
} else {
|
||||
return '视频';
|
||||
}
|
||||
},
|
||||
tagType: (row) => { return ''; }
|
||||
},
|
||||
{
|
||||
@ -72,10 +85,13 @@ export default {
|
||||
title: '操 作',
|
||||
width: '320',
|
||||
buttons: [
|
||||
// {
|
||||
// name: '编辑',
|
||||
// handleClick: this.doEdit
|
||||
// },
|
||||
{
|
||||
name: '编辑',
|
||||
handleClick: this.doEdit,
|
||||
showControl: row => {
|
||||
return row.type == 'Video';
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '删 除',
|
||||
handleClick: this.doDelete,
|
||||
@ -83,10 +99,10 @@ export default {
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
{ text: '上 传', handler: this.doUpload }
|
||||
]
|
||||
// actions: [
|
||||
// { text: '添 加', handler: this.doCreate }
|
||||
// ]
|
||||
}
|
||||
};
|
||||
},
|
||||
@ -94,11 +110,11 @@ export default {
|
||||
reloadTable() {
|
||||
this.queryList.reload();
|
||||
},
|
||||
doCreate() {
|
||||
// this.$refs.addSeason.doShow();
|
||||
doUpload() {
|
||||
this.$refs.uploadVideo.doShow();
|
||||
},
|
||||
doEdit(row) {
|
||||
// this.$refs.addSeason.doShow(row);
|
||||
doEdit(index, row) {
|
||||
this.$refs.uploadVideo.doShow(row.id);
|
||||
},
|
||||
doDelete(index, row) {
|
||||
this.$confirm('该操作将删除竞赛场景,是否继续?', '提 示', {
|
||||
|
238
src/views/contestDataManage/contestSceneManage/UploadVideo.vue
Normal file
238
src/views/contestDataManage/contestSceneManage/UploadVideo.vue
Normal file
@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<el-dialog v-dialogDrag :visible="show" width="500px" center :close-on-click-modal="false" title="上传视频" :before-close="doClose" destroy-on-close>
|
||||
<div v-if="formData.id==''||formData.reUpLoad" class="wrapper">
|
||||
<label v-show="showUpload" for="file">
|
||||
<el-card shadow="hover" class="card">
|
||||
<i class="el-icon-upload" />
|
||||
</el-card>
|
||||
</label>
|
||||
<input id="file" ref="file" style="display: none;" type="file" @change="onUploadChange">
|
||||
<div v-show="!showUpload" class="preview" @mouseenter="showDelete = true" @mouseleave="showDelete = false">
|
||||
<el-card shadow="hover" class="card">
|
||||
<div class="file-type">{{ fileType }}</div>
|
||||
<div class="file-name">{{ formData.fileName }}</div>
|
||||
<div v-show="showDelete" class="delete" @click="handleDelete">
|
||||
<i class="el-icon-delete-solid" />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
<el-form ref="form" :model="formData" inline :rules="rules">
|
||||
<el-form-item label="场景名字" required prop="title">
|
||||
<el-input v-model="formData.title" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.id" label="是否重新上传" prop="reUpLoad">
|
||||
<el-checkbox v-model="formData.reUpLoad" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.id==''||formData.reUpLoad" label="文件名" required prop="fileName">
|
||||
<el-input v-model="formData.fileName" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div v-if="!showUpload" class="path-display">
|
||||
<span>文件存储路径: </span>
|
||||
<code>{{ `${urlPrefix}/${formData.directory}/${formData.fileName}` }}</code>
|
||||
</div>
|
||||
<footer>
|
||||
<el-button v-if="formData.id==''" type="primary" size="small" @click="handleUpload">上传</el-button>
|
||||
<el-button v-if="formData.id" type="primary" size="small" @click="doSave">确定</el-button>
|
||||
<el-button size="small" @click="doClose">取消</el-button>
|
||||
</footer>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getUploadUrl } from '@/api/projectConfig';
|
||||
import { checkIsExist } from '@/api/management/fileManage';
|
||||
import { saveSceneVideo, getContextSenceDetail, editSceneVideo } from '@/api/contest';
|
||||
export default {
|
||||
name: 'UploadVideo',
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
showUpload: true,
|
||||
showDelete: false,
|
||||
fileType: '',
|
||||
formData: {
|
||||
id:'',
|
||||
reUpLoad:false,
|
||||
directory: 'Video',
|
||||
title: '',
|
||||
fileName: ''
|
||||
},
|
||||
rules: {
|
||||
title: [{ required: true, message: '请输入场景名字', trigger: 'blur' }],
|
||||
fileName: [{ required: true, message: '请输入文件名', trigger: 'blur' }]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
urlPrefix() {
|
||||
return this.$store.state.user.ossUrl;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doShow(sceneId) {
|
||||
this.show = true;
|
||||
if (sceneId) {
|
||||
getContextSenceDetail(sceneId).then((res) => {
|
||||
this.formData.id = res.data.id;
|
||||
this.formData.title = res.data.name;
|
||||
this.formData.fileName = res.data.scene.fileName;
|
||||
}).catch(error => {
|
||||
this.$message.error(error.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
doClose() {
|
||||
this.show = false;
|
||||
this.handleDelete();
|
||||
},
|
||||
onUploadChange(e) {
|
||||
const fileList = e.target.files;
|
||||
if (!fileList.length) return;
|
||||
this.showUpload = false;
|
||||
const fileName = fileList[0].name;
|
||||
this.formData.fileName = fileName;
|
||||
const devideIndex = fileName.lastIndexOf('.');
|
||||
this.formData.title = fileName.slice(0, devideIndex);
|
||||
this.fileType = fileList[0].type.split('/').pop();
|
||||
},
|
||||
doSave() {
|
||||
const data = {name:this.formData.title, type:this.formData.directory,
|
||||
scene:{url:`${this.formData.directory}/${this.formData.fileName}`, fileName:this.formData.fileName}};
|
||||
if (!this.formData.reUpLoad) {
|
||||
editSceneVideo(this.formData.id, data).then((res) => {
|
||||
this.$message.success('编辑成功');
|
||||
this.doClose();
|
||||
this.$emit('reloadTable');
|
||||
}).catch(error => {
|
||||
this.$message.error(error.message);
|
||||
});
|
||||
} else {
|
||||
this.handleUpload();
|
||||
}
|
||||
},
|
||||
handleUpload() {
|
||||
const form = this.$refs.form;
|
||||
const file = this.$refs.file.files[0];
|
||||
const params = {
|
||||
directory: this.formData.directory,
|
||||
fileName: this.formData.fileName
|
||||
};
|
||||
const execUpload = () => {
|
||||
getUploadUrl({ ...params, method: 'PUT' }).then(res => {
|
||||
const url = res.data;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('PUT', url);
|
||||
xhr.upload.onload = () => {
|
||||
const data = {name:this.formData.title, type:this.formData.directory,
|
||||
scene:{url:`${this.formData.directory}/${this.formData.fileName}`, fileName:this.formData.fileName}};
|
||||
if (!this.formData.id) {
|
||||
saveSceneVideo(data).then(res => {
|
||||
this.$message.success('上传成功');
|
||||
this.doClose();
|
||||
this.$emit('reloadTable');
|
||||
});
|
||||
} else {
|
||||
editSceneVideo(this.formData.id, data).then((res) => {
|
||||
this.$message.success('编辑且重新上传成功');
|
||||
this.doClose();
|
||||
this.$emit('reloadTable');
|
||||
}).catch(error => {
|
||||
this.$message.error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
xhr.send(file);
|
||||
});
|
||||
};
|
||||
form.validate().then(valid => {
|
||||
if (valid && file.size > 0) {
|
||||
checkIsExist(params).then(resp => {
|
||||
if (!resp.data) {
|
||||
execUpload();
|
||||
} else {
|
||||
this.$confirm('该目录下有同名文件, 是否覆盖?', this.$t('global.tips'), {
|
||||
confirmButtonText: this.$t('global.confirm'),
|
||||
cancelButtonText: this.$t('global.cancel'),
|
||||
type: 'warning'
|
||||
}).then(execUpload);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDelete() {
|
||||
if (this.formData.id == '' || this.formData.reUpLoad) {
|
||||
this.$refs.file.value = '';
|
||||
}
|
||||
this.showUpload = true;
|
||||
this.formData.id = '';
|
||||
this.formData.reUpLoad = false;
|
||||
this.formData.title = '';
|
||||
this.formData.fileName = '';
|
||||
this.formData.directory = '';
|
||||
this.fileType = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.block {
|
||||
user-select: none;
|
||||
padding: 5px;
|
||||
color: #909399;
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
.card {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
.el-icon-upload {
|
||||
font-size: 4em;
|
||||
}
|
||||
.file-type {
|
||||
text-align: center;
|
||||
font-size: 4rem;
|
||||
line-height: 6rem;
|
||||
}
|
||||
.file-name {
|
||||
line-height: 1.2em;
|
||||
text-align: center;
|
||||
}
|
||||
.delete {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0;
|
||||
left: 0%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
opacity: 0.5;
|
||||
transition-duration: 1s;
|
||||
transition-property: all;
|
||||
font-size: 3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.path-display {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user