Commit ef23ad84 authored by lijin's avatar lijin

优化 youtube页面

parent 4e5d4494
......@@ -478,11 +478,12 @@ export function getVideoLog(params) {
}
export function getVideoLogCount() {
export function getVideoLogCount(params) {
return request({
// url: 'http://localhost:8567/youtube/getCount',
url: process.env.PUTIN_API + '/youtube/getCount',
method: 'get',
params
})
}
......@@ -639,5 +640,15 @@ export function addMaterial(data) {
data
})
}
export function retryUploadYoutube(params) {
return request({
url: process.env.PUTIN_API + '/youtube/retryUploadYoutube',
method: 'post',
params
})
}
// ----------------------------------------
......@@ -47,7 +47,7 @@ Vue.filter("toFixed", function (price, limit) {
});
router.beforeEach((to, from, next) => {
if (to.path == "/login" || to.path == "/materialUpload" || to.path == "/materialTag") {
if (to.path == "/login" || to.path == "/materialUpload" || to.path == "/materialTag" || to.path == "/assetManagement/uploadYoutubeNew") {
next();
} else {
gatewayUserRouters().then(res => {
......
......@@ -102,6 +102,16 @@ export const constantRouterMap = [
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",
component: () => import("@/views/uploadYoutube/YoutubeVideoManage"),
meta: { title: "Youtube视频管理", icon: "chart" }
}
]
},
......
......@@ -23,10 +23,12 @@
</el-submenu>
<el-submenu index="3">
<template slot="title">Youtube工具</template>
<el-menu-item index="/assetManagement/googleOauthYoutube">账号管理</el-menu-item>
<el-menu-item index="/assetManagement/uploadYoutube">视频上传</el-menu-item>
<el-menu-item index="/assetManagement/uploadYoutubeLog">上传结果</el-menu-item>
<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>
</div>
......
<template>
<div class="youtube-list-container">
<!-- Filter section -->
<div class="filter-section">
<el-form :inline="true" :model="condition" class="filter-form">
<el-form-item label="频道">
<el-select class="filter-select"
v-model="condition.selectedAccount"
placeholder="请选择"
clearable
@change="fetchData">
<el-option
v-for="account in youtubeAccounts"
:key="account.id"
:label="account.alias"
:value="account.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="名称">
<el-input
v-model="condition.name"
placeholder="请输入"
class="filter-input">
</el-input>
</el-form-item>
<el-form-item label="标题">
<el-input
v-model="condition.title"
placeholder="请输入"
class="filter-input">
</el-input>
</el-form-item>
<el-form-item label="上传状态">
<el-select
v-model="condition.status"
placeholder="请选择"
class="filter-select"
@change="fetchData">
<el-option label="全部" value=""></el-option>
<el-option label="成功" value="SUCCESS"></el-option>
<el-option label="失败" value="FAILED"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="限制">-->
<!-- <el-select-->
<!-- v-model="condition.restriction"-->
<!-- placeholder="请选择"-->
<!-- class="filter-select">-->
<!-- <el-option label="全部" value=""></el-option>-->
<!-- <el-option label="无限制" value="none"></el-option>-->
<!-- <el-option label="有限制" value="restricted"></el-option>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="播放列表">-->
<!-- <el-select-->
<!-- v-model="condition.playlist"-->
<!-- placeholder="请选择"-->
<!-- class="filter-select">-->
<!-- <el-option label="全部" value=""></el-option>-->
<!-- <el-option label="列表1" value="list1"></el-option>-->
<!-- <el-option label="列表2" value="list2"></el-option>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
</el-form>
</div>
<el-divider></el-divider>
<!-- table Header actions -->
<div class="header-actions">
<el-button type="primary" @click="goToUpload">上传</el-button>
<el-dropdown>
<el-button>
批量操作<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="batchRetry">批量重试</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="header-right">
<el-button icon="el-icon-refresh" @click="fetchData">刷新</el-button>
<!-- <el-button icon="el-icon-back">导出</el-button>-->
<!-- <el-button icon="el-icon-user">登出</el-button>-->
</div>
</div>
<!-- Main table -->
<el-table
:data="tableData"
border
style="width: 100%"
:row-class-name="tableRowClassName"
v-loading="dataLoading"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="ID"
width="50">
</el-table-column>
<el-table-column
label="缩略图"
width="100">
<template slot-scope="scope">
<img :src="`${scope.row.ossUrl}?x-oss-process=video/snapshot,t_1000,scaletype_fill`" class="video-thumbnail" />
</template>
</el-table-column>
<el-table-column
prop="title"
label="标题"
width="180">
</el-table-column>
<el-table-column
prop="youtubeAccountName"
label="频道"
width="120">
</el-table-column>
<el-table-column
prop="videoId"
label="视频ID"
width="150">
</el-table-column>
<el-table-column
prop="videoUrl"
label="视频播放地址">
<template slot-scope="scope">
<el-link type="primary" :href="`https://www.youtube.com/watch?v=${scope.row.videoId}`" target="_blank">
https://www.youtube.com/watch?v={{ scope.row.videoId }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="materialBusinessId" label="Local ID" width="120"></el-table-column>
<el-table-column
prop="status"
label="状态"
width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.uploadStatus === 'SUCCESS' ? 'success' : 'danger'">
{{ scope.row.uploadStatus === 'SUCCESS' ? '成功' : '失败' }}
</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="名称"
width="250">
</el-table-column>
<!-- <el-table-column-->
<!-- prop="contentRating"-->
<!-- label="内容是否面向儿童"-->
<!-- width="150">-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- prop="viewCount"-->
<!-- label="观看次数"-->
<!-- width="100">-->
<!-- </el-table-column>-->
<el-table-column
prop="uploadEndTime"
label="上传时间"
align="center"
width="180">
<template slot-scope="scope">
{{ scope.row.uploadEndTime ? scope.row.uploadEndTime.substring(0, 10) : '' }}
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
width="120">
<template slot-scope="scope">
<el-button
v-if="scope.row.uploadStatus === 'FAILED'"
size="mini"
type="primary"
:loading="scope.row.retryStatus === 'running'"
:disabled="scope.row.retryStatus === 'running' || scope.row.retryStatus === 'complete'"
@click="retryUploadYoutube(scope.row)">
重新上传
</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="total"
layout="total, prev, pager, next, sizes"
>
</el-pagination>
</div>
</div>
</template>
<script>
import {
getYoutubeAccountList,
getVideoLog,
getVideoLogCount,
retryUploadYoutube } from '@/api/report';
export default {
name: 'YouTubeList',
data() {
return {
dataLoading: false,
currentPage: 1,
pageSize: 10,
total: 0,
condition: {
channel: '',
name: '',
format: '',
status: '',
restriction: '',
playlist: '',
selectedAccount: ''
},
tableData: [],
youtubeAccounts: [],
}
},
created() {
this.fetchYoutubeAccounts();
this.fetchData();
},
methods: {
goToUpload() {
window.open('/assetManagement/uploadYoutube');
},
async retryUploadYoutube(row) {
this.$set(row, 'retryStatus', "running");
try {
const response = await retryUploadYoutube({
id: row.id
});
if (response.status === 200) {
this.$message.success('提交成功');
this.$set(row, 'retryStatus', "complete");
} else {
this.$message.error('提交失败');
this.$set(row, 'retryStatus', "default");
}
} catch (error) {
this.$message.error('提交失败');
this.$set(row, 'retryStatus', "default");
} finally {
}
},
tableRowClassName({ rowIndex }) {
if (rowIndex % 2 === 1) {
return 'blue-row'
}
return ''
},
handleSelectionChange(selection) {
console.log('Selected rows:', selection)
},
fetchData() {
this.dataLoading=true
Promise.all([
getVideoLog({page: this.currentPage - 1, size: this.pageSize, status: this.condition.status, youtube_account_id: this.condition.selectedAccount}),
getVideoLogCount({status: this.condition.status, youtube_account_id: this.condition.selectedAccount})
])
.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('获取数据失败');
}).finally(() => {
this.dataLoading = false
});
},
handleSizeChange(val) {
this.pageSize = val;
this.currentPage = 1;
this.fetchData();
},
handleCurrentChange(val) {
this.currentPage = val;
this.fetchData();
},
fetchYoutubeAccounts() {
getYoutubeAccountList()
.then(response => {
if (response.status === 200) {
this.youtubeAccounts = response.result.data;
} else {
this.$message.error('获取YouTube账户列表失败');
}
})
.catch(error => {
console.error('Error fetching YouTube accounts:', error);
this.$message.error('获取YouTube账户列表失败');
});
},
},
watch: {
condition: {
handler(newVal) {
console.log('Filter changed:', newVal)
// Implement your filter logic here
},
deep: true
}
}
}
</script>
<style>
.youtube-list-container {
padding: 20px;
background-color: #fff;
}
.header-actions {
display: flex;
align-items: center;
margin-bottom: 20px;
gap: 10px;
}
.header-right {
margin-left: auto;
display: flex;
gap: 10px;
}
.filter-section {
/*margin-bottom: 20px;*/
/*padding: 16px;*/
}
.filter-form {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.filter-select {
width: 180px;
}
.filter-input {
width: 180px;
}
.video-thumbnail {
width: 60px;
height: 45px;
object-fit: cover;
border-radius: 2px;
}
.blue-row {
background-color: #f0f7ff !important;
}
/* Element UI overrides */
.el-form--inline .el-form-item {
margin-right: 10px;
margin-bottom: 10px;
}
.el-table th {
background-color: #f5f7fa;
color: #606266;
font-weight: normal;
padding: 8px 0;
}
.el-table td {
padding: 8px 0;
}
.el-table--enable-row-hover .el-table__body tr:hover > td {
background-color: #f5f7fa;
}
.el-button--primary {
background-color: #1890ff;
border-color: #1890ff;
}
.el-button--primary:hover,
.el-button--primary:focus {
background-color: #40a9ff;
border-color: #40a9ff;
}
.el-link {
color: #1890ff;
}
.el-link:hover {
color: #40a9ff;
}
.el-select .el-input__inner,
.el-input__inner {
border-radius: 2px;
}
/* Previous styles remain the same, add pagination styles */
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
padding: 10px 0;
}
/* Customize Element UI pagination to match the design */
.el-pagination {
font-weight: normal;
}
.el-pagination .el-select .el-input {
width: 110px;
}
.el-pagination .el-select .el-input__inner {
padding-right: 25px;
}
.el-pagination .btn-prev,
.el-pagination .btn-next {
background: transparent;
}
.el-pagination .el-pager li {
background: transparent;
border: none;
}
.el-pagination .el-pager li.active {
color: #1890ff;
background-color: #e6f7ff;
border-radius: 2px;
}
.el-pagination__total {
margin-right: 10px;
}
.el-pagination__sizes {
margin-left: 10px;
}
</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