Commit b2f77e8e authored by lijin's avatar lijin

优化 youtube页面

parent ef23ad84
......@@ -97,16 +97,6 @@ export const constantRouterMap = [
name: "assetManagement.youtubeuploadvideo",
component: () => import("@/views/uploadYoutube/uploadYoutube"),
meta: { title: "Youtube视频上传", icon: "chart" }
},{
path: "/assetManagement/uploadYoutubeLog",
name: "assetManagement.youtubeuploadvideolog",
component: () => import("@/views/uploadYoutube/uploadYoutubeLog"),
meta: { title: "Youtube账号上传日志", icon: "chart" }
},{
path: "/assetManagement/uploadYoutubeNew",
name: "assetManagement.youtubeuploadvideonew",
component: () => import("@/views/uploadYoutube/youtubeUploadNew"),
meta: { title: "Youtube视频上传新", icon: "chart" }
},{
path: "/assetManagement/YoutubeVideoManage",
name: "assetManagement.YoutubeVideoManage",
......
......@@ -26,8 +26,6 @@
<template slot="title">工具</template>
<el-menu-item index="/assetManagement/googleOauthYoutube">Youtube账号管理</el-menu-item>
<el-menu-item index="/assetManagement/uploadYoutube">Youtube视频上传</el-menu-item>
<el-menu-item index="/assetManagement/uploadYoutubeNew">Youtube视频上传新</el-menu-item>
<!-- <el-menu-item index="/assetManagement/uploadYoutubeLog">上传结果</el-menu-item>-->
<el-menu-item index="/assetManagement/YoutubeVideoManage">Youtube视频管理</el-menu-item>
</el-submenu>
</el-menu>
......
......@@ -148,11 +148,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="materialBusinessId"
label="Local ID"
width="100">
</el-table-column>
<el-table-column
prop="name"
label="名称"
......
<template>
<div class="video-upload-form">
<div class="form-header">
<div class="account-select">
<el-select v-model="selectedAccount" placeholder="选择YouTube账户" style="width: 200px;" @change="handleAccountChange">
<el-option
v-for="account in youtubeAccounts"
:key="account.id"
:label="account.alias"
:value="account.id">
</el-option>
</el-select>
<el-button type="primary" class="add-material-btn" @click="openMaterialDialog">添加素材</el-button>
<div class="upload-form-container">
<!-- YouTube账户选择 -->
<div class="form-section">
<div class="section-label">YouTube账户</div>
<el-select
v-model="form.youtubeAccountId"
placeholder="请选择账户"
@change="handleAccountChange"
style="width: 300px">
<el-option
v-for="account in youtubeAccounts"
:key="account.id"
:label="account.alias"
:value="account.id">
</el-option>
</el-select>
</div>
<!-- Video section -->
<div class="form-section">
<div class="section-label">视频</div>
<div class="button-group">
<el-button type="default" icon="el-icon-plus" @click="openMaterialDialog">添加素材</el-button>
<el-button type="default" @click="handleClearList">清空</el-button>
</div>
<!-- Video table -->
<el-table
:data="videoList"
style="width: 100%"
border
:row-class-name="tableRowClassName">
<el-table-column
label="缩略图"
width="80">
<template slot-scope="scope">
<img :src="`${scope.row.url}?x-oss-process=video/snapshot,t_1000,scaletype_fill`" class="video-thumbnail" />
</template>
</el-table-column>
<el-table-column
prop="materialName"
label="名称"
>
</el-table-column>
<el-table-column
label="标题"
>
<template slot-scope="scope">
<div class="title-input-container">
<el-input
:value="scope.row.title"
size="small"
:maxlength="100"
@input="(value) => handleTitleInput(scope.row, value)"
placeholder="请输入标题">
</el-input>
<span class="char-count">{{ scope.row.titleLength || 0 }}/100</span>
</div>
</template>
</el-table-column>
<el-table-column
label="操作"
width="200"
align="center">
<template slot-scope="scope">
<el-button type="text" class="delete-button" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-table :data="tableData" style="width: 100%" @selection-change="handleTableSelectionChange" ref="table" row-key="id">
<el-table-column type="selection" width="55" :reserve-selection="true"></el-table-column>
<el-table-column label="预览" width="180">
<template slot-scope="scope">
<video v-if="scope.row.url || scope.row.resType === 2" style="width: 100px; height: 56px" controls>
<source :src="scope.row.url" type="video/mp4">
Your browser does not support the video tag.
</video>
<el-image v-else-if="scope.row.thumbnail || scope.row.resType === 1" style="width: 100px; height: 56px" :src="scope.row.thumbnail" fit="cover"></el-image>
</template>
</el-table-column>
<el-table-column prop="materialName" label="名称" width="180"></el-table-column>
<el-table-column label="标题" width="180">
<template slot-scope="scope">
<el-input v-model="scope.row.title" size="small" placeholder="请输入标题"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template slot-scope="scope">
<el-button type="text" size="small" @click="removeVideo(scope.row)">移除</el-button>
</template>
</el-table-column>
</el-table>
<!-- Content settings -->
<div class="form-section">
<div class="section-label">设置</div>
<el-form :model="form" label-position="top">
<!-- Content for children -->
<div class="radio-group-container">
<el-radio-group v-model="form.forChildren">
<el-radio :label="true">内容是面向儿童</el-radio>
<el-radio :label="false">内容不是面向儿童</el-radio>
</el-radio-group>
</div>
<!-- Category -->
<div class="form-item-container">
<div class="section-label">类别</div>
<el-select v-model="form.category" placeholder="请选择分类" style="width: 300px">
<el-option
v-for="category in categories"
:key="category.id"
:label="category.snippet.title"
:value="category.id"
></el-option>
</el-select>
</div>
<!-- Playlist -->
<div class="form-item-container">
<div class="section-label">播放列表</div>
<div class="playlist-container">
<el-select v-model="form.playlist" placeholder="请选择播放列表" style="width: 300px">
<el-option
v-for="playlist in playlists"
:key="playlist.id"
:label="playlist.snippet.title"
:value="playlist.id"
></el-option>
</el-select>
<el-button type="primary" :disabled="true">创建播放列表</el-button>
</div>
</div>
<!-- Privacy -->
<div class="form-item-container">
<div class="section-label">公开范围</div>
<el-select v-model="form.visibility" placeholder="请选择公开范围" style="width: 300px">
<el-option
v-for="item in visibilityOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<!-- Description -->
<div class="form-item-container">
<div class="section-label">说明</div>
<el-input
type="textarea"
:value="form.description"
:maxlength="5000"
:rows="6"
placeholder="请输入视频说明"
resize="none"
@input="handleDescriptionInput">
</el-input>
<div class="char-count">{{ descriptionLength }}/5000</div>
</div>
</el-form>
</div>
<!-- Action buttons -->
<div class="form-actions">
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handlePublish">发布</el-button>
</div>
<!-- Material Dialog -->
<el-dialog title="选择素材" :visible.sync="materialDialogVisible" width="70%" @close="fetchTotalVideoNumber">
......@@ -64,140 +173,209 @@
:total="totalVideoNumber">
</el-pagination>
</div>
<span slot-footer class="dialog-footer">
<el-button @click="materialDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="confirmMaterialSelection">确 定</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="handleMaterialCancel">取 消</el-button>
<el-button type="primary" @click="confirmMaterialSelection">确 定</el-button>
</span>
</el-dialog>
<el-form :model="form" label-width="120px">
<!-- <el-form-item label="观众">-->
<!-- <span>内容是面向儿童</span>-->
<!-- <el-switch v-model="form.isForChildren"></el-switch>-->
<!-- <el-tag-->
<!-- :type="form.isForChildren ? 'info' : ''"-->
<!-- :effect="form.isForChildren ? 'dark' : 'plain'"-->
<!-- >-->
<!-- 内容不是面向儿童-->
<!-- </el-tag>-->
<!-- </el-form-item>-->
<el-form-item label="类别" required>
<el-select v-model="form.category" placeholder="请选择分类" style="width: 100%" v-if="categories.length > 0">
<el-option
v-for="category in categories"
:key="category.id"
:label="category.snippet.title"
:value="category.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="播放列表" v-if="playlists.length > 0">
<el-select v-model="form.playlist" placeholder="请选择播放列表" style="width: calc(100% - 130px)">
<el-option
v-for="playlist in playlists"
:key="playlist.id"
:label="playlist.snippet.title"
:value="playlist.id"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="标签">
<el-input
type="text"
placeholder="请输入标签,用逗号分隔"
v-model="form.tags"
></el-input>
</el-form-item>
<el-form-item label="公开范围">
<el-tabs v-model="activeTab">
<el-tab-pane label="发布" name="publish">
<el-select v-model="form.visibility" placeholder="选择可见性" style="width: 100%">
<el-option label="私密" value="private"></el-option>
<el-option label="公开" value="public"></el-option>
<el-option label="不公开列出" value="unlisted"></el-option>
</el-select>
</el-tab-pane>
</el-tabs>
</el-form-item>
<el-form-item label="说明">
<el-input
type="textarea"
:rows="4"
placeholder="请输入视频描述"
v-model="form.description"
></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveForm">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { getYoutubeAccountList, getYoutubeCategoryId, getListMyPlaylists, getVideo, getTotalVideoNumber, uploadFromOss } from '@/api/report';
import { getYoutubeAccountList, getVideo, getTotalVideoNumber, getYoutubeCategoryId, getListMyPlaylists, uploadFromOss } from '@/api/report';
export default {
name: 'YouTubeUploadForm',
data() {
return {
materialDialogVisible: false,
materialTableData: [],
selectedMaterials: [],
materialCurrentPage: 1,
materialPageSize: 5,
totalVideoNumber: 0,
youtubeAccounts: [],
selectedAccount: '',
tableData: [], // 表格数据
selectedVideos: [], // 已选视频列表, 只用在弹窗那里
tableSelectedVideos: [], // 表格中选中的视频
categories: [],
playlists: [],
visibilityOptions: [
{ label: '公开', value: 'public' },
{ label: '不公开', value: 'private' },
{ label: '解锁', value: 'unlisted' }
],
descriptionLength: 0,
form: {
youtubeAccountId: '',
forChildren: false,
category: '',
playlist: '',
visibility: '',
description: '',
tags: '' // 标签
privacy: 'public',
description: ''
},
activeTab: 'publish',
materialDialogVisible: false,
materialTableData: [],
selectedMaterials: [],
materialCurrentPage: 1,
materialPageSize: 5,
totalVideoNumber: 0,
videoList: [
]
}
},
created() {
this.fetchYoutubeAccounts();
this.getYoutubeAccounts();
this.fetchTotalVideoNumber();
},
methods: {
fetchYoutubeAccounts() {
getYoutubeAccountList()
// 获取YouTube账户列表
async getYoutubeAccounts() {
try {
const response = await getYoutubeAccountList();
if (response.status === 200) {
this.youtubeAccounts = response.result.data;
} else {
this.$message.error('获取YouTube账户列表失败');
}
} catch (error) {
console.error('获取YouTube账户列表错误:', error);
this.$message.error('获取YouTube账户列表失败');
}
},
handleAccountChange(accountId) {
this.form.youtubeAccountId = accountId;
this.fetchCategories();
this.fetchPlaylists();
},
openMaterialDialog() {
this.materialDialogVisible = true;
this.fetchMaterials();
},
fetchMaterials() {
getVideo({ page: this.materialCurrentPage - 1, size: this.materialPageSize })
.then(response => {
if (response.status === 200) {
this.materialTableData = response.result.data;
this.$nextTick(() => {
const selectedVideoIds = this.videoList.map(video => video.id);
this.materialTableData.forEach(row => {
if (selectedVideoIds.includes(row.id)) {
this.$refs.materialTable.toggleRowSelection(row, true);
}
});
});
} else {
this.$message.error('获取素材列表失败');
}
})
.catch(error => {
console.error('Error fetching materials:', error);
this.$message.error('获取素材列表失败');
});
},
handleMaterialSelectionChange(val) {
this.selectedMaterials = val;
},
handleMaterialCancel() {
this.materialDialogVisible = false;
this.selectedMaterials = [];
this.$refs.materialTable.clearSelection();
},
handleTitleInput(row, value) {
// 直接使用输入的值
const title = value || '';
// 更新标题
this.$set(row, 'title', title);
// 更新字数统计
this.$set(row, 'titleLength', title.length);
},
confirmMaterialSelection() {
if (this.selectedMaterials.length === 0) {
this.$message.warning('请选择要添加的素材');
return;
}
this.selectedMaterials.forEach(newVideo => {
const isDuplicate = this.videoList.some(existingVideo => existingVideo.id === newVideo.id);
if (!isDuplicate) {
const title = newVideo.materialName;
this.videoList.push({
...newVideo,
materialName: newVideo.materialName,
title,
progress: '0',
thumbnail: newVideo.url,
titleLength: title.length
});
}
});
this.$message.success('添加素材成功');
this.materialDialogVisible = false;
this.selectedMaterials = [];
this.$refs.materialTable.clearSelection();
},
handleMaterialPageSizeChange(val) {
this.materialPageSize = val;
this.fetchMaterials();
},
handleMaterialPageCurrentChange(val) {
this.materialCurrentPage = val;
this.fetchMaterials();
},
fetchTotalVideoNumber() {
getTotalVideoNumber()
.then(response => {
if (response.status === 200) {
this.youtubeAccounts = response.result.data;
this.totalVideoNumber = response.result.data;
} else {
this.$message.error('获取YouTube账户列表失败');
this.$message.error('获取视频总数失败');
}
})
.catch(error => {
console.error('Error fetching YouTube accounts:', error);
this.$message.error('获取YouTube账户列表失败');
console.error('Error fetching total video number:', error);
this.$message.error('获取视频总数失败');
});
},
handleDelete(index, row) {
this.$confirm('确定要删除这个视频吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.videoList.splice(index, 1);
this.$message.success('删除成功');
}).catch(() => {
// 取消删除,不做任何处理
});
},
handleClearList() {
if (this.videoList.length === 0) {
return;
}
this.$confirm('确定要清空当前列表吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.videoList = [];
this.$message.success('列表已清空');
}).catch(() => {
// 取消操作,不需要做任何处理
});
},
fetchCategories() {
if (!this.selectedAccount) {
if (!this.form.youtubeAccountId) {
this.categories = [];
return;
}
getYoutubeCategoryId({ youTubeAccountId: this.selectedAccount })
getYoutubeCategoryId({ youTubeAccountId: this.form.youtubeAccountId })
.then(response => {
if (response.status === 200) {
this.categories = response.result.data;
......@@ -210,12 +388,13 @@ export default {
this.$message.error('获取YouTube类别失败');
});
},
fetchPlaylists() {
if (!this.selectedAccount) {
if (!this.form.youtubeAccountId) {
this.playlists = [];
return;
}
getListMyPlaylists({ youTubeAccountId: this.selectedAccount })
getListMyPlaylists({ youTubeAccountId: this.form.youtubeAccountId })
.then(response => {
if (response.status === 200) {
this.playlists = response.result.data;
......@@ -228,18 +407,15 @@ export default {
this.$message.error('获取YouTube播放列表失败');
});
},
handleAccountChange() {
this.fetchCategories();
this.fetchPlaylists();
},
saveForm() {
if (this.tableSelectedVideos.length === 0) {
handlePublish() {
if (this.videoList.length === 0) {
this.$message.warning('请选择要上传的视频');
return;
}
// 构建 videoInfos 数组
const videoInfos = this.tableSelectedVideos.map(video => ({
const videoInfos = this.videoList.map(video => ({
id: video.id,
title: video.title
}));
......@@ -247,7 +423,7 @@ export default {
// 构建 UploadRequest 对象
const uploadRequest = {
videoInfos: videoInfos,
youTubeAccountId: this.selectedAccount,
youTubeAccountId: this.form.youtubeAccountId,
description: this.form.description,
tags: this.form.tags,
categoryId: this.form.category,
......@@ -255,15 +431,15 @@ export default {
playlistId: this.form.playlist
};
// 使用 then 的写法调用 uploadFromOss 方法发送请求
// 发送上传请求
uploadFromOss(uploadRequest)
.then(response => {
if (response.status === 200) {
this.$message.success('开始上传视频');
// 清空表格选中项
this.$refs.table.clearSelection();
// 清空表格数据
this.tableData = [];
// 清空视频列表
this.videoList = [];
// 跳转到视频管理页面
this.$router.push('/assetManagement/YoutubeVideoManage');
} else {
this.$message.error('上传视频失败: ' + response.message);
}
......@@ -273,157 +449,160 @@ export default {
console.error(error);
});
},
generateTitle() {
// 基于 tableSelectedVideos 生成标题
if (this.tableSelectedVideos.length === 0) {
return '默认标题';
}
if (this.tableSelectedVideos.length === 1) {
return this.tableSelectedVideos[0].title || '默认标题';
}
return '多個視頻';
},
handleTableSelectionChange(val) {
this.tableSelectedVideos = val;
},
openMaterialDialog() {
this.materialDialogVisible = true;
this.fetchMaterials();
},
fetchMaterials() {
getVideo({ page: this.materialCurrentPage - 1, size: this.materialPageSize })
.then(response => {
if (response.status === 200) {
this.materialTableData = response.result.data;
this.$nextTick(() => {
this.selectedVideos.forEach(selectedVideo => {
const targetRow = this.materialTableData.find(row => row.id === selectedVideo.id);
if (targetRow) {
this.$refs.materialTable.toggleRowSelection(targetRow, true);
}
});
});
} else {
this.$message.error('获取素材列表失败');
}
})
.catch(error => {
console.error('Error fetching materials:', error);
this.$message.error('获取素材列表失败');
});
},
handleMaterialSelectionChange(val) {
this.selectedMaterials = val;
handleDescriptionInput(value) {
const description = value || '';
this.form.description = description;
this.descriptionLength = description.length;
},
confirmMaterialSelection() {
this.selectedMaterials.forEach(newVideo => {
const isDuplicate = this.tableData.some(existingVideo => existingVideo.id === newVideo.id);
if (!isDuplicate) {
// 直接添加素材,不添加 editable 属性
this.tableData.push({ ...newVideo, title: newVideo.title || ''});
}
});
this.$nextTick(() => {
this.tableData.forEach(row => {
const isSelected = this.selectedMaterials.some(selectedVideo => selectedVideo.id === row.id);
if (isSelected) {
this.$refs.table.toggleRowSelection(row, true);
}
});
});
this.materialDialogVisible = false;
this.selectedMaterials = [];
},
handleMaterialPageSizeChange(val) {
this.materialPageSize = val;
this.fetchMaterials();
},
handleMaterialPageCurrentChange(val) {
this.materialCurrentPage = val;
this.fetchMaterials();
},
fetchTotalVideoNumber() {
getTotalVideoNumber()
.then(response => {
if (response.status === 200) {
this.totalVideoNumber = response.result.data;
} else {
this.$message.error('获取视频总数失败');
}
})
.catch(error => {
console.error('Error fetching total video number:', error);
this.$message.error('获取视频总数失败');
});
handleCancel() {
this.$router.push('/assetManagement/YoutubeVideoManage');
},
// 移除视频
removeVideo(row) {
this.$confirm('确定移除该视频吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
const index = this.tableData.findIndex(item => item.id === row.id);
if (index !== -1) {
this.tableData.splice(index, 1);
this.$message.success('移除成功!');
}
})
.catch(() => {
this.$message.error('移除失败!');
});
tableRowClassName({ rowIndex }) {
if (rowIndex % 2 === 1) {
return 'blue-row'
}
return ''
}
}
}
</script>
<style scoped>
.video-upload-form {
max-width: 1200px;
margin: 0 auto;
<style>
.upload-form-container {
padding: 20px;
/* max-width: 1200px; */
margin: 0 auto;
background-color: #fff;
}
.form-section {
margin-bottom: 24px;
}
.form-header {
.section-label {
font-size: 14px;
color: #606266;
margin-bottom: 8px;
font-weight: bold;
}
.button-group {
margin-bottom: 16px;
display: flex;
gap: 8px;
}
.video-thumbnail {
width: 60px;
height: 45px;
object-fit: cover;
border-radius: 2px;
}
.operation-cell {
display: flex;
justify-content: space-between;
align-items: center;
}
.progress-text {
color: #909399;
}
.delete-button {
color: #409EFF;
}
.blue-row {
background-color: #f0f7ff !important;
}
.form-item-container {
margin-bottom: 20px;
}
.form-header h2 {
margin-right: 20px;
white-space: nowrap;
.radio-group-container {
margin-bottom: 20px;
}
.playlist-container {
display: flex;
gap: 16px;
align-items: flex-start;
}
.privacy-container {
margin-bottom: 16px;
}
.el-select {
margin-right: 10px;
.privacy-sub-option {
margin-top: 8px;
margin-left: 24px;
}
.el-form-item {
margin-bottom: 22px;
.char-count {
text-align: right;
color: #909399;
font-size: 12px;
margin-top: 4px;
}
.el-tag {
margin-left: 10px;
.form-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #EBEEF5;
}
/* Element UI overrides */
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: normal;
}
.el-radio {
margin-right: 30px;
margin-bottom: 12px;
}
.el-radio__label {
font-weight: normal;
}
.el-tabs__nav {
padding-left: 0;
.el-textarea__inner {
font-family: inherit;
}
.el-tag + .el-tag {
margin-left: 10px;
.el-button--primary {
background-color: #1890ff;
border-color: #1890ff;
}
.account-select {
.el-button--primary:hover,
.el-button--primary:focus {
background-color: #40a9ff;
border-color: #40a9ff;
}
.el-select .el-input__inner {
border-radius: 2px;
}
.title-input-container {
display: flex;
flex-direction: column;
align-items: flex-start;
align-items: center;
gap: 8px;
}
.add-material-btn {
margin-top: 10px;
.char-count {
color: #909399;
font-size: 12px;
white-space: nowrap;
}
</style>
<template>
<div class="upload-log">
<h2>YouTube 上传日志</h2>
<el-table :data="tableData" style="width: 100%" border>
<el-table-column type="index" label="序号" width="50"></el-table-column>
<el-table-column prop="materialBusinessId" label="关联资源 ID" width="120"></el-table-column>
<el-table-column prop="videoId" label="YouTube 视频 ID" width="150"></el-table-column>
<el-table-column prop="ossUrl" label="OSS URL">
<template slot-scope="scope">
<video :src="scope.row.ossUrl" controls width="200"></video>
</template>
</el-table-column>
<el-table-column prop="title" label="视频标题"></el-table-column>
<el-table-column prop="privacyStatus" label="隐私状态" width="100"></el-table-column>
<el-table-column prop="uploadStatus" label="上传状态" width="100"></el-table-column>
<el-table-column prop="uploadProgress" label="上传进度" width="150">
<template slot-scope="scope">
<el-progress :percentage="scope.row.uploadProgress"></el-progress>
</template>
</el-table-column>
<el-table-column prop="uploadStartTime" label="上传开始时间" width="160"></el-table-column>
<el-table-column prop="uploadEndTime" label="上传结束时间" width="160"></el-table-column>
<el-table-column prop="errorMessage" label="错误信息"></el-table-column>
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script>
import { getVideoLog, getVideoLogCount } from '@/api/report';
export default {
name: "UploadLog",
data() {
return {
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
};
},
methods: {
fetchData() {
Promise.all([
getVideoLog({page: this.currentPage - 1, size: this.pageSize}),
getVideoLogCount()
])
.then(([logResponse, countResponse]) => {
if (logResponse.status === 200) {
this.tableData = logResponse.result.data;
} else {
this.$message.error('获取上传日志列表失败');
}
if (countResponse.status === 200) {
this.total = countResponse.result.data;
} else {
this.$message.error('获取上传日志总数失败');
}
})
.catch(error => {
console.error('Error fetching data:', error);
this.$message.error('获取数据失败');
});
},
handleSizeChange(val) {
this.pageSize = val;
this.currentPage = 1;
this.fetchData();
},
handleCurrentChange(val) {
this.currentPage = val;
this.fetchData();
}
},
created() {
this.fetchData();
}
};
</script>
<style scoped>
.upload-log {
padding: 20px;
}
</style>
<template>
<div class="upload-form-container">
<!-- YouTube账户选择 -->
<div class="form-section">
<div class="section-label">YouTube账户</div>
<el-select
v-model="form.youtubeAccountId"
placeholder="请选择账户"
@change="handleAccountChange"
style="width: 300px">
<el-option
v-for="account in youtubeAccounts"
:key="account.id"
:label="account.alias"
:value="account.id">
</el-option>
</el-select>
</div>
<!-- Video section -->
<div class="form-section">
<div class="section-label">视频</div>
<div class="button-group">
<el-button type="default" icon="el-icon-plus" @click="openMaterialDialog">添加素材</el-button>
<el-button type="default" @click="handleClearList">清空</el-button>
</div>
<!-- Video table -->
<el-table
:data="videoList"
style="width: 100%"
border
:row-class-name="tableRowClassName">
<el-table-column
label="缩略图"
width="80">
<template slot-scope="scope">
<img :src="`${scope.row.url}?x-oss-process=video/snapshot,t_1000,scaletype_fill`" class="video-thumbnail" />
</template>
</el-table-column>
<el-table-column
prop="name"
label="名称"
width="300">
</el-table-column>
<el-table-column
prop="format"
label="格式">
</el-table-column>
<el-table-column
label="操作"
width="200">
<template slot-scope="scope">
<div class="operation-cell">
<span class="progress-text">{{ scope.row.progress }} / 100</span>
<el-button type="text" class="delete-button" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- Content settings -->
<div class="form-section">
<div class="section-label">设置</div>
<el-form :model="form" label-position="top">
<!-- Content for children -->
<div class="radio-group-container">
<el-radio-group v-model="form.forChildren">
<el-radio :label="true">内容是面向儿童</el-radio>
<el-radio :label="false">内容不是面向儿童</el-radio>
</el-radio-group>
</div>
<!-- Category -->
<div class="form-item-container">
<div class="section-label">类别</div>
<el-select v-model="form.category" placeholder="请选择分类" style="width: 300px">
<el-option label="教育" value="education"></el-option>
</el-select>
</div>
<!-- Playlist -->
<div class="form-item-container">
<div class="section-label">播放列表</div>
<div class="playlist-container">
<el-select v-model="form.playlist" placeholder="请选择播放列表" style="width: 300px">
<el-option label="默认列表" value="default"></el-option>
</el-select>
<el-button type="primary">创建播放列表</el-button>
</div>
</div>
<!-- Privacy -->
<div class="form-item-container">
<div class="section-label">公开范围</div>
<div class="privacy-container">
<el-radio-group v-model="form.privacy">
<el-radio label="public">保存或发布</el-radio>
<el-radio label="private">安排时间</el-radio>
</el-radio-group>
<div class="privacy-sub-option" v-if="form.privacy === 'private'">
<el-radio label="unlisted">不公开列出</el-radio>
</div>
</div>
</div>
<!-- Description -->
<div class="form-item-container">
<div class="section-label">说明</div>
<el-input
type="textarea"
v-model="form.description"
:rows="6"
placeholder="请输入视频说明"
resize="none">
</el-input>
<div class="char-count">0/5000</div>
</div>
</el-form>
</div>
<!-- Action buttons -->
<div class="form-actions">
<el-button>取消</el-button>
<el-button type="primary">发布</el-button>
</div>
<!-- Material Dialog -->
<el-dialog title="选择素材" :visible.sync="materialDialogVisible" width="70%" @close="fetchTotalVideoNumber">
<el-table :data="materialTableData" style="width: 100%" @selection-change="handleMaterialSelectionChange" ref="materialTable" height="400">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="预览" width="180">
<template slot-scope="scope">
<video style="width: 100px; height: 56px" controls>
<source :src="scope.row.url" type="video/mp4">
Your browser does not support the video tag.
</video>
</template>
</el-table-column>
<el-table-column prop="materialName" label="名称"></el-table-column>
<el-table-column prop="datadate" label="日期"></el-table-column>
</el-table>
<div style="margin-top: 20px;">
<el-pagination
@size-change="handleMaterialPageSizeChange"
@current-change="handleMaterialPageCurrentChange"
:current-page="materialCurrentPage"
:page-sizes="[5, 10, 20]"
:page-size="materialPageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="totalVideoNumber">
</el-pagination>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="handleMaterialCancel">取 消</el-button>
<el-button type="primary" @click="confirmMaterialSelection">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getYoutubeAccountList, getVideo, getTotalVideoNumber } from '@/api/report';
export default {
name: 'YouTubeUploadForm',
data() {
return {
materialDialogVisible: false,
materialTableData: [],
selectedMaterials: [],
materialCurrentPage: 1,
materialPageSize: 5,
totalVideoNumber: 0,
youtubeAccounts: [],
form: {
youtubeAccountId: '',
forChildren: false,
category: '',
playlist: '',
privacy: 'public',
description: ''
},
videoList: [
]
}
},
created() {
this.getYoutubeAccounts();
this.fetchTotalVideoNumber();
},
methods: {
// 获取YouTube账户列表
async getYoutubeAccounts() {
try {
const response = await getYoutubeAccountList();
if (response.status === 200) {
this.youtubeAccounts = response.result.data;
} else {
this.$message.error('获取YouTube账户列表失败');
}
} catch (error) {
console.error('获取YouTube账户列表错误:', error);
this.$message.error('获取YouTube账户列表失败');
}
},
handleAccountChange(accountId) {
this.form.youtubeAccountId = accountId;
},
openMaterialDialog() {
this.materialDialogVisible = true;
this.fetchMaterials();
},
fetchMaterials() {
getVideo({ page: this.materialCurrentPage - 1, size: this.materialPageSize })
.then(response => {
if (response.status === 200) {
this.materialTableData = response.result.data;
this.$nextTick(() => {
const selectedVideoIds = this.videoList.map(video => video.id);
this.materialTableData.forEach(row => {
if (selectedVideoIds.includes(row.id)) {
this.$refs.materialTable.toggleRowSelection(row, true);
}
});
});
} else {
this.$message.error('获取素材列表失败');
}
})
.catch(error => {
console.error('Error fetching materials:', error);
this.$message.error('获取素材列表失败');
});
},
handleMaterialSelectionChange(val) {
this.selectedMaterials = val;
},
handleMaterialCancel() {
this.materialDialogVisible = false;
this.selectedMaterials = [];
this.$refs.materialTable.clearSelection();
},
confirmMaterialSelection() {
if (this.selectedMaterials.length === 0) {
this.$message.warning('请选择要添加的素材');
return;
}
this.selectedMaterials.forEach(newVideo => {
const isDuplicate = this.videoList.some(existingVideo => existingVideo.id === newVideo.id);
if (!isDuplicate) {
this.videoList.push({
...newVideo,
name: newVideo.materialName,
format: newVideo.materialName,
progress: '0',
thumbnail: newVideo.url
});
}
});
this.$message.success('添加素材成功');
this.materialDialogVisible = false;
this.selectedMaterials = [];
this.$refs.materialTable.clearSelection();
},
handleMaterialPageSizeChange(val) {
this.materialPageSize = val;
this.fetchMaterials();
},
handleMaterialPageCurrentChange(val) {
this.materialCurrentPage = val;
this.fetchMaterials();
},
fetchTotalVideoNumber() {
getTotalVideoNumber()
.then(response => {
if (response.status === 200) {
this.totalVideoNumber = response.result.data;
} else {
this.$message.error('获取视频总数失败');
}
})
.catch(error => {
console.error('Error fetching total video number:', error);
this.$message.error('获取视频总数失败');
});
},
handleDelete(index, row) {
this.$confirm('确定要删除这个视频吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.videoList.splice(index, 1);
this.$message.success('删除成功');
}).catch(() => {
// 取消删除,不做任何处理
});
},
handleClearList() {
if (this.videoList.length === 0) {
return;
}
this.$confirm('确定要清空当前列表吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.videoList = [];
this.$message.success('列表已清空');
}).catch(() => {
// 取消操作,不需要做任何处理
});
},
tableRowClassName({ rowIndex }) {
if (rowIndex % 2 === 1) {
return 'blue-row'
}
return ''
}
}
}
</script>
<style>
.upload-form-container {
padding: 20px;
/* max-width: 1200px; */
margin: 0 auto;
background-color: #fff;
}
.form-section {
margin-bottom: 24px;
}
.section-label {
font-size: 14px;
color: #606266;
margin-bottom: 8px;
font-weight: bold;
}
.button-group {
margin-bottom: 16px;
display: flex;
gap: 8px;
}
.video-thumbnail {
width: 60px;
height: 45px;
object-fit: cover;
border-radius: 2px;
}
.operation-cell {
display: flex;
justify-content: space-between;
align-items: center;
}
.progress-text {
color: #909399;
}
.delete-button {
color: #409EFF;
}
.blue-row {
background-color: #f0f7ff !important;
}
.form-item-container {
margin-bottom: 20px;
}
.radio-group-container {
margin-bottom: 20px;
}
.playlist-container {
display: flex;
gap: 16px;
align-items: flex-start;
}
.privacy-container {
margin-bottom: 16px;
}
.privacy-sub-option {
margin-top: 8px;
margin-left: 24px;
}
.char-count {
text-align: right;
color: #909399;
font-size: 12px;
margin-top: 4px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #EBEEF5;
}
/* Element UI overrides */
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: normal;
}
.el-radio {
margin-right: 30px;
margin-bottom: 12px;
}
.el-radio__label {
font-weight: normal;
}
.el-textarea__inner {
font-family: inherit;
}
.el-button--primary {
background-color: #1890ff;
border-color: #1890ff;
}
.el-button--primary:hover,
.el-button--primary:focus {
background-color: #40a9ff;
border-color: #40a9ff;
}
.el-select .el-input__inner {
border-radius: 2px;
}
</style>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment