提交 6e912336 authored 作者: 王鹏飞's avatar 王鹏飞

Merge branch 'ip' into gdrtvu

VITE_LOGIN_URL=https://login.segedu.cn/auth/login/index
VITE_LAB_URL=https://bi.segedu.cn/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.segedu.cn
VITE_DML_PRO_URL=https://saas-dml-pro.segedu.cn
VITE_SAAS_LEARN_URL=https://saas-learn.segedu.cn
VITE_QA_CENTER_URL=https://qa-center.segedu.cn
VITE_X_LEARNING_URL=https://x-learning.segedu.cn
VITE_EXAM_SHOW_URL=https://exam-show.segedu.cn
VITE_SWSJFXS_LOGIN_URL=https://login.segedu.cn/swsjfxs/login/index
VITE_LOGIN_URL=http://172.16.3.201:1001/auth/login/index
VITE_LAB_URL=http://172.16.3.201:1008/bi/viewer?proc=0&action=index
VITE_DML_URL=http://172.16.3.201:1006
VITE_DML_PRO_URL=http://172.16.3.201:1007
VITE_SAAS_LEARN_URL=http://172.16.3.201:1009
VITE_QA_CENTER_URL=http://172.16.3.201:1004
VITE_X_LEARNING_URL=https://x-learning.zijing.chat
VITE_EXAM_SHOW_URL=https://exam-show.zijing.chat
VITE_SWSJFXS_LOGIN_URL=http://172.16.3.201:1001/swsjfxs/login/index
VITE_SYS_FLAG=chat
VITE_STATIC_URL=https://saas-lab-api
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
# VITE_LAB_URL=https://digitalmarketinglab.ezijing.com
VITE_LAB_URL=https://bi.ezijing.com/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.ezijing.com
VITE_DML_PRO_URL=https://saas-dml-pro.ezijing.com
\ No newline at end of file
VITE_LOGIN_URL=https://login.zijing.chat/auth/login/index
# VITE_LAB_URL=https://digitalmarketinglab.zijing.chat
VITE_LAB_URL=https://bi.zijing.chat/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.zijing.chat
VITE_DML_PRO_URL=https://saas-dml-pro.zijing.chat
\ No newline at end of file
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
VITE_LAB_URL=https://bi.ezijing.com/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.ezijing.com
VITE_DML_PRO_URL=https://saas-dml-pro.ezijing.com
\ No newline at end of file
VITE_LOGIN_URL=https://login.zijing.chat/auth/login/index
VITE_LAB_URL=https://bi.zijing.chat/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.zijing.chat
VITE_DML_PRO_URL=https://saas-dml-pro.zijing.chat
\ No newline at end of file
......@@ -4,7 +4,7 @@
"type": "module",
"scripts": {
"dev": "vite --mode dev",
"build": "vue-tsc --noEmit && vite build --mode prod && npm run deploy",
"build": "vue-tsc --noEmit && vite build --mode prod",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:pre": "vue-tsc --noEmit && vite build --mode pre",
"preview": "vite preview --port 4173",
......
......@@ -74,3 +74,26 @@ export function readMessages(params: { message_id: string }) {
params
})
}
// 获取分片大小和唯一文件名
export function getLocalFileChunk(params: { file_size: number; file_name: string }) {
return httpRequest.get('/api/lab/v1/common/file/chunk', { params })
}
// 上传每个分片前请求接口来获取当前文件是否超时,之前的分片是否被清理,如果被请求则拒绝处理。返回客户端错误码,让客户端户端不再续传剩余分片
// 1文件被清理 0文件未被清理
export function checkLocalFile(params: { file_name: string }) {
return httpRequest.get('/api/lab/v1/common/file/check', { params })
}
// 上传文件
export function uploadLocalFile(
data: { file: File | Blob; file_name: string; is_continuingly?: number; now_package_num: number; total_package_num: number },
options = {}
) {
return httpRequest.post(
'/api/lab/v1/common/file/upload',
data,
Object.assign({ withCredentials: false, headers: { 'Content-Type': 'multipart/form-data' } }, options)
)
}
<script setup lang="ts">
import OSS from 'ali-oss'
import '@/lib/aliyun-upload-sdk/aliyun-upload-sdk-1.5.3.min.js'
import { VideoPlay } from '@element-plus/icons-vue'
import { getUploadVideoAuth, updateUploadVideoAuth } from '@/api/base'
;(window as any).OSS = OSS
// uploadInfo 包含要上传的文件信息
interface UploadInfo {
bucket: string
checkpoint: { file: File; name: string; fileSize: number; partSize: number; uploadId: string }
endpoint: string
file: File
fileHash: string
isImage: boolean
loaded: number
object: string
region: string
retry: boolean
ri: string
state: string
userData: string
videoId: string
videoInfo: any
progress: number
}
import { useUpload } from '@/composables/useUpload'
interface Props {
beforeUpload?: (file: any) => void
}
......@@ -31,12 +10,16 @@ const props = defineProps<Props>()
const emit = defineEmits(['success'])
const uploader = createUploader()
let fileList = $ref<UploadInfo[]>([])
const { files, addFile, stopUpload } = useUpload({
onUploadSucceed(file) {
emit('success', file)
}
})
onUnmounted(() => {
uploader && uploader.stopUpload()
stopUpload()
})
const fileChange = async (event: Event) => {
const element = event.currentTarget as HTMLInputElement
const files: FileList | null = element.files
......@@ -44,104 +27,16 @@ const fileChange = async (event: Event) => {
if (props.beforeUpload) {
try {
await props.beforeUpload(files)
uploader.cleanList()
} catch (error) {
console.log(error)
return false
}
}
for (const file of files) {
// 是否重复上传
const hasRepeat = !!fileList.find(
item =>
item.file.name === file.name && item.file.size === file.size && item.file.lastModified === file.lastModified
)
!hasRepeat && uploader.addFile(file, null, null, null, '{"Vod":{}}')
addFile(file)
}
uploader.startUpload()
fileList = uploader._uploadList
}
function updateFileList(uploadInfo: UploadInfo) {
if (!uploadInfo) return
fileList = fileList.map(item => {
if (item.ri === uploadInfo.ri) {
return { ...item, ...uploadInfo }
}
return item
})
}
function createUploader() {
return new (window as any).AliyunUpload.Vod({
//userID,必填,您可以使用阿里云账号访问账号中心(https://account.console.aliyun.com/),即可查看账号ID
userId: '1303984639806000',
//上传到视频点播的地域,默认值为'cn-shanghai',
//eu-central-1,ap-southeast-1
region: 'cn-shanghai',
//分片大小默认1 MB,不能小于100 KB(100*1024)
partSize: 1048576,
//并行上传分片个数,默认5
parallel: 5,
//网络原因失败时,重新上传次数,默认为3
retryCount: 3,
//网络原因失败时,重新上传间隔时间,默认为2秒
retryDuration: 2,
//开始上传
onUploadstarted: onUploadStarted,
//文件上传成功
onUploadSucceed: onUploadSucceed,
//文件上传失败
onUploadFailed: onUploadFailed,
//文件上传进度,单位:字节
onUploadProgress: onUploadProgress,
//上传凭证或STS token超时
onUploadTokenExpired: onUploadTokenExpired,
//全部文件上传结束
onUploadEnd: onUploadEnd
})
}
// 开始上传
function onUploadStarted(uploadInfo: UploadInfo) {
console.log('onUploadStarted', uploadInfo)
getUploadVideoAuth({ title: uploadInfo.file.name, file_name: uploadInfo.file.name }).then(res => {
uploader.setUploadAuthAndAddress(uploadInfo, res.data.upload_auth, res.data.upload_address, res.data.source_id)
})
updateFileList(uploadInfo)
}
// 文件上传成功
function onUploadSucceed(uploadInfo: UploadInfo) {
console.log('onUploadSucceed', uploadInfo)
updateFileList(uploadInfo)
emit('success', uploadInfo)
}
//文件上传失败
function onUploadFailed(uploadInfo: UploadInfo, code: number, message: string) {
console.log('onUploadFailed', uploadInfo, code, message)
updateFileList(uploadInfo)
}
//文件上传进度,单位:字节
function onUploadProgress(uploadInfo: UploadInfo, totalSize: number, loadedPercent: number) {
console.log('onUploadProgress', uploadInfo.file.name, uploadInfo, totalSize, loadedPercent)
updateFileList(uploadInfo)
}
//上传凭证或STS token超时
function onUploadTokenExpired(uploadInfo: UploadInfo) {
console.log('onUploadTokenExpired', uploadInfo)
updateUploadVideoAuth({ source_id: uploadInfo.videoId }).then(res => {
uploader.resumeUploadWithAuth(res.data.UploadAuth)
})
updateFileList(uploadInfo)
}
// 全部文件上传结束
function onUploadEnd(uploadInfo: UploadInfo) {
console.log('onUploadEnd', uploadInfo)
updateFileList(uploadInfo)
}
// 进度条
function percentage(value: number) {
return parseFloat((value ? value * 100 : 0).toFixed(2))
}
defineExpose({ uploader, fileList: $$(fileList) })
defineExpose({ files })
</script>
<template>
<div class="upload-video">
......@@ -151,13 +46,13 @@ defineExpose({ uploader, fileList: $$(fileList) })
</div>
<div class="tips">推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。</div>
<ul class="upload-video-list" v-for="(item, index) in fileList" :key="index">
<ul class="upload-video-list" v-for="(item, index) in files" :key="index">
<li>
<div class="upload-video-list__item-info">
<el-icon><VideoPlay /></el-icon>
<p>{{ item.file?.name }}</p>
</div>
<el-progress :stroke-width="2" :percentage="percentage(item.loaded)" v-if="item.loaded !== 1" />
<el-progress :stroke-width="2" :percentage="item.progress" v-if="item.progress !== 100" />
</li>
</ul>
</div>
......
<script setup lang="ts">
import Editor from '@tinymce/tinymce-vue'
import md5 from 'blueimp-md5'
import { getSignature, uploadFile } from '@/api/base'
import { getLocalFileChunk, uploadLocalFile } from '@/api/base'
const props = defineProps({
height: {
......@@ -13,31 +12,24 @@ const props = defineProps({
const ImageUploadHandler = (blobInfo: any) =>
new Promise((resolve, reject) => {
const file = blobInfo.blob()
getSignature()
getLocalFileChunk({ file_name: file.name, file_size: file.size })
.then((response: any) => {
const prefix = 'upload/admin/'
const key = prefix + md5(file.name + new Date().getTime()) + file.name.substr(file.name.lastIndexOf('.'))
const { accessid, policy, signature, host } = response
const params = {
key,
host,
OSSAccessKeyId: accessid,
policy,
signature,
success_action_status: '200',
file,
url: `${host}/${key}`
file_name: response.data.detail.file_name,
now_package_num: 1,
total_package_num: 1
}
uploadFile(params)
uploadLocalFile(params)
.then((res: any) => {
resolve(res.url)
resolve(res.data.detail.uri)
})
.catch(() => {
reject('上传失败')
})
})
.catch(() => {
reject('获取Signature失败')
reject('上传失败')
})
})
......
......@@ -2,8 +2,8 @@
import type { UploadProps, UploadUserFile } from 'element-plus'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import md5 from 'blueimp-md5'
import { getSignature } from '@/api/base'
// import md5 from 'blueimp-md5'
import { getLocalFileChunk } from '@/api/base'
interface Props {
modelValue: string | { name: string; url: string }[]
......@@ -41,19 +41,12 @@ const handleBeforeUpload = async (file: any) => {
ElMessage.error(`文件大小不能超过${props.size / 1024 / 1024}M`)
return false
}
const fileName = file.name
const key = props.prefix + md5(fileName + new Date().getTime()) + '/' + fileName
const response: Record<string, any> = await getSignature()
const response: Record<string, any> = await getLocalFileChunk({ file_name: file.name, file_size: file.size })
uploadData.value = {
key,
host: response.host,
OSSAccessKeyId: response.accessid,
policy: response.policy,
signature: response.signature,
success_action_status: '200',
url: `${response.host}/${key}`
file_name: response.data.detail.file_name,
now_package_num: 1,
total_package_num: 1
}
file.url = `${response.host}/${key}`
if (props.beforeUpload) {
return props.beforeUpload(file)
}
......@@ -61,6 +54,7 @@ const handleBeforeUpload = async (file: any) => {
// 上传成功
const handleSuccess = (response: any, file: any, files: any) => {
file.url = response.data.detail.uri
if (!files.every((item: any) => item.status === 'success')) return
if (showFileList.value) {
if (props.limit && props.limit === 1) {
......@@ -89,7 +83,7 @@ const handleSuccess = (response: any, file: any, files: any) => {
)
}
} else {
emit('update:modelValue', file.raw.url)
emit('update:modelValue', response.data.detail.uri)
}
emit('success', file, files)
}
......@@ -121,7 +115,7 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
<template>
<el-upload
:action="uploadData?.host"
action="/api/lab/v1/common/file/upload"
:data="uploadData"
:show-file-list="showFileList"
:before-upload="handleBeforeUpload"
......@@ -130,8 +124,7 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
:on-preview="handlePreview"
:on-success="handleSuccess"
:file-list="fileList"
class="uploader"
>
class="uploader">
<slot>
<template v-if="showFileList">
<template v-if="$attrs['list-type'] === 'picture-card'">
......
import { getLocalFileChunk, uploadLocalFile } from '@/api/base'
interface FileItem {
file: File
url: string
name: string
progress: number
abortController: AbortController
}
interface UploadOptions {
multiple?: boolean
autoUpload?: boolean
onUploadStarted?: (file: FileItem) => void
onUploadSucceed?: (file: FileItem) => void
onUploadFailed?: (file: FileItem, error: any) => void
onUploadProgress?: (file: FileItem, progress: number) => void
onUploadEnd?: () => void
}
export function useUpload(options: UploadOptions = {}) {
options = Object.assign({ autoUpload: true, multiple: false }, options)
const files = ref<FileItem[]>([])
const uploading = ref(false)
function addFile(file: File) {
// 检查文件是否已经在队列中
const existingFileIndex = files.value.findIndex(f => f.name === file.name)
// 如果文件已经在队列中,并且上传已经开始,则直接返回
if (existingFileIndex !== -1 && files.value[existingFileIndex].progress > 0) return
if (!options.multiple) stopUpload()
const abortController = new AbortController()
files.value.push({ url: '', name: file.name, file, progress: 0, abortController })
if (options.autoUpload && !uploading.value) startUpload()
}
async function startUpload() {
uploading.value = true
for (const file of files.value) {
if (file.progress === 0) {
// 只处理尚未开始上传的文件
try {
options?.onUploadStarted?.(file)
await uploadFile(file)
options?.onUploadSucceed?.(file)
} catch (error) {
options?.onUploadFailed?.(file, error)
}
}
}
uploading.value = false
options?.onUploadEnd?.()
}
async function uploadFile(file: FileItem) {
const {
data: { detail }
} = await getLocalFileChunk({ file_name: file.file.name, file_size: file.file.size })
const fileName = detail.file_name
const chunkSize = detail.chunk_size
const totalChunks = Math.ceil(file.file.size / chunkSize)
for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
const chunk = file.file.slice(chunkIndex * chunkSize, (chunkIndex + 1) * chunkSize)
await uploadChunk({ file, chunk, chunkIndex, fileName, totalChunks })
}
}
async function uploadChunk({
file,
chunk,
chunkIndex,
fileName,
totalChunks
}: {
file: FileItem
chunk: Blob
chunkIndex: number
fileName: string
totalChunks: number
}) {
const {
data: { detail }
} = await uploadLocalFile(
{ file_name: fileName, file: chunk, now_package_num: chunkIndex + 1, total_package_num: totalChunks },
{
signal: file.abortController.signal,
onUploadProgress(event: ProgressEvent) {
updateProgress({ file, event, chunkIndex, totalChunks })
}
}
)
file.url = detail.uri
}
function updateProgress({ event, chunkIndex, totalChunks, file }: { file: FileItem; event: ProgressEvent; chunkIndex: number; totalChunks: number }) {
const totalSize = event.total * totalChunks
const loadedSize = event.loaded + chunkIndex * event.total
const progressPercent = (loadedSize / totalSize) * 100
file.progress = parseFloat(progressPercent.toFixed(2))
options?.onUploadProgress?.(file, file.progress)
console.log(file.progress)
}
function cancelUpload(file: FileItem) {
file.abortController.abort()
files.value = files.value.filter(item => item.name !== file.name)
}
function stopUpload() {
files.value.forEach(cancelUpload) // 停止所有文件的上传
files.value.length = 0
uploading.value = false // 更新上传状态
}
return { files, uploading, addFile, startUpload, cancelUpload, stopUpload }
}
......@@ -29,7 +29,8 @@ const form = reactive<any>({
source_id: '',
duration: 0,
type: '',
protocol: false
protocol: false,
size: ''
})
watchEffect(() => {
if (!props.data) return
......@@ -69,7 +70,8 @@ function handleBeforeUpload() {
function handleUploadSuccess(uploadInfo: any) {
form.name = uploadInfo.file.name.split('.').shift()
form.source_id = uploadInfo.videoId
form.source_id = uploadInfo.url
form.size = uploadInfo.file.size
}
// 提交
function handleSubmit() {
......
......@@ -6,21 +6,25 @@ interface Props {
data: ContestVideoItem
}
const props = defineProps<Props>()
let playList = $ref<any>([])
let playList = $ref<any>()
function fetchInfo() {
getContestVideoPalyInfo({ source_id: props.data.source_id }).then(res => {
playList = res.data.replay_list.play_info_list
})
}
const playUrl = $computed(() => {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
if (Array.isArray(playList)) {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
} else {
return playList
}
})
onMounted(() => {
fetchInfo()
......@@ -29,8 +33,6 @@ onMounted(() => {
<template>
<el-dialog title="查阅操作视频">
<AppVideoPlayer
:options="{ fluid: true, sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
v-if="playUrl"></AppVideoPlayer>
<AppVideoPlayer :options="{ fluid: true, sources: [{ src: playUrl }] }" v-if="playUrl"></AppVideoPlayer>
</el-dialog>
</template>
......@@ -109,6 +109,7 @@ export interface ContestVideoCreateParams {
status: string
duration: number
source_id: string
size: string
}
export type ContestVideoUpdateParams = ContestVideoCreateParams & { id: string }
......@@ -77,7 +77,7 @@ function handleBeforeUpload() {
function handleUploadSuccess(file: any) {
form.name = file.name.split('.').shift()
form.size = (file.size / 1024 / 1024).toString()
form.url = file.raw.url
form.url = file.url
form.type = file.raw.type || 'unknown'
}
// 提交
......
......@@ -84,7 +84,7 @@ function handleBeforeUpload() {
function handleUploadSuccess(file: any) {
form.name = file.name.split('.').shift()
form.size = (file.size / 1024 / 1024).toString()
form.url = file.raw.url
form.url = file.url
form.type = file.raw.type || 'unknown'
}
// 提交
......
......@@ -12,7 +12,7 @@ const ScoreLogDialog = defineAsyncComponent(() => import('../components/ScoreLog
const { courses, experiments, specialties, classes } = useFilterList()
const cookies = useCookies(['TGC'])
const LAB_URL = `${import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}`
const LAB_URL = `${import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}&sysFlag=${import.meta.env.VITE_SYS_FLAG}`
const route = useRoute()
......
......@@ -33,7 +33,8 @@ const form = reactive<VideoCreateItem>({
experiment_id: '',
status: '1',
source_id: '',
protocol: false
protocol: false,
size: ''
})
watchEffect(() => {
if (!props.data) return
......@@ -75,7 +76,8 @@ function handleBeforeUpload() {
function handleUploadSuccess(uploadInfo: any) {
form.name = uploadInfo.file.name.split('.').shift()
form.source_id = uploadInfo.videoId
form.source_id = uploadInfo.url
form.size = uploadInfo.file.size
}
// 提交
function handleSubmit() {
......
......@@ -29,4 +29,5 @@ export interface VideoCreateItem {
name: string
source_id: string
protocol: boolean
size: string
}
......@@ -9,7 +9,7 @@ interface Props {
const props = defineProps<Props>()
let detail = $ref<VideoItem | null>(null)
let playList = $ref<any>([])
let playList = $ref<any>()
function fetchInfo() {
if (!props.id) return
......@@ -19,14 +19,18 @@ function fetchInfo() {
})
}
const playUrl = $computed(() => {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
if (Array.isArray(playList)) {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
} else {
return playList
}
})
onMounted(() => {
fetchInfo()
......@@ -47,9 +51,7 @@ onMounted(() => {
<el-descriptions-item label="创建时间:">{{ detail.created_time }}</el-descriptions-item>
</el-descriptions>
<div style="height: 600px" v-if="playUrl">
<AppVideoPlayer
:options="{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%"></AppVideoPlayer>
<AppVideoPlayer :options="{ sources: [{ src: playUrl }] }" style="width: 100%; height: 100%"></AppVideoPlayer>
</div>
</template>
</AppCard>
......
<script setup lang="ts">
import type { ExperimentVideoType, PlayInfo } from '../types'
import type { ExperimentVideoType } from '../types'
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
import { getExperimentVideoPlayInfo } from '../api'
......@@ -11,7 +11,7 @@ interface Props {
data: ExperimentVideoType
}
const props = defineProps<Props>()
let playList = $ref<PlayInfo[]>([])
let playList = $ref<any>()
function fetchInfo() {
getExperimentVideoPlayInfo({ source_id: props.data.source_id }).then(res => {
playList = res.data.list.play_info_list
......@@ -28,14 +28,18 @@ function fetchInfo() {
})
}
const playUrl = $computed(() => {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
if (Array.isArray(playList)) {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
} else {
return playList
}
})
onMounted(() => {
fetchInfo()
......@@ -46,9 +50,7 @@ onMounted(() => {
<h2>{{ data.name }}</h2>
<!-- <img :src="data.cover" /> -->
<div style="height: 200px" v-if="playUrl">
<AppVideoPlayer
:options="{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%"></AppVideoPlayer>
<AppVideoPlayer :options="{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }" style="width: 100%; height: 100%"></AppVideoPlayer>
</div>
</div>
</template>
......
<script setup lang="ts">
import type { ExperimentVideoType, PlayInfo } from '../types'
import type { ExperimentVideoType, } from '../types'
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
import { getExperimentVideoPlayInfo } from '../api'
......@@ -12,7 +12,7 @@ interface Props {
data: ExperimentVideoType
}
const props = defineProps<Props>()
let playList = $ref<PlayInfo[]>([])
let playList = $ref<any>()
function fetchInfo() {
getExperimentVideoPlayInfo({ source_id: props.data.source_id }).then(res => {
playList = res.data.play_info_list
......@@ -29,14 +29,18 @@ function fetchInfo() {
})
}
const playUrl = $computed(() => {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
if (Array.isArray(playList)) {
const definitionUrl = playList.reduce(
(result: any, item: any) => {
result[item.Definition] = item.PlayURL
return result
},
{ SD: '', LD: '', FD: '' }
)
return definitionUrl.SD || definitionUrl.LD || definitionUrl.FD
} else {
return playList
}
})
onMounted(() => {
fetchInfo()
......@@ -47,9 +51,7 @@ onMounted(() => {
<h2>{{ data.name }}</h2>
<!-- <img :src="data.cover" /> -->
<div style="height: 200px" v-if="playUrl">
<AppVideoPlayer
:options="{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%"></AppVideoPlayer>
<AppVideoPlayer :options="{ sources: [{ src: playUrl }] }" style="width: 100%; height: 100%"></AppVideoPlayer>
</div>
</div>
</template>
......
......@@ -98,7 +98,7 @@ const cookies = useCookies(['TGC'])
const LAB_URL = computed(() => {
return experimentInfo?.type === 4
? `${appConfig.dmlURL || import.meta.env.VITE_DML_URL}/trip/my?experiment_id=${form.experiment_id}`
: `${appConfig.dmlURL || import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}`
: `${appConfig.dmlURL || import.meta.env.VITE_LAB_URL}&token=${cookies.get('TGC')}&sysFlag=${import.meta.env.VITE_SYS_FLAG}`
})
let iframeKey = $ref(Date.now())
// 返回首页
......
......@@ -46,6 +46,14 @@ httpRequest.interceptors.response.use(
ElMessage.error(data.message || data.msg)
return Promise.reject(data)
}
if (import.meta.env.VITE_STATIC_URL) {
try {
const regex = /(http|https):\/\/(.*?)saas-lab-api/gi
return JSON.parse(JSON.stringify(data).replaceAll(regex, import.meta.env.VITE_STATIC_URL))
} catch (error) {
console.log(error)
}
}
return data
},
function (error) {
......
......@@ -7,8 +7,8 @@ import vue from '@vitejs/plugin-vue'
// import checker from 'vite-plugin-checker'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig(({ mode }) => ({
base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
export default defineConfig(() => ({
// base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
plugins: [
vue({ reactivityTransform: true, script: { defineModel: true } }),
AutoImport({
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论