我在2019年的时候,曾经在一家工厂,做过一个文件管理系统,当时有个界面效果是如下,采用的是jquery来写的,2年后,我又看到了这种,不过这并不是我写的,但不影响我去学习研究并对比,如下是现在VUE响应式的文件夹/文件前端效果
Rec 0002
功能:新增 移动 上传 删除 全选 重命名 查找
所用技术 Vue+element
所用技术点
element的面包屑
element的分页
vue-simple-uploader文件多线程式上传
封装了图片组件
视频播放器vue-video-player
难度等级:中等
技术难度还好,主要是和后端约定好数据的设计,业务步骤,有了这些,再加上上面的插件,基本就没难度了。附上完整代码。
index.vue
<!--多媒体素材首页 -->
<template>
<div class="video-container">
<el-card shadow="hover">
<div class="video-header clearfix">
<div class="header-top">
<el-button :loading="uploadeFile_loading" type="primary" size="medium" @click="uploadeFile">
<i class="el-icon-upload2"></i>
<!-- 上传 -->
{{ $t('material.upload') }}
</el-button>
<!-- 新建文件夹 -->
<el-button type="primary" size="medium" @click="addFolder">
<i class="el-icon-plus"></i>
{{ $t('material.newFolder') }}
</el-button>
<!-- 重命名 -->
<el-button
v-show="activeFlag"
:disabled="activeFlagNotReame"
style="margin-left:10px"
type="primary"
size="medium"
@click="handleRenameFile"
>
<i class="el-icon-edit-outline"></i>
{{ $t('material.rename') }}
</el-button>
<!-- 移动到 -->
<el-button
v-show="activeFlag"
:disabled="activeFlagMoveble"
style="margin-left:10px"
type="primary"
size="medium"
@click="moveFolder"
>
<i class="el-icon-rank"></i>
{{ $t('material.moveTo') }}
</el-button>
<!-- 全选 -->
<el-button v-show="activeFlag" type="primary" style="margin-left:10px" size="medium" @click="toggleCheckAllSelect">
<i class="el-icon-check"></i>
{{ $t('material.selectAll') }}
</el-button>
<!-- 删除 -->
<el-button v-show="activeFlag" style="margin-left:10px" size="medium" @click="handleDeleteFile">
<i class="el-icon-delete"></i>
{{ $t('material.delete') }}
</el-button>
<div class="fr">
<el-radio-group v-model="resType" @change="queryMaterialPage">
<!-- 全部 -->
<el-radio label="-1">{{ $t('material.all') }}</el-radio>
<!-- 图片 -->
<el-radio label="1">{{ $t('material.image') }}</el-radio>
<!-- 应用 -->
<el-radio label="98">{{ $t('programModel.apply') }}</el-radio>
</el-radio-group>
<el-input
v-model="keyWord"
:placeholder="$t('common.name')"
style="width:150px;margin-left:10px;"
size="medium"
clearable
@clear="resetGetMaterialPage"
></el-input>
<!-- 查询 -->
<el-button size="medium" type="primary" style="margin-left:2px" icon="el-icon-search" @click="queryMaterialPage">{{ $t('material.search') }}</el-button>
</div>
</div>
<div class="breadcrumb">
<!-- 面包屑 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item v-for="(item,index) in breadcrumbList" :key="index">
<span
:class="(index === breadcrumbList.length-1) ? 'breadcrumb-link-active': ''"
class="breadcrumb-link"
@click="backFileFolder(item)"
>{{ item.name }}</span>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
</div>
<div
v-loading="loading"
class="video-main"
>
<ul class="list">
<li
v-for="(file,index) in files"
:key="index"
:class="{active:file.active}"
class="list-item"
@dblclick="dbClickOpenFile(file)"
>
<div class="inner">
<el-image
v-if="file.resType > 0"
:src="file.thumUrl%20?%20file.thumUrl%20:%20defultThumUrl"
class="icon-thumb"
fit="contain"
alt
></el-image>
<i v-else class="icon-folder"></i>
<div v-show="file.resType > 0" class="hover-cover">
<span style="color:#fff;">{{ file.size | sizeFilter }}</span>
</div>
</div>
<i class="icon-file-selected" @click="toggleSelect(file,file.id)"></i>
<div class="file-name">
<span :title="file.name">{{ file.name }}</span>
</div>
</li>
</ul>
<!-- 分页 -->
<el-pagination
:current-page="pageIndex"
:hide-on-single-page="true"
:page-sizes="[100, 200, 400, 600]"
:page-size="100"
:total="total"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
></el-pagination>
</div>
</el-card>
<!-- 重命名弹出框 -->
<el-dialog :title="$t('common.rename')" :visible.sync="renameDialogVisible" width="30%">
<el-form ref="renameForm" :model="renameForm" :rules="renameRules" style="margin-top:20px">
<el-form-item prop="name">
<el-input v-model="renameForm.name" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button size="medium" @click="renameDialogVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button :loading="rename_loading" type="primary" size="medium" @click="handleRenameConfirm">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
<!-- 上传组件 -->
<uploader-dialog :show.sync="showDialog" :upload-url="upfileUrl" :dir-id="parentId"></uploader-dialog>
<!-- 移动文件组件 -->
<move-file-dialog
:show.sync="moveFileDialog"
:ids="activeItems"
:parent-id="parentId"
@removeActiveItem="handleRemoveActiveItem"
></move-file-dialog>
<!-- 图片弹出放大组件 -->
<viewer-dialog
:show.sync="showImageDialog"
:src="imgSrc"
:img-title="imgTitle"
@closeImgDialog="imgDialogClose"
></viewer-dialog>
<!-- 视频播放组件 -->
<el-dialog :visible.sync="showlPlayVideo" :append-to-body="true" @close="closePlay">
<player :video-url="videoUrl" :state="state" :poster="poster"></player>
</el-dialog>
</div>
</template>
<script>
import Vue from 'vue'
// 引入上传子组件dialog
import uploaderDialog from './components/uploader-dialog'
// 引入移动文件子组件dialog
import moveFileDialog from './components/movefile-dialog'
// 引入图片子组件dialog
import viewerDialog from './components/viewer-dialog'
// 引入视频播放组件
import player from '@/components/Video-player'
export default {
name: 'Videomanagement',
components: {
uploaderDialog,
moveFileDialog,
viewerDialog,
player
},
filters: {
sizeFilter($bytesize) {
let $i = 0
while (Math.abs($bytesize) >= 1024) {
$bytesize = $bytesize / 1024
$i++
if ($i === 4) break
}
const $units = ['Bytes', 'KB', 'MB', 'GB', 'TB']
const $newsize = Math.round($bytesize, 2)
return $newsize + ' ' + $units[$i]
}
},
data() {
return {
uploadeFile_loading: false,
rename_loading: false,
defultThumUrl: '../../assets/svg/icon-file-thumbnail.svg', // 默认文件夹的样式图片
loading: false, // 加载loading样式
keyWord: '', // 查询关键字
resType: '-1', // 资源类型查询条件
id: 0, // 当前目录id
parentId: 0, // 当前目录的父文件夹id
upfileUrl: '', // 上传文件服务器的url地址
showDialog: false, // 是否弹出上传dialog
moveFileDialog: false, // 是否弹出移动文件dialog
showImageDialog: false, // 弹出大图浏览dialog
imgSrc: '', // 弹出大图的图片url地址
imgTitle: '', // 弹出大图的图片title
showlPlayVideo: false, // 弹出视频播放
videoUrl: '', // 视频播放地址
state: false, // 视频播放状态
poster: '', // 视频播放的封面
activeFlag: false, // 用来显示关闭删除,重命名,移动到按钮操作
activeFlagNotReame: false, // 当激活多个元素的时候,重命名操作灰掉
activeFlagMoveble: false, // 当激活选中的元素中有文件夹的时候,移动操作不可用,灰掉
activeItems: [], // 选中激活的文件列表
files: [], // 文件数据列表
folderListData: [], // 文件夹列表数据
breadcrumbList: [], // 面包屑中保存的文件夹数据
toggleCheckAllSelectFlag: false, // 全选样式切换
pageIndex: 1, // 分页参数-当前页
pageSize: 100, // 分页参数-分页大小
total: 0, // 分页参数-总条数
storagePercent: 0, // 存储空间容量百分比
maxStorage: 0, // 存储空间总容量
useStorage: 0, // 存储空间已用容量
renameDialogVisible: false,
renameForm: {
name: ''
},
renameRules: {
name: { required: true,
pattern: /^[^/\\\\:\\*\\?\\<\\>\\|\"]{1,255}$/,
message: this.$t('material.incorrectFileNameTips'), // 请输入正确的文件名称
trigger: 'blur'
}
}
}
},
computed: {
},
created() {
this.initMaterialPage()
this.getFolderList() // 获取文件夹列表数据
},
mounted() {},
methods: {
handleCurrentChange(val) {
this.pageIndex = val
this.initMaterialPage(this.parentId)
},
uploadeFile() {
this.uploadeFile_loading = true
// 上传文件
this.urlmethod(this.url.usermanagement.serverAddr_servers, null)
.then(res => {
this.upfileUrl = res.data.uploadUrl
this.showDialog = true
this.uploadeFile_loading = false
})
.catch(err => {
this.uploadeFile_loading = false
console.log('err', err)
this.loading = false
})
},
moveFolder() {
// 点击移动文件按钮,弹出移动文件dialog
this.moveFileDialog = true
},
toggleSelect(item, id) {
// 切换选择文件或文件夹的激活样式
this.id = id
if (item.active) {
Vue.set(item, 'active', false) // 为item添加不存在的属性,需要使用vue提供的Vue.set( object, key, value )方法
const index = this.activeItems.indexOf(id)
if (index > -1) {
this.activeItems.splice(index, 1)
}
} else {
Vue.set(item, 'active', true)
this.activeItems.push(id)
}
if (this.activeItems.length > 0) {
// 当有文件被激活时,显示重命名,删除,移动操作按钮
this.activeFlag = true
} else {
this.activeFlag = false
}
if (this.activeItems.length > 1) {
// 当有两个以上的文件被选中的时候,重命名,移动操作按钮不可用
this.activeFlagNotReame = true
} else {
this.activeFlagNotReame = false
}
this.activeFlagMoveble = true
this.files.forEach(item => {
if (item.active) {
if (item.resType !== 0) {
// 只要有一个不是文件夹的被选中,就可以移动
this.activeFlagMoveble = false
}
}
})
},
toggleCheckAllSelect() {
// 全选切换
this.activeFlagMoveble = !this.activeFlagMoveble // 全选移动操作可用,切换取反
if (!this.toggleCheckAllSelectFlag) {
this.files.forEach(item => {
if (!item.active) {
Vue.set(item, 'active', true)
}
})
this.toggleCheckAllSelectFlag = true
} else {
this.files.forEach(item => {
if (item.active) {
Vue.set(item, 'active', false)
}
})
this.toggleCheckAllSelectFlag = false
}
this.activeItems = []
this.files.forEach(item => {
if (item.active) {
this.activeItems.push(item.id)
if (item.resType !== 0) {
// 只要有一个不是文件夹的被选中,就可以移动
this.activeFlagMoveble = false
}
}
})
// 当没有选项被选中的时候
if (this.activeItems.length === 0) {
this.activeFlag = false
this.activeFlagNotReame = false
}
},
initMaterialPage(id = 0) {
// 初始化获取素材管理分页(可以通过传递文件夹id来获取对应的文件列表,默认值0,获取的是根目录)
this.loading = true
const para = {
data: {
pageIndex: this.pageIndex,
pageSize: this.pageSize,
parentId: id,
resType: Number(this.resType),
keyWord: this.keyWord
}
}
this.urlmethod(this.url.material.list_res, para)
.then(res => {
this.files = [...res.data.list]
this.loading = false
this.total = res.data.recordTotal
this.activeItems = [] // 激活选项空
this.activeFlag = false // 可操作按钮隐藏
this.activeFlagMoveble = false // 恢复能不能移动的初始值
})
.catch(err => {
console.log('err', err)
this.loading = false
})
},
queryMaterialPage() {
// 通过条件筛选查询获取文件列表,也需要把当前文件夹的id传递进去
this.initMaterialPage(this.parentId)
},
resetGetMaterialPage() {
// 点击搜索框清楚按钮,重新获取列表数据
// 把当前的文件夹id传进去
this.initMaterialPage(this.parentId)
},
getFolderList() {
// 获取文件夹列表数据
this.urlmethod(this.url.material.selectDir_res, null)
.then(res => {
this.folderListData.push(res.data)
const temObj = {}
temObj.id = this.folderListData[0].id
temObj.name = this.folderListData[0].name
temObj.parentId = this.folderListData[0].parentId
this.breadcrumbList.push(temObj) // 这里只保存了根目录文件夹
})
.catch(err => {
console.log('err', err)
this.loading = false
})
},
backFileFolder(item) {
// 点击面包屑回退到对应的文件夹级别,并且分页的索引值也需要退回到第一页
this.pageIndex = 1
const index = this.breadcrumbList.indexOf(item)
const length = this.breadcrumbList.length
if (item.id === this.breadcrumbList[length - 1].id) {
// 点击的元素id是最后一个文件id,不能删除
} else {
this.breadcrumbList.splice(index + 1)
}
this.initMaterialPage(item.id) // 点击面包屑中的哪个文件夹就获取对应的文件列表页
this.parentId = item.id // 更新当前界面的父文件夹id
},
addFolder() {
// 新增文件夹
this.$prompt(null, this.$t('common.add'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
inputPlaceholder: this.$t('material.enterFolderNameTips'),
inputPattern: /^[^/\\\\:\\*\\?\\<\\>\\|\"]{1,255}$/, // 文件名正则
inputErrorMessage: this.$t('material.incorrectFileNameTips') // 文件名格式不正确
})
.then(({ value }) => {
const para = {
data: {
name: value,
parentId: this.parentId
}
}
this.urlmethod(this.url.material.addDir_res, para)
.then(() => {
this.initMaterialPage(this.parentId)
this.$message({
type: 'success',
message: this.$t('material.newFolderNameIs') + value // 新的文件名是
})
})
.catch(err => {
console.log('err', err)
})
})
.catch(() => {
this.$message({
type: 'info',
message: this.$t('common.canceled')
})
})
},
// 打开重命名弹窗
handleRenameFile() {
// 文件重命名操作
// 当激活项里只有唯一的一个元素,遍历files,通过id就可以找到该项需要重命名的name
this.files.forEach(v => {
if (this.activeItems.indexOf(v.id) === 0) {
this.renameForm.name = v.name
}
})
this.renameDialogVisible = true
},
// 重命名弹窗确定提交
handleRenameConfirm() {
this.$refs['renameForm'].validate((valid) => {
if (valid) {
this.rename_loading = true
const para = {
data: {
id: this.id,
name: this.renameForm.name
}
}
this.urlmethod(this.url.material.reName_res, para)
.then(() => {
// 重命名成功后重新获取列表
this.initMaterialPage(this.parentId)
this.$message({
type: 'success',
message: this.$t('material.newFolderNameIs') + this.renameForm.name
})
this.renameDialogVisible = false
this.rename_loading = false
})
.catch(err => {
this.rename_loading = false
console.log('err', err)
})
} else {
return false
}
})
},
handleDeleteFile() {
// 删除文件
this.$confirm(this.$t('common.tipsBeforeDelete'), this.$t('common.tips'), {
confirmButtonText: this.$t('common.confirm'),
cancelButtonText: this.$t('common.cancel'),
type: 'warning'
})
.then(() => {
this.loading = true
// 执行删除文件的api
const para = {
data: {
ids: this.activeItems
}
}
this.urlmethod(this.url.material.delete_res, para)
.then(() => {
// 删除成功后重新获取列表,注意,这是不是this.id,而是父id,因为点击文件夹了,当前的id已经发生了改变
this.initMaterialPage(this.parentId)
this.activeItems = [] // 激活选项空
this.activeFlag = false // 可操作按钮隐藏
this.$message({
type: 'success',
message: this.$t('common.deleteSuccess')
})
this.loading = false
})
.catch(err => {
this.loading = false
console.log('err', err)
})
})
.catch(() => {
this.$message({
type: 'info',
message: this.$t('common.canceled')
})
})
},
handleRemoveActiveItem(removeId) {
// 子组件把文件移动位置成功后触发这个绑定的事件处理函数
this.activeItems = [] // 激活选项空
this.activeFlag = false // 可操作按钮隐藏
},
dbClickOpenFile(file) {
// 双击文件操作
// 双击后记住当前文件的id
this.id = file.id
if (file.resType === 0) {
// 打开的是文件夹
const temObj = {}
temObj.id = file.id
temObj.name = file.name
temObj.parentId = file.parentId
this.breadcrumbList.push(temObj) // 双击文件夹把当前文件夹的信息push到面包屑列表中
this.parentId = file.id // 进入文件夹后,此时当前的父id就应该修改为双击文件夹的那个id
this.initMaterialPage(this.id)
} else if (file.resType === 1) {
// 打开的是图片
this.showImageDialog = true
this.imgSrc = file.url
this.imgTitle = file.name
} else if (file.resType === 2 || file.resType === 99) {
// 打开的是视频
this.showlPlayVideo = true
this.videoUrl = file.url
this.state = true
this.poster = file.thumUrl
} else {
// 其他文件的操作
// 暂不支持
}
},
closePlay() {
// 关闭视频播放弹窗
this.state = false
this.videoUrl = ''
},
imgDialogClose() {
// 子组件发出图片弹窗关闭,这里需要清空传入的图片地址,fix在慢网速的情况下,会先看到上一次的图片。
this.imgSrc = ''
}
}
}
</script>
<style lang="scss" scoped>
.clearfix:after {
content: '';
display: block;
clear: both;
}
.video-container {
min-width: 630px;
margin: 10px;
.video-header {
padding: 0 0 5px 0;
border-bottom: 1px solid #dbdbdb;
.breadcrumb {
// float: left;
height: 20px;
margin-top: 10px;
.breadcrumb-link {
cursor: pointer;
//font-size: 16px;
}
.breadcrumb-link:hover {
color: #409eff;
text-decoration: underline;
}
.breadcrumb-link-active {
// 面包屑当前激活文件夹的样式
font-weight: 700;
}
}
.header-top {
// float: right;
height: 40px;
line-height: 40px;
position: relative;
}
}
.video-main {
.list-item {
border: 1px solid #fff;
box-sizing: border-box;
position: relative;
height: 100px;
width: 80px;
// background-color: green;
margin: 5px;
display: inline-block;
cursor: pointer;
.inner {
height: 60px;
width: 60px;
// background-color: red;
margin: 5px 10px;
// padding: 10px 15px 10px 15px;
.icon-folder {
// 文件夹的样式
display: inline-block;
width: 60px;
height: 60px;
background-image: url(../../assets/svg/icon-file-close.svg);
background-size: 100% 100%;
}
.icon-thumb {
// 文件的样式
width: 60px;
height: 60px;
}
}
.file-name {
// 文件夹name样式
padding-left: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #424e67;
font-size: 12px;
}
.file-name:hover {
color: #409eff;
}
}
.hover-cover {
width: 60px;
height: 60px;
position: absolute;
left: 10px;
top: 5px;
background-color: rgb(0,0,0);
opacity: 0;
text-align: center;
line-height: 60px;
font-size: 12px;
}
.list-item:hover {
background-color: #f1f5fa;
.icon-file-selected {
opacity: 0.5;
}
.hover-cover {
opacity: 0.6;
}
}
.icon-file-selected {
// 小圆点默认样式
position: absolute;
left: 5px;
top: 5px;
display: inline-block;
width: 20px;
height: 20px;
background-size: 100% 100%;
background-image: url(../../assets/svg/icon-file-selected.svg);
opacity: 0;
}
.icon-file-selected:hover {
// 小圆点hover
opacity: 1 !important;
}
.active {
// 选择文件触发激活样式
border: 1px solid #409eff;
border-radius: 8px;
.icon-file-selected {
position: absolute;
left: 5px;
top: 5px;
display: inline-block;
width: 20px;
height: 20px;
background-size: 100% 100%;
background-image: url(../../assets/svg/icon-file-selected.svg);
opacity: 1;
}
}
.active:hover {
// 激活状态小圆点透明度1
.icon-file-selected {
opacity: 1 !important;
}
}
.loadding-message {
// 加载loading的文字样式
color: #424e67;
font-size: 12px;
text-align: center;
}
}
}
</style>
viewer-dialog.vue
<!-- 图片预览组件dailog形式 -->
<template>
<div class="viewer-dialog-wrap">
<el-dialog
:visible.sync="visible"
:title="imgTitle"
:show="show"
custom-class="dialog-width"
@close="$emit('update:show', false)"
>
<img :src="src" alt />
<!-- <div slot="footer" class="dialog-footer">{{imgTitle}}</div> -->
</el-dialog>
</div>
</template>
<script>
export default {
name: 'ViewerDialog',
filters: {},
props: {
id: {
type: String,
default: ''
},
show: {
type: Boolean,
default: false
},
src: {
type: String,
default: ''
},
imgTitle: {
type: String,
default: ''
}
},
data() {
return {
visible: this.show
}
},
computed: {},
watch: {
show(val, oldVal) {
if (val === oldVal) {
return
}
this.visible = val
},
// 如果内部有新值变化,更新外部的visible
visible(val, oldVal) {
if (val === oldVal) {
return
}
this.$emit('update:visible', val)
if (val === false) {
// 当关闭弹窗的时候,向父组件传递清空src的消息
this.$emit('closeImgDialog')
}
}
},
created() {},
mounted() {},
methods: {
inited(viewer) {
this.$viewer = viewer
}
}
}
</script>
<style lang="scss">
.viewer-dialog-wrap {
.dialog-width {
img {
width: 100%;
height: 100%;
}
}
}
</style>
uploader-dialog.vue
<!-- 素材管理上传功能弹出页面 -->
<template>
<div class="uploader-dialog-wrap">
<el-dialog
:title="title"
:visible.sync="visible"
:show="show"
:show-close="false"
:close-on-click-modal="false"
custom-class="dialog-width"
@close="$emit('update:show', false)"
>
<uploader
v-if="visible"
ref="uploader"
:options="options"
:file-status-text="fileStatusText"
:auto-start="true"
class="uploader-example"
@file-added="onFileAdded"
@file-progress="onFileProgress"
@file-success="onFileSuccess"
@file-error="onFileError"
>
<uploader-unsupport></uploader-unsupport>
<uploader-btn ref="uploadBtn" :attrs="attrs" class="global-uploader-btn">{{ $t('material.selectFile') }}</uploader-btn>
<uploader-list></uploader-list>
<div class="warning-message"><i class="el-icon-warning" style="color:#E6A23C;font-size:20px;"></i>{{ $t('material.uploadTips') }}</div>
</uploader>
<div slot="footer" class="dialog-footer">
<el-button :title="$t('common.nocompleteWillEmpty')" @click="close">{{ $t('common.close') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getCookie } from '@/prototypeEx/cacheEx'
export default {
filters: {},
props: {
dirId: {
// 上传文件夹id
type: Number,
default: 0
},
show: {
type: Boolean,
default: false
},
uploadUrl: {
type: String,
default: '',
required: true
}
},
data() {
return {
selectFile_loading: false,
visible: this.show,
title: this.$t('common.upload'),
dialogLoading: false,
MineType: [
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
'image/webp',
'application/x-msdownload',
'application/vnd.android.package-archive'
], // 定义可接受的文件类型
options: {
target: '/', // 目标上传 URL
testChunks: true, // 是否开启服务器分片校验,是否测试每个块是否在服务端已经上传了,主要用来实现秒传、跨浏览器上传等,默认 true
chunkSize: this.config.uploader_chunk_size, // 分块时按照该值来分10kb
fileParameterName: 'file', // 上传文件时文件的参数名,默认 file
maxChunkRetries: 3, // 最大自动失败重试上传次数
query: {
token: '', // 从vuex中拿到的token
dirId: this.dirId // 上传目标文件夹
},
checkChunkUploadedByResponse: function(chunk, message) {
// 服务器分片校验函数,秒传及断点续传基础,用于根据 XHR 响应内容检测每个块是否上传成功了
// let objMessage = JSON.parse(message);
// if (objMessage.skipUpload) {
// return true;
// }
// return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0;
},
simultaneousUploads: 3 // 并发上传数,默认 3
// singleFile: true 是否开启单文件上传
// headers: {
// token: "eddd7f17-f352-4d25-8bd6-541ef72e446e"
// }
},
attrs: {
accept:
'image/gif,image/png,image/jpg,image/jpeg,image/webp,application/octet-stream,.apk'
},
fileStatusText: {
success: this.$t('common.success'),
error: this.$t('common.error'),
uploading: this.$t('material.uploading'),
paused: this.$t('common.pause'),
waiting: this.$t('common.wait')
}
}
},
computed: {
uploader() {
return this.$refs.uploader.uploader // 实例化上传对象
}
},
watch: {
show(val, oldVal) {
if (val === oldVal) {
return
}
if (val) {
this.initData()
}
this.visible = val
},
// 如果内部有新值变化,更新外部的visible
visible(val, oldVal) {
if (val === oldVal) {
return
}
this.$emit('update:visible', val)
}
},
created() {
},
mounted() {
this.options.query.token = getCookie('token') // 加载后把vuex中的token赋值给上传组件的options
},
methods: {
// 初始化上传地址问题
initData() {
this.options.target = this.uploadUrl
this.options.query.dirId = this.dirId
},
createUUID(file) {
// 生成文件的UUID作为唯一的标识符
// file.pause(); // 文件上传先暂停
const s = []
const hexDigits = '0123456789abcdef'
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'
const uuid = s.join('')
file.uniqueIdentifier = uuid
// file.resume(); // 文件继续上传
},
onFileAdded(file) {
// 选择文件后
// this.computeMD5(file);
this.createUUID(file)
const index = this.MineType.indexOf(file.fileType)
if (index === -1) {
// 不在MINE自定义的文件格式类型中,不让上传;
file.ignored = true
}
},
onFileProgress(rootFile, file, chunk) {
// 上传中的回调函数
},
onFileSuccess(rootFile, file, response, chunk) {
// 文件上传成功后的回调
const res = JSON.parse(response)
// console.log(res);
if (res.success === 1) {
const para = {
data: {
parentId: this.dirId,
name: file.name,
identifier: res.data.identifier,
thumPic: res.data.t_identifier,
size: file.size,
resType: res.data.resType,
duration: res.data.duration,
serverCode: res.data.serverCode
}
}
this.urlmethod(this.url.material.add_res, para)
.then(res => {
})
.catch(err => {
console.log(err)
})
// 上传成功
} else {
this.$message({
message: res.errMsg,
type: 'warning'
})
}
// 服务器自定义的错误(即虽返回200,但是是错误的情况),这种错误是Uploader无法拦截的
// if (!res.result) {
// this.$message({ message: res.message, type: "error" });
// //文件状态设为“失败”
// this.statusSet(file.id, "failed");
// return;
// }
// 如果服务端返回需要合并
// if (res.needMerge) {
// // 文件状态设为“合并中”
// this.statusSet(file.id, "merging");
// api
// .mergeSimpleUpload({
// tempName: res.tempName,
// fileName: file.name,
// ...this.params
// })
// .then(res => {
// // 文件合并成功
// // Bus.$emit("fileSuccess");
// this.statusRemove(file.id);
// })
// .catch(e => {});
// 不需要合并
// } else {
// // Bus.$emit("fileSuccess");
// console.log("上传成功");
// }
},
onFileError(rootFile, file, response, chunk) {
// 错误的回调
console.log(response)
// this.$message({
// message: response,
// type: "error"
// });
},
close() {
// 点击确认关闭上传框
this.uploader.cancel() // 点击关闭按钮后清空上传对象
// 这里调用父组件重新获取数据刷新列表的方法,重新获取容量
this.$parent.initMaterialPage(this.dirId)
this.visible = false
}
}
}
</script>
<style lang="scss" scoped>
.uploader-dialog-wrap {
.warning-message {
margin-top: 10px;
color: #F56C6C;
vertical-align: middle;
}
}
</style>
<style lang="scss">
.uploader-dialog-wrap {
.el-dialog {
.el-dialog__header {
// background-color: #42b983;
.el-dialog__title {
color: #333;
font-size: 24px;
}
.el-dialog__headerbtn .el-dialog__close {
color: #333;
}
}
}
.dialog-width {
min-width: 45rem !important;
}
.uploader-example {
// 上传组件外壳样式
// width: 880px;
padding: 15px;
// margin: 40px auto 0;
font-size: 12px;
// box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
}
.uploader-example .uploader-btn {
// 上传按钮样式
background-color: #409eff;
color: #fff;
border-color: #409eff;
padding: 7px 13px;
border-radius: 3px;
margin-bottom: 10px;
}
.uploader-example .uploader-btn:hover {
// 按钮hover样式
background-color: #66b1ff;
border-color: #66b1ff;
}
.el-button--danger {
background-color: #409EFF;
border-color: #409EFF;
&:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
}
.uploader-example .uploader-list {
// 上传文件列表样式
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
// 覆盖掉默认的文件图标样式
.uploader-file-icon[icon='image']:before {
content: '';
}
.uploader-file-icon[icon='image'] {
background: url(../../../assets/image-icon.png);
}
.uploader-file-icon[icon='document']:before {
content: '';
}
.uploader-file-icon[icon='document'] {
background: url(../../../assets/text-icon.png);
}
.el-dialog__body {
padding: 5px;
}
}
</style>
player.vue
<template>
<div class="video-container-wrap">
<div class="player">
<video-player
ref="videoPlayer"
:playsinline="false"
:options="playerOptions"
class="video-player vjs-custom-skin"
@play="onPlayerPlay($event)"
@pause="onPlayerPause($event)"
@statechanged="playerStateChanged($event)"
></video-player>
</div>
</div>
</template>
<script>
// 引入样式
import { videoPlayer } from 'vue-video-player'
import 'video.js/dist/video-js.css'
export default {
components: {
videoPlayer
},
props: {
videoUrl: { type: String, default: '' },
state: { type: Boolean, default: false },
poster: { type: String, default: '' }
},
data() {
return {
playerOptions: {
autoplay: false, // 如果true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 导致视频一结束就重新开始。
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'en',
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [
{
type: 'video/mp4',
src: this.videoUrl // 你的m3u8地址(必填)
}
],
poster: this.poster,
// poster: 'https://surmon-china.github.io/vue-quill-editor/static/images/surmon-3.jpg', // 你的封面地址
width: document.documentElement.clientWidth,
notSupportedMessage: this.$t('material.noPlay') // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
}
}
},
computed: {
player() {
return this.$refs.videoPlayer.player
}
},
watch: {
// 更改视频源 videoUrl从弹出框组件传值
videoUrl(val, oldVal) {
if (val === oldVal) {
return
}
if (val !== '') {
this.playerOptions.sources[0].src = val
}
},
// 弹出框关闭后暂停 否则一直在播放 state从弹出框组件传值
state(val, oldVal) {
if (val === oldVal) {
return
}
this.$refs.videoPlayer.player.pause()
},
poster(val, oldVal) {
// 检测它的变化,来改变视频封面
if (val === oldVal) {
return
}
this.playerOptions.poster = val
}
},
methods: {
onPlayerPlay(player) {},
onPlayerPause(player) {},
playerStateChanged(player) {}
}
}
</script>
<style lang="scss">
.video-container-wrap {
.player {
.video-js .vjs-big-play-button {
/*
播放按钮换成圆形
*/
height: 2em;
width: 2em;
line-height: 2em;
border-radius: 1em;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
</style>
movefile-dialog.vue
<!-- 素材管理移动文件功能弹出页面 -->
<template>
<div class="movefile-dialog-wrap">
<el-dialog
:title="title"
:visible.sync="visible"
:show="show"
custom-class="dialog-width"
@close="$emit('update:show', false)"
>
<el-tree
ref="tree"
:data="folderListData"
:props="defaultProps"
:check-strictly="true"
show-checkbox
node-key="id"
@check-change="orgCheckChange"
></el-tree>
<div slot="footer" class="dialog-footer">
<el-button size="medium" @click="handleClose">{{ $t('common.cancel') }}</el-button>
<el-button :loading="move_loading" type="primary" size="medium" @click="handleMoveFile">{{ $t('common.confirm') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
filters: {},
props: {
ids: {
// 当前点击需要移动操作的文件id数组集合
type: Array,
default: function() {
return []
}
},
parentId: {
// 当前移动操作文件所属的文件夹id
type: Number,
default: 0
},
show: {
type: Boolean,
default: false
}
},
data() {
return {
move_loading: false,
visible: this.show,
title: this.$t('material.moveTo'),
dialogLoading: false,
folderListData: [], // 文件夹列表数据
defaultProps: {
children: 'children',
label: 'name'
},
selectOrg: {
orgsid: []
}
}
},
computed: {},
watch: {
show(val, oldVal) {
if (val === oldVal) {
return
}
this.visible = val
this.handleFechtUserFolderList() // 每次打开对话框,都去重新获取文件夹列表接口
},
// 如果内部有新值变化,更新外部的visible
visible(val, oldVal) {
if (val === oldVal) {
return
}
this.$emit('update:visible', val)
},
parentId(val, oldVal) {
if (val === oldVal) {
return
}
// 监测父组件那边传过来的上传文件夹id的变化, 如果有改变,就更新options中的目标上传id
this.parentId = val
}
},
created() {
},
mounted() {},
methods: {
handleFechtUserFolderList() {
// 处理获取文件夹列表
this.loading = true
this.urlmethod(this.url.material.selectDir_res, null)
.then(res => {
this.folderListData = []
this.folderListData.push(res.data)
this.loading = false
})
.catch(err => {
console.log('err', err)
this.loading = false
})
},
handleMoveFile() {
// 执行移动文件的操作
// this.id就是传递过来的需要操作的文件id
if (this.selectOrg.orgsid.length === 1) {
this.move_loading = true
const para = {
data: {
ids: this.ids, // 移动文件的id
parentId: this.selectOrg.orgsid[0] // 移动到的目标文件夹id
}
}
this.urlmethod(this.url.material.remove_res, para)
.then(() => {
// 移动文件操作成功
this.$message({
type: 'success',
message: this.$t('common.moveSuccess')
})
this.move_loading = false
this.visible = false
this.$emit('removeActiveItem', this.id)
// 这里调用父组件重新获取数据刷新列表的方法
this.$parent.initMaterialPage(this.parentId)
})
.catch(err => {
this.move_loading = false
console.log('err', err)
this.loading = false
})
}
},
// check-change
// 节点选中状态发生变化时的回调
// 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
orgCheckChange(data, checked, indeterminate) {
// 获取当前选择的id在数组中的索引
const indexs = this.selectOrg.orgsid.indexOf(data.id)
// 如果不存在数组中,并且数组中已经有一个id并且checked为true的时候,代表不能再次选择。
if (indexs < 0 && this.selectOrg.orgsid.length === 1 && checked) {
// this.$message({
// message: this.$t('esell.onlyOneSelect'),
// type: 'warning',
// showClose: true
// })
// 设置已选择的节点为false 很重要
this.$refs.tree.setCheckedKeys([])
this.$refs.tree.setChecked(data, true)
this.selectOrg.orgsid = []
this.selectOrg.orgsid.push(data.id)
} else if (this.selectOrg.orgsid.length === 0 && checked) {
// 发现数组为空 并且是已选择
// 防止数组有值,首先清空,再push
this.selectOrg.orgsid = []
this.selectOrg.orgsid.push(data.id)
} else if (
indexs >= 0 &&
this.selectOrg.orgsid.length === 1 &&
!checked
) {
// 再次直接进行赋值为空操作
this.selectOrg.orgsid = []
}
},
handleClose() {
// 点击取消按钮
this.visible = false
}
}
}
</script>
<style lang="scss">
.movefile-dialog-wrap {
.el-dialog {
border-radius: 6px;
.el-dialog__header {
border-bottom: 1px solid #d0dfe7;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
}
.dialog-width {
min-width: 45rem !important;
}
}
</style>
uploader-dialog.vue
<!-- 上传功能弹出页面 -->
<template>
<div class="uploader-dialog-wrap">
<el-dialog
:title="title"
:visible.sync="visible"
:show="show"
:show-close="false"
:close-on-click-modal="false"
custom-class="dialog-width"
@close="$emit('update:show', false)"
>
<uploader
v-if="visible"
ref="uploader"
:options="options"
:file-status-text="fileStatusText"
:auto-start="true"
class="uploader-example"
@file-added="onFileAdded"
@file-progress="onFileProgress"
@file-success="onFileSuccess"
@file-error="onFileError"
>
<uploader-unsupport></uploader-unsupport>
<uploader-btn ref="uploadBtn" :attrs="attrs" class="global-uploader-btn">{{ $t('admanagement.selectFile') }}</uploader-btn>
<uploader-list></uploader-list>
<div v-if="selectType==0" class="warning-message"><i class="el-icon-warning" style="color:#E6A23C;font-size:20px;"></i> {{ $t('admanagement.uploadTips') }}</div>
<div v-if="selectType==1" class="warning-message"><i class="el-icon-warning" style="color:#E6A23C;font-size:20px;"></i> {{ $t('admanagement.uploadTips3') }}</div>
</uploader>
<div slot="footer" class="dialog-footer">
<el-button :title="$t('admanagement.nocompleteWillEmpty')" @click="close">{{ $t('common.close') }}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
filters: {},
props: {
dirId: {
// 上传文件夹id
type: Number,
default: 0
},
show: {
type: Boolean,
default: false
},
bigFiles: {
type: Number,
default: 0
},
exsitFileNum: {
type: Number,
default: 0
},
uploadUrl: {
type: String,
default: '',
required: true
},
selectType: { // 1代表只能选图片
type: Number,
default: 0
}
},
data() {
return {
isComing: false,
upload_loading: true,
addFiles: 0,
imageVideoList: [],
visible: this.show,
title: this.$t('common.upload'),
token: this.cacheEx.getCookie('token'),
dialogLoading: false,
MineType: [
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
'image/webp',
'video/mp4',
'video/x-ms-wmv',
'video/avi',
'video/quicktime', // mov格式
'video/3gpp', // 3gp格式
'video/x-matroska' // mkv格式
], // 定义可接受的文件类型
options: {
target: '/', // 目标上传 URL
testChunks: true, // 是否开启服务器分片校验,是否测试每个块是否在服务端已经上传了,主要用来实现秒传、跨浏览器上传等,默认 true
chunkSize: this.config.uploader_chunk_size, // 分块时按照该值来分10kb
fileParameterName: 'file', // 上传文件时文件的参数名,默认 file
maxChunkRetries: 3, // 最大自动失败重试上传次数
query: {
token: '', // 从vuex中拿到的token
dirId: this.dirId, // 上传目标文件夹
refCount: 0
},
checkChunkUploadedByResponse: function(chunk, message) {
// 服务器分片校验函数,秒传及断点续传基础,用于根据 XHR 响应内容检测每个块是否上传成功了
// let objMessage = JSON.parse(message);
// if (objMessage.skipUpload) {
// return true;
// }
// return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0;
},
simultaneousUploads: 3 // 并发上传数,默认 3
// singleFile: true 是否开启单文件上传
// headers: {
// token: "eddd7f17-f352-4d25-8bd6-541ef72e446e"
// }
},
attrs: {
accept:
'image/gif,image/png,image/jpg,image/jpeg,image/webp,.mp4,.mkv,.wmv,.avi,.mov,.3gp'
},
fileStatusText: {
success: this.$t('accountManage.sucess'),
error: this.$t('common.error'),
uploading: this.$t('admanagement.uploading'),
paused: this.$t('admanagement.pause'),
waiting: this.$t('admanagement.wait')
}
}
},
computed: {
uploader() {
return this.$refs.uploader.uploader // 实例化上传对象
}
},
watch: {
show(val, oldVal) {
if (val === oldVal) {
return
}
if (val) {
this.addFiles = 0
if (this.selectType == 1) {
this.MineType = [
'image/png',
'image/jpg',
'image/jpeg',
'image/gif',
'image/webp'
]
}
this.initData()
}
this.visible = val
},
// 如果内部有新值变化,更新外部的visible
visible(val, oldVal) {
if (val === oldVal) {
return
}
this.$emit('update:visible', val)
}
},
created() {
},
mounted() {
this.options.query.token = this.token // 加载后把vuex中的token赋值给上传组件的options
},
methods: {
// 初始化上传地址问题
initData() {
this.imageVideoList = []
this.options.target = this.uploadUrl
this.options.query.dirId = this.dirId
},
createUUID(file) {
// 生成文件的UUID作为唯一的标识符
// file.pause(); // 文件上传先暂停
const s = []
const hexDigits = '0123456789abcdef'
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'
const uuid = s.join('')
file.uniqueIdentifier = uuid
// file.resume(); // 文件继续上传
},
onFileAdded(file) {
if (this.exsitFileNum + (++this.addFiles) > this.bigFiles) {
file.ignored = true
if (!this.isComing) {
this.$message({
message: this.$t('admanagement.nomorethan5') + (this.bigFiles - this.exsitFileNum) + this.$t('admanagement.per'),
type: 'warning'
})
this.isComing = true
}
return
}
// 选择文件后
// this.computeMD5(file);
this.createUUID(file)
const index = this.MineType.indexOf(file.fileType)
if (index === -1) {
// 不在MINE自定义的文件格式类型中,不让上传;
file.ignored = true
}
if (file.size > 1024 * 1024 * 1024 * 1024) { // 5 * 1024 * 1024 * 1000
file.ignored = true
this.$message({
message: this.$t('admanagement.uploadTips2'),
type: 'warning'
})
}
},
onFileProgress(rootFile, file, chunk) {
// 上传中的回调函数
},
onFileSuccess(rootFile, file, response, chunk) {
// 文件上传成功后的回调
const res = JSON.parse(response)
// console.log(res);
if (res.success === 1) {
res.data.fileName = file.name
this.imageVideoList.push(res.data)
// 上传成功
} else {
this.$message({
message: res.errMsg,
type: 'warning'
})
}
// 服务器自定义的错误(即虽返回200,但是是错误的情况),这种错误是Uploader无法拦截的
// if (!res.result) {
// this.$message({ message: res.message, type: "error" });
// //文件状态设为“失败”
// this.statusSet(file.id, "failed");
// return;
// }
// 如果服务端返回需要合并
// if (res.needMerge) {
// // 文件状态设为“合并中”
// this.statusSet(file.id, "merging");
// api
// .mergeSimpleUpload({
// tempName: res.tempName,
// fileName: file.name,
// ...this.params
// })
// .then(res => {
// // 文件合并成功
// // Bus.$emit("fileSuccess");
// this.statusRemove(file.id);
// })
// .catch(e => {});
// 不需要合并
// } else {
// // Bus.$emit("fileSuccess");
// console.log("上传成功");
// }
},
onFileError(rootFile, file, response, chunk) {
// 错误的回调
console.log(response)
// this.$message({
// message: response,
// type: "error"
// });
},
close() {
// 点击确认关闭上传框
this.uploader.cancel() // 点击关闭按钮后清空上传对象
this.$emit('adPackageImaListChosed', this.imageVideoList)
this.imageVideoList = []
this.visible = false
}
}
}
</script>
<style lang="scss" scoped>
.uploader-dialog-wrap {
.warning-message {
margin-top: 10px;
color: #F56C6C;
vertical-align: middle;
}
}
</style>
<style lang="scss">
.uploader-dialog-wrap {
.el-dialog {
.el-dialog__header {
// background-color: #42b983;
.el-dialog__title {
color: #333;
font-size: 24px;
}
.el-dialog__headerbtn .el-dialog__close {
color: #333;
}
}
}
.dialog-width {
min-width: 45rem !important;
}
.uploader-example {
// 上传组件外壳样式
// width: 880px;
padding: 15px;
// margin: 40px auto 0;
font-size: 12px;
// box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
}
.uploader-example .uploader-btn {
// 上传按钮样式
background-color: #409eff;
color: #fff;
border-color: #409eff;
padding: 7px 13px;
border-radius: 3px;
margin-bottom: 10px;
}
.uploader-example .uploader-btn:hover {
// 按钮hover样式
background-color: #66b1ff;
border-color: #66b1ff;
}
.el-button--danger {
background-color: #409EFF;
border-color: #409EFF;
&:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
}
.uploader-example .uploader-list {
// 上传文件列表样式
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
// 覆盖掉默认的文件图标样式
.uploader-file-icon[icon='image']:before {
content: '';
}
.uploader-file-icon[icon='image'] {
background: url(../../assets/image-icon.png);
}
.uploader-file-icon[icon='document']:before {
content: '';
}
.uploader-file-icon[icon='document'] {
background: url(../../assets/text-icon.png);
}
.el-dialog__body {
padding: 5px;
}
}
</style>
index.vue
<template>
<div class="video-container-wrap">
<div class="player" >
<video-player
ref="videoPlayer"
:playsinline="false"
:options="playerOptions"
class="video-player vjs-custom-skin"
@play="onPlayerPlay($event)"
@pause="onPlayerPause($event)"
@statechanged="playerStateChanged($event)"
></video-player>
</div>
</div>
</template>
<script>
import { videoPlayer } from 'vue-video-player'
import 'video.js/dist/video-js.css'
export default {
components: {
videoPlayer
},
props: {
videoUrl: { type: String, default: '' },
state: { type: Boolean, default: false },
poster: { type: String, default: '' },
playType: { type: String, default: 'video/mp4' }
},
data() {
return {
playerOptions: {
autoplay: false, // 如果true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 导致视频一结束就重新开始。
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'en',
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [
{
type: this.playType,
src: this.videoUrl // 你的m3u8地址(必填)
}
],
poster: this.poster,
width: document.documentElement.clientWidth,
notSupportedMessage: this.$t('esell.noPlay') // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
}
}
},
computed: {
player() {
return this.$refs.videoPlayer.player
}
},
watch: {
// 更改视频源 videoUrl从弹出框组件传值
videoUrl(val, oldVal) {
if (val === oldVal) {
return
}
if (val !== '') {
this.playerOptions.sources[0].src = val
}
},
// 弹出框关闭后暂停 否则一直在播放 state从弹出框组件传值
state(val, oldVal) {
if (val === oldVal) {
return
}
this.$refs.videoPlayer.player.pause()
},
poster(val, oldVal) {
// 检测它的变化,来改变视频封面
if (val === oldVal) {
return
}
this.playerOptions.poster = val
}
},
methods: {
onPlayerPlay(player) {},
onPlayerPause(player) {},
playerStateChanged(player) {}
}
}
</script>
<style lang="scss">
.video-container-wrap {
.player {
.video-js .vjs-big-play-button {
/*
播放按钮换成圆形
*/
height: 2em;
width: 2em;
line-height: 2em;
border-radius: 1em;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
</style>
版权声明:本文为u012174809原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。