| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025 |
- <template>
- <div class="pc-player-container">
- <div class="pc-player-wrapper">
- <div class="pc-player-left">
- <div class="video-section">
- <div class="video-title">{{ media.name || tpl.name }}</div>
- <div class="video-player-wrap">
- <video-player v-if="options.sources.length" id="myVideo1" ref="videoPlayer" :playsinline="true" @pause="onPlayerPause($event)"
- @timeupdate="onPlayerTimeupdate($event)" @play="onPlayerStart($event)" @ready="playerReadied"
- @ended="onPlayerEnded($event)" @error="onPlayerError($event)"
- :globalOptions="{controls:true}" :options="options">
- </video-player>
- </div>
- <div class="dialog-footer pt30">
- <el-row class="media-footer">
- <el-col :span="6" class="media-time">
- <span>{{curTimes|useTime}}</span>
- <strong>/</strong>
- <span>{{media.duration|useTime}}</span>
- </el-col>
- <el-col :span="12" class="media-center">
- <span class="volume-control">
- <span class="vol-icon" @click="toggleMute">音量</span>
- <el-slider class="volume-slider" :value="volume" :min="0" :max="1" :step="0.05"
- @input="setVolume"></el-slider>
- </span>
- <span class="pip-control">
- <span class="pip-icon" :class="{ 'is-active': isPiP }" @click="togglePiP">{{ isPiP ? '退出画中画' : '画中画' }}</span>
- </span>
- </el-col>
- <el-col :span="6" class="media-select">
- <div style="margin-top:0px;float: right;">
- <el-select placeholder="流畅" :value="mediaType" class="media-el-select" @change="changeMedia">
- <el-option label="流畅" value="ld"></el-option>
- <el-option label="标清" value="hls"></el-option>
- </el-select>
- </div>
- </el-col>
- </el-row>
- </div>
- </div>
- </div>
- <div class="panel-toggle" @click="showPanel = !showPanel">
- <i :class="showPanel ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'"></i>
- </div>
- <div class="pc-player-right" v-show="showPanel">
- <div class="playlist-header">
- <div class="playlist-title">
- <i class="el-icon-video-list"></i>
- 课程列表
- </div>
- <div class="playlist-header-row">
- <div class="playlist-count">
- 共 {{ list.length }} 个视频
- <span class="finished-count" v-if="finishedCount">,已完成 {{ finishedCount }} 个</span>
- </div>
- <el-switch
- v-model="hideFinished"
- active-text="隐藏已完成"
- inactive-text=""
- size="small"
- ></el-switch>
- </div>
- </div>
- <div class="playlist-body">
- <div class="playlist-item" v-for="(item, index) in filteredList" :key="item.id" :class="{
- 'is-active': media.id === item.id,
- 'is-finished': item.isFinish == 1,
- 'is-current-playing': media.id === item.id && onPlay
- }" @click="loadMedia(item)">
- <div class="item-index" :class="{ 'active-index': media.id === item.id }">
- <span v-if="item.isFinish == 1" class="finish-badge">
- <i class="el-icon-check"></i>
- </span>
- <span v-else>{{ index + 1 }}</span>
- </div>
- <div class="item-info">
- <div class="item-name">{{ item.name }}</div>
- <div class="item-meta">
- <span class="item-duration">学时:{{ item.xs / 10 }}</span>
- <span class="item-progress">{{item|percent}}%</span>
- </div>
- </div>
- <div class="item-action">
- <el-button v-if="media.id === item.id && onPlay" type="warning" size="mini" icon="el-icon-video-pause"
- circle @click.stop="doPause"></el-button>
- <el-button v-else-if="media.id === item.id" type="primary" size="mini" icon="el-icon-video-play" circle
- @click.stop="doPlay"></el-button>
- <el-button v-else type="text" size="mini" icon="el-icon-video-play"
- @click.stop="loadMedia(item)">播放</el-button>
- </div>
- </div>
- <div class="playlist-empty" v-if="list.length === 0">
- <i class="el-icon-document"></i>
- <p>暂无视频</p>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { mapActions } from "vuex";
- import {
- getPercent
- } from '@/utils/index.js'
- import {
- videoPlayer
- } from 'vue-video-player';
- import 'video.js/dist/video-js.css'
- import {
- httpServer
- } from "@/components/httpServer/httpServer.js";
- export default {
- data() {
- return {
- courseId: 0,
- mediaId: 0,
- media: {},
- options: {
- controls: true,
- autoplay: true,
- muted: true,
- loop: false,
- preload: "auto",
- language: 'zh-CN',
- aspectRatio: '16:9',
- fluid: true,
- sources: [],
- poster: '',
- notSupportedMessage: '无法播放媒体源',
- playtimes: '',
- controlBar: {
- timeDivider: true,
- durationDisplay: true,
- remainingTimeDisplay: false,
- fullscreenToggle: true
- }
- },
- curTimes: 0,
- tpl: {},
- info: {isBan:0},
- list: [],
- timer: false,
- tickNum: 0,
- prevTime: 0,
- isReady: false,
- onPlay: false,
- volume: 0.5,
- isMuted: false,
- mediaType: 'hls',
- hasSeeked: false,
- hideFinished: false,
- showPanel: true,
- hiddenStartTime: 0,
- hiddenBasePosition: 0,
- isPiP: false
- };
- },
- components: {
- videoPlayer
- },
- computed: {
- finishedCount() {
- return this.list.filter(i => i.isFinish == 1).length;
- },
- filteredList() {
- if (this.hideFinished) {
- return this.list.filter(i => i.isFinish != 1)
- }
- return this.list
- },
- playPercent() {
- if (!this.media.duration) return 0;
- if(this.media.isFinish ==1) return 100;
- return +((this.curTimes / this.media.duration) * 100).toFixed(1);
- }
- },
- filters: {
- percent(item){
- if( item.isFinish == 1) return 100;
- return (item.position * 100 / item.duration).toFixed(1)
- },
- useTime(val) {
- val = parseInt(val) || 0
- let timestr = ""
- let hour = parseInt(val / 3600);
- let min = parseInt(val / 60 % 60);
- let sec = parseInt(val % 60);
- if (hour < 10) hour = "0" + hour;
- if (min < 10) min = "0" + min;
- if (sec < 10) sec = "0" + sec;
- return hour + ":" + min + ":" + sec
- }
- },
- methods: {
- getCourse() {
- let param = {
- courseId: this.courseId
- }
- httpServer("course.getCourse", param).then(res => {
- if (res.code == 200) {
- let {
- info,
- extra,
- list,
- tpl
- } = res.data;
- this.info = Object.assign(info, extra || {});
- this.tpl = tpl || {};
- this.list = list.map((item) => {
- item.percent = getPercent(item) || 0;
- return item;
- })
- let target = null
- if (this.mediaId) {
- target = this.list.find(i => i.id == this.mediaId || i.mediaId == this.mediaId)
- }
- if (!target) {
- target = this.list.find(i => i.isFinish != 1)
- }
- if (!target && this.list.length) {
- target = this.list[0]
- }
- if (target) {
- this.loadMedia(target)
- }
- }
- })
- },
- handleVisibilityChange() {
- if (document.hidden) {
- let myPlayer = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (myPlayer && myPlayer.currentTime) {
- this.hiddenStartTime = Date.now()
- this.hiddenBasePosition = myPlayer.currentTime() || 0
- }
- } else if (this.hiddenStartTime) {
- let elapsed = (Date.now() - this.hiddenStartTime) / 1000
- let estimated = this.hiddenBasePosition + elapsed
- this.hiddenStartTime = 0
- if (estimated < (this.media.duration || Infinity)) {
- this.setposition(estimated)
- }
- }
- },
- onEnterPiP() {
- this.isPiP = true
- },
- onLeavePiP() {
- this.isPiP = false
- },
- async enterPiP() {
- if (!document.pictureInPictureEnabled || this.isPiP) return
- let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (!player) return
- let video = player.tech_ && player.tech_.el_
- if (video && video.requestPictureInPicture) {
- try { await video.requestPictureInPicture() } catch (e) {}
- }
- },
- async exitPiP() {
- if (document.pictureInPictureElement) {
- try { await document.exitPictureInPicture() } catch (e) {}
- }
- },
- togglePiP() {
- if (document.pictureInPictureElement) {
- this.exitPiP()
- } else {
- this.enterPiP()
- }
- },
- setVolume(val) {
- this.volume = val
- this.isMuted = val === 0
- let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (player && player.volume) {
- player.volume(val)
- player.muted(this.isMuted)
- }
- },
- changeMedia(val) {
- if (!val || !this.mediaUrl) return;
- let mediaUrl = this.mediaUrl;
- if (val == 'ld') {
- mediaUrl = mediaUrl.replace('/hls/', '/ld/');
- } else {
- mediaUrl = mediaUrl.replace('/ld/', '/hls/');
- }
- this.mediaType = val;
- this.onPlay = false
- this.curTimes = 0
- this.hasSeeked = false
- this.options = {
- ...this.options,
- sources: [{
- src: mediaUrl + (mediaUrl.includes('?') ? '&' : '?') + '_t=' + Date.now(),
- type: "application/x-mpegURL"
- }],
- html5: {
- hls: {
- xhrSetup(xhr) {
- xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
- xhr.setRequestHeader('Pragma', 'no-cache')
- }
- }
- },
- playtimes: this.media.position || 0,
- autoplay: true
- }
- },
- startTick() {
- console.log("startTick")
- let tick = this.tryTick;
- if (this.timer) clearInterval(this.timer);
- this.timer = setInterval(tick, 5 * 1000);
- },
- stopTick() {
- console.log("stopTick")
- if (this.timer) clearInterval(this.timer);
- },
- tryTick() {
- let that = this;
- try {
- that.tick()
- } catch (err) {
- that.reportErr("play", '' + err.message)
- }
- },
- playerReadied(player) {
- this.isReady = true
- let video = player.tech_ && player.tech_.el_
- if (video) {
- video.addEventListener('enterpictureinpicture', this.onEnterPiP)
- video.addEventListener('leavepictureinpicture', this.onLeavePiP)
- }
- if (this.media.position > 5 && this.media.position < this.media.duration - 10) {
- setTimeout(() => {
- this.setposition(this.media.position)
- }, 300)
- }
- },
- onPlayerError(event) {
- console.error("Video error:", event)
- },
- onPlayerTimeupdate(player) {
- let curTimes = player.currentTime();
- this.curTimes = curTimes || 0
- if (this.media.isFinish) {
- return;
- }
- },
- safePlay(player) {
- if (!player) return
- try {
- const p = player.play()
- if (p && p.catch) p.catch(() => {})
- } catch (e) {}
- },
- setposition(position) {
- if( position < 10) return;
- if (position > this.media.duration) position = this.media.duration;
- let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player;
- if (!player || !player.currentTime) return;
- player.currentTime(position);
- this.safePlay(player)
- if (this.media.isFinish) return;
- if (this.media.position >= this.media.duration - 10 && !this.media.isFinish) {
- this.tick(true)
- }
- },
- onPlayerPause(event) {
- this.reportErr("play", 'pause');
- // this.stopTick()
- this.onPlay = false
- },
- onPlayerEnded(event) {
- this.reportErr("play", 'end');
- this.tick(true)
- this.playNext()
- },
- playNext() {
- if(this.tpl.courseCode.indexOf('-')==-1){
- return;
- }
- let currentIndex = this.list.findIndex(i => i.id === this.media.id)
- let nextItem = null
- for (let i = currentIndex + 1; i < this.list.length; i++) {
- if (this.list[i].isFinish != 1) {
- nextItem = this.list[i]
- break
- }
- }
- if (!nextItem) {
- for (let i = 0; i < currentIndex; i++) {
- if (this.list[i].isFinish != 1) {
- nextItem = this.list[i]
- break
- }
- }
- }
- if (nextItem) {
- this.$message.success(`即将播放: ${nextItem.name}`)
- setTimeout(() => {
- this.loadMedia(nextItem)
- }, 1000)
- }
- },
- onClose() {
- this.reportErr("play", 'close')
- this.doPause()
- },
- doPause() {
- this.stopTick()
- this.onPlay = false
- let myPlayer = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (myPlayer) myPlayer.pause()
- },
- doPlay() {
- this.onPlay = true
- this.startTick();
- if (!this.$refs.videoPlayer || !this.$refs.videoPlayer.player) return;
- this.safePlay(this.$refs.videoPlayer.player)
- this.tickNum = 0
- },
- onPlayerStart() {
- console.log("onPlayerStart")
- this.reportErr("play", 'start');
- },
- toggleMute() {
- this.isMuted = !this.isMuted
- let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (player && player.volume) {
- player.muted(this.isMuted)
- }
- },
- reportErr(action, msg) {
- httpServer("course.report", { action, msg }, true)
- },
- getPlayerTime() {
- if (this.hiddenStartTime) {
- let elapsed = (Date.now() - this.hiddenStartTime) / 1000
- let estimated = this.hiddenBasePosition + elapsed
- let myPlayer = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (myPlayer && myPlayer.currentTime) {
- let actual = myPlayer.currentTime()
- if (actual >= estimated - 0.5) {
- this.hiddenStartTime = 0
- return actual
- }
- }
- return Math.min(estimated, this.media.duration || 1/0)
- }
- let myPlayer = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (myPlayer && myPlayer.currentTime) {
- return myPlayer.currentTime()
- }
- return 0
- },
- tick(force = false) {
- console.log("tick", this.tickNum)
- let media = this.media;
- this.tickNum++
- if (this.media.isFinish) {
- return;
- }
- let myPlayer = this.$refs.videoPlayer && this.$refs.videoPlayer.player;
- if (!myPlayer) return;
- let curTimes = parseInt(this.getPlayerTime());
- this.curTimes = curTimes || 0
- let isFinish = force ? 1 : 0
- if (curTimes >= media.duration) isFinish = 1;
- if (window.navigator.webdriver) {
- this.reportErr("play", 'webdriver');
- this.doPause()
- return
- }
- let param = {
- id: media.id,
- position: curTimes,
- isFinish
- };
- let that = this
- httpServer("course.tick", param, true).then(res => {
- if (res.code == 200) {
- let {
- skip,
- position,
- pause,
- msg,
- closed
- } = res.data
- if(closed){
- that.doPause();
- that.$message.errorMsg(msg, 10)
- return
- }
- if (pause) {
- that.doPause();
- that.$message.errorMsg(msg, 10)
- setTimeout(() => that.playNext(), 10*1000)
- return
- }
- if (!skip) {
- let diff = Math.abs(position - curTimes);
- if (diff > 3) {
- that.setposition(position)
- }
- }
- Object.assign(that.media, res.data)
- if( res.data.isFinish == 1){
- that.playNext()
- }
- }
- })
- },
- loadMedia(item) {
- this.media = item;
- this.stopTick()
- httpServer('course.GetMedia', {
- id: item.id
- }).then(res => {
- if (res.code != 200) return;
- let {
- mediaUrl,
- id,
- position,
- marks,
- duration
- } = res.data || {};
- if(position < 10) position = 0;
- this.mediaUrl = mediaUrl;
- this.media.position = position;
- this.media.duration = duration;
- this.media.id = id;
- this.curTimes = 0
- this.onPlay = false
- this.hasSeeked = false
- this.hiddenStartTime = 0
- this.options = {
- ...this.options,
- sources: [{
- src: mediaUrl,
- type: "application/x-mpegURL"
- }],
- html5: {
- hls: {
- overrideNative: true
- }
- },
- playtimes: this.media.position || 0,
- autoplay: true
- }
- this.startTick()
- });
- }
- },
- created() {
- document.addEventListener('visibilitychange', this.handleVisibilityChange)
- let { uid, token, courseId, mediaId } = this.$route.query
- this.courseId = +courseId || +this.$route.params.courseId
- this.mediaId = mediaId || this.$route.query.mediaId || 0
- if (uid) localStorage.uid = uid
- if (token) localStorage.token = token
- if (this.courseId) {
- this.getCourse()
- }
- },
- beforeDestroy() {
- document.removeEventListener('visibilitychange', this.handleVisibilityChange)
- this.exitPiP()
- this.stopTick()
- let player = this.$refs.videoPlayer && this.$refs.videoPlayer.player
- if (player) {
- let video = player.tech_ && player.tech_.el_
- if (video) {
- video.removeEventListener('enterpictureinpicture', this.onEnterPiP)
- video.removeEventListener('leavepictureinpicture', this.onLeavePiP)
- }
- }
- this.reportErr("play", 'destroy')
- }
- }
- </script>
- <style>
- @import url("./play.css");
- .pc-player-container {
- min-height: 100vh;
- background: #f0f2f5;
- padding: 20px;
- box-sizing: border-box;
- }
- .pc-player-wrapper {
- max-width: 1440px;
- margin: 0 auto;
- display: flex;
- gap: 20px;
- height: calc(100vh - 40px);
- }
- .pc-player-left {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- }
- .panel-toggle {
- flex-shrink: 0;
- width: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- color: #999;
- font-size: 14px;
- user-select: none;
- transition: color 0.2s;
- }
- .panel-toggle:hover {
- color: #409EFF;
- }
- .video-section {
- background: #fff;
- border-radius: 12px;
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
- overflow: hidden;
- }
- .video-title {
- padding: 16px 20px;
- font-size: 16px;
- font-weight: 600;
- color: #1a1a2e;
- border-bottom: 1px solid #f0f0f0;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- .video-player-wrap {
- width: 100%;
- background: #000;
- touch-action: none;
- }
- .video-player-wrap .video-js {
- width: 100%;
- }
- .video-info-bar {
- padding: 12px 20px;
- display: flex;
- align-items: center;
- gap: 16px;
- border-top: 1px solid #f0f0f0;
- }
- .video-time {
- font-size: 14px;
- color: #666;
- white-space: nowrap;
- font-variant-numeric: tabular-nums;
- }
- .time-current {
- color: #409EFF;
- font-weight: 600;
- }
- .time-sep {
- margin: 0 4px;
- color: #ccc;
- }
- .video-progress {
- flex: 1;
- min-width: 100px;
- }
- .video-status {
- flex-shrink: 0;
- }
- .pc-player-right {
- width: 380px;
- flex-shrink: 0;
- background: #fff;
- border-radius: 12px;
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
- .playlist-header {
- padding: 16px 20px 12px;
- border-bottom: 1px solid #f0f0f0;
- flex-shrink: 0;
- }
- .playlist-header-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-top: 6px;
- }
- .playlist-title {
- font-size: 16px;
- font-weight: 600;
- color: #1a1a2e;
- margin-bottom: 4px;
- }
- .playlist-title i {
- margin-right: 6px;
- color: #409EFF;
- }
- .playlist-count {
- font-size: 12px;
- color: #999;
- }
- .finished-count {
- color: #67C23A;
- }
- .playlist-body {
- flex: 1;
- overflow-y: auto;
- padding: 8px 0;
- }
- .playlist-body::-webkit-scrollbar {
- width: 6px;
- }
- .playlist-body::-webkit-scrollbar-thumb {
- background: #ddd;
- border-radius: 3px;
- }
- .playlist-body::-webkit-scrollbar-track {
- background: transparent;
- }
- .playlist-item {
- display: flex;
- align-items: center;
- padding: 12px 20px;
- cursor: pointer;
- transition: all 0.2s ease;
- border-left: 3px solid transparent;
- gap: 12px;
- }
- .playlist-item:hover {
- background: #f5f7fa;
- }
- .playlist-item.is-active {
- background: #ecf5ff;
- border-left-color: #409EFF;
- }
- .playlist-item.is-finished {
- opacity: 0.65;
- }
- .playlist-item.is-current-playing {
- background: #ecf5ff;
- border-left-color: #409EFF;
- }
- .item-index {
- width: 28px;
- height: 28px;
- border-radius: 50%;
- background: #f0f2f5;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 12px;
- font-weight: 600;
- color: #666;
- flex-shrink: 0;
- transition: all 0.2s;
- }
- .item-index.active-index {
- background: #409EFF;
- color: #fff;
- }
- .finish-badge {
- color: #67C23A;
- font-size: 14px;
- }
- .item-info {
- flex: 1;
- min-width: 0;
- }
- .item-name {
- font-size: 14px;
- color: #333;
- font-weight: 500;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-bottom: 2px;
- }
- .is-finished .item-name {
- color: #999;
- text-decoration: line-through;
- }
- .is-active .item-name {
- color: #409EFF;
- }
- .item-meta {
- display: flex;
- gap: 12px;
- font-size: 12px;
- color: #999;
- }
- .item-progress {
- color: #409EFF;
- }
- .is-finished .item-progress {
- color: #67C23A;
- }
- .item-action {
- flex-shrink: 0;
- }
- .playlist-empty {
- text-align: center;
- padding: 60px 20px;
- color: #ccc;
- }
- .playlist-empty i {
- font-size: 48px;
- display: block;
- margin-bottom: 12px;
- }
- .playlist-empty p {
- font-size: 14px;
- margin: 0;
- }
- .dialog-footer {
- padding: 0 16px;
- border-top: 1px solid #f0f0f0;
- background: #fafafa;
- }
- .dialog-footer.pt30 {
- padding: 8px 16px;
- }
- .media-footer {
- display: flex;
- align-items: center;
- line-height: 1;
- }
- .media-time {
- font-size: 14px;
- color: #666;
- white-space: nowrap;
- font-variant-numeric: tabular-nums;
- display: flex;
- align-items: center;
- gap: 4px;
- }
- .media-center {
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .bicon {
- font-size: 22px !important;
- padding: 4px !important;
- }
- .volume-control {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- }
- .vol-icon {
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- color: #606266;
- user-select: none;
- line-height: 1;
- }
- .vol-icon:hover {
- color: #409EFF;
- }
- .volume-slider {
- width: 300px !important;
- }
- .volume-slider .el-slider__runway {
- height: 4px;
- }
- .volume-slider .el-slider__bar {
- height: 4px;
- }
- .volume-slider .el-slider__button {
- width: 12px;
- height: 12px;
- }
- .pip-control {
- display: inline-flex;
- align-items: center;
- margin-left: 12px;
- }
- .pip-icon {
- font-size: 12px;
- font-weight: 600;
- cursor: pointer;
- color: #606266;
- user-select: none;
- line-height: 1;
- padding: 4px 8px;
- border: 1px solid #dcdfe6;
- border-radius: 4px;
- transition: all 0.2s;
- }
- .pip-icon:hover {
- color: #409EFF;
- border-color: #c6e2ff;
- background: #ecf5ff;
- }
- .pip-icon.is-active {
- color: #409EFF;
- border-color: #409EFF;
- background: #ecf5ff;
- }
- .media-select {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- }
- .media-el-select {
- width: 80px;
- }
- .media-el-select .el-input__inner {
- height: 28px;
- line-height: 28px;
- font-size: 12px;
- }
- @media (max-width: 900px) {
- .pc-player-wrapper {
- flex-direction: column;
- height: auto;
- }
- .pc-player-right {
- width: 100%;
- max-height: 400px;
- }
- .pc-player-container {
- padding: 0;
- min-height: auto;
- }
- .panel-toggle {
- display: none;
- }
- .video-section {
- border-radius: 0;
- }
- .video-title {
- padding: 10px 12px;
- font-size: 14px;
- }
- .dialog-footer.pt30 {
- padding: 6px 10px;
- }
- .media-footer {
- flex-wrap: wrap;
- gap: 6px;
- }
- .media-center {
- display: none;
- }
- .volume-slider {
- width: 100px !important;
- }
- .media-select {
- display: none;
- }
- .pc-player-right {
- display: none;
- }
- }
- </style>
|