Commit 5505c443 authored by hzl's avatar hzl

feat: 处理资源组需求

parent 0ddc5d15
<template>
<div class="resource-group-selector">
<el-select
v-model="selectedGroups"
multiple
filterable
placeholder="请选择资源组"
@change="handleChange"
style="width: 100%">
<el-option
v-for="group in resourceGroups"
:key="group.id"
:label="group.name"
:value="group.id">
<span style="float: left">{{ group.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ group.id }}</span>
</el-option>
</el-select>
</div>
</template>
<script>
import { getAllResourceGroups } from '@/api/resourceGroup'
export default {
name: 'ResourceGroupSelector',
props: {
value: {
type: Array,
default: () => []
}
},
data() {
return {
resourceGroups: [],
selectedGroups: []
}
},
watch: {
value: {
immediate: true,
handler(newVal) {
this.selectedGroups = Array.isArray(newVal) ? [...newVal] : []
}
}
},
created() {
this.fetchResourceGroups()
},
methods: {
async fetchResourceGroups() {
try {
const response = await getAllResourceGroups()
if (response.status === 200 && response.result && response.result.data) {
this.resourceGroups = response.result.data
} else {
this.$message.error('获取资源组列表失败')
}
} catch (error) {
console.error('获取资源组列表失败:', error)
this.$message.error('获取资源组列表失败')
}
},
handleChange(value) {
this.selectedGroups = value
this.$emit('input', value)
this.$emit('change', value)
}
}
}
</script>
<style scoped>
.resource-group-selector {
width: 100%;
}
</style>
......@@ -162,6 +162,12 @@ export const constantRouterMap = [
component: () => import('@/views/materialGroup'),
meta: { title: '素材组管理' }
},
{
path: '/assetManagement/resource-group',
name: 'assetManagement.resource-group',
component: () => import('@/views/resourceGroup'),
meta: { title: '资源组管理' }
},
{
path: "/assetManagement/materialManage",
......
......@@ -27,6 +27,7 @@
<el-submenu index="4">
<template slot="title">资产管理</template>
<el-menu-item index="/assetManagement/material-group">素材组管理</el-menu-item>
<el-menu-item index="/assetManagement/resource-group">资源组管理</el-menu-item>
<el-menu-item index="/assetManagement/copywritingLibrary">文案管理</el-menu-item>
<el-menu-item index="/assetManagement/app-group">产品组管理</el-menu-item>
<el-menu-item index="/assetManagement/location-group">地域组管理</el-menu-item>
......
<template>
<div class="resource-group-container">
<!-- 过滤条件 -->
<div class="filter-section">
<el-form :inline="true" :model="condition" class="filter-form">
<el-form-item label="资源组名称">
<el-input
v-model="condition.name"
placeholder="请输入资源组名称"
clearable
@change="handleNameChange"
style="width: 200px;">
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</div>
<el-divider></el-divider>
<!-- 表格头部操作 -->
<div class="header-actions">
<el-button type="primary" @click="showAddDialog">新增资源组</el-button>
<div class="header-right">
<el-button icon="el-icon-refresh" @click="fetchData">刷新</el-button>
</div>
</div>
<!-- 主表格 -->
<el-table
:data="tableData"
border
style="width: 100%;"
v-loading="loading">
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="name" label="名称" min-width="200"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="250">
<template slot-scope="scope">
<span v-if="scope.row.remark">{{ scope.row.remark }}</span>
<span v-else style="color: #999;">暂无备注</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="120">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pagination.currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pagination.pageSize"
:total="pagination.total"
layout="total, prev, pager, next, sizes">
</el-pagination>
</div>
<!-- 新增资源组对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="60%">
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
<el-form-item label="资源组名称" prop="name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="上传素材">
<file-upload
:upload-url="uploadUrl"
:extra-data="extraData"
:headers="headers"
@update:files="handleFilesUpdate"
@upload-success="handleUploadSuccess"
@upload-error="handleUploadError"
@upload-complete="handleUploadComplete"
ref="uploadComponent"
>
<!-- 自定义上传按钮 -->
<template #trigger>
<el-button type="primary" icon="el-icon-upload">
选择图片或视频
</el-button>
</template>
</file-upload>
<!-- 显示已上传的文件列表 -->
<div v-if="form.uploadedFiles && form.uploadedFiles.length > 0" class="uploaded-files-list">
<h4>已上传文件:</h4>
<el-table :data="form.uploadedFiles" size="mini" style="width: 100%; margin-top: 10px;">
<el-table-column prop="name" label="文件名" width="200"></el-table-column>
<el-table-column prop="type" label="类型" width="100"></el-table-column>
<el-table-column prop="size" label="大小" width="100">
<template slot-scope="scope">
{{ formatFileSize(scope.row.size) }}
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="previewFile(scope.row)">预览</el-button>
<el-button type="text" size="mini" style="color: #f56c6c;" @click="removeFile(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" :rows="3"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="submitLoading">确定</el-button>
</div>
</el-dialog>
<!-- 资源组详情对话框 -->
<el-dialog
title="资源组详情"
:visible.sync="detailVisible"
width="80%"
:close-on-click-modal="false">
<div v-if="currentResourceGroup" class="detail-header">
<h3>{{ currentResourceGroup.name }}</h3>
<p v-if="currentResourceGroup.remark" class="remark">{{ currentResourceGroup.remark }}</p>
<p v-else class="remark" style="color: #999;">暂无备注</p>
</div>
<!-- 文件上传区域 -->
<div class="upload-section">
<h4>继续上传文件</h4>
<file-upload
:upload-url="uploadUrl"
:extra-data="extraData"
:headers="headers"
@update:files="handleDetailFilesUpdate"
@upload-success="handleDetailUploadSuccess"
@upload-error="handleUploadError"
@upload-complete="handleDetailUploadComplete"
ref="detailUploadComponent"
>
<!-- 自定义上传按钮 -->
<template #trigger>
<el-button type="primary" icon="el-icon-upload">
添加更多文件
</el-button>
</template>
</file-upload>
<!-- 显示待上传的文件列表 -->
<div v-if="detailUploadFiles && detailUploadFiles.length > 0" class="pending-upload-files">
<h5>待确认上传的文件:</h5>
<el-table :data="detailUploadFiles" size="mini" style="width: 100%; margin-top: 10px;">
<el-table-column prop="name" label="文件名" width="200"></el-table-column>
<el-table-column prop="type" label="类型" width="100"></el-table-column>
<el-table-column prop="size" label="大小" width="100">
<template slot-scope="scope">
{{ formatFileSize(scope.row.size) }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template slot-scope="scope">
<el-button type="text" size="mini" style="color: #f56c6c;" @click="removeDetailFile(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 确定上传按钮 -->
<div style="margin-top: 15px; text-align: right;">
<el-button
type="success"
icon="el-icon-check"
:loading="detailUploadLoading"
@click="confirmDetailUpload">
确定上传
</el-button>
</div>
</div>
</div>
<el-divider></el-divider>
<!-- 文件列表 -->
<div class="files-section">
<div class="section-header">
<h4>文件列表</h4>
<div class="file-search-controls">
<el-input
v-model="fileSearchName"
placeholder="搜索文件名"
clearable
style="width: 200px; margin-right: 10px;"
@change="handleFileSearch">
</el-input>
<el-button
type="primary"
size="small"
@click="handleFileSearch">
搜索
</el-button>
<el-button
type="text"
icon="el-icon-refresh"
@click="refreshFileList"
:loading="fileListLoading">
刷新
</el-button>
</div>
</div>
<el-table
:data="fileList"
v-loading="fileListLoading"
border
style="width: 100%;">
<el-table-column prop="id" label="文件ID" width="80"></el-table-column>
<el-table-column prop="materialName" label="文件名" min-width="200"></el-table-column>
<el-table-column prop="language" label="语言" width="100"></el-table-column>
<el-table-column prop="resType" label="类型" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.resType === 1 ? 'success' : 'warning'" size="small">
{{ scope.row.resType === 1 ? '图片' : '视频' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="上传时间" width="180">
<template slot-scope="scope">
{{ formatDate(scope.row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="previewFileFromList(scope.row)">预览</el-button>
</template>
</el-table-column>
</el-table>
<!-- 文件分页 -->
<div class="pagination-container" style="margin-top: 20px;">
<el-pagination
@size-change="handleFilePageSizeChange"
@current-change="handleFileCurrentChange"
:current-page="filePagination.currentPage"
:page-sizes="[10, 20, 50]"
:page-size="filePagination.pageSize"
:total="filePagination.total"
layout="total, prev, pager, next, sizes">
</el-pagination>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import FileUpload from '@/components/FileUpload'
import { getResourceGroupsByPage, createResourceGroup, updateResourceGroup, deleteResourceGroup, getResourceGroupDetailPage } from '@/api/resourceGroup'
import { addMaterial } from '@/api/report'
export default {
name: 'ResourceGroupManage',
components: {
FileUpload
},
data() {
return {
// 表格数据
tableData: [],
originalData: [], // 原始完整数据
loading: false,
// 分页
pagination: {
currentPage: 1,
pageSize: 10,
total: 0
},
// 过滤条件
condition: {
name: ''
},
// 对话框
dialogVisible: false,
dialogTitle: '',
form: {
id: null,
name: '',
uploadedFiles: [],
remark: ''
},
// 文件上传相关
uploadUrl: process.env.PUTIN_API + '/material/business/youtube/uploadToCDN',
extraData: {},
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token')
},
// 表单验证
rules: {
name: [
{ required: true, message: '请输入资源组名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在1-50个字符之间', trigger: 'blur' }
]
},
// 提交loading状态
submitLoading: false,
// 详情对话框相关
detailVisible: false,
currentResourceGroup: null,
// 文件列表相关
fileList: [],
fileListLoading: false,
filePagination: {
currentPage: 1,
pageSize: 10,
total: 0
},
// 详情页上传相关
detailUploadFiles: [],
detailUploadLoading: false,
// 文件搜索
fileSearchName: ''
}
},
created() {
this.fetchData()
},
methods: {
// 获取资源组数据
fetchData() {
this.loading = true
const params = {
name: this.condition.name || '',
page: this.pagination.currentPage,
pageSize: this.pagination.pageSize
}
getResourceGroupsByPage(params).then(response => {
this.loading = false
if (response.status === 200 && response.result && response.result.data && response.result.data.data) {
const data = response.result.data.data
this.tableData = data.value || []
this.pagination.total = data.count || 0
this.originalData = [...this.tableData]
} else {
this.$message.error('获取资源组数据失败')
}
}).catch(error => {
this.loading = false
console.error('获取资源组失败:', error)
this.$message.error('获取资源组失败: ' + error.message)
})
},
// 处理文件列表更新
handleFilesUpdate(files) {
this.form.uploadedFiles = files
console.log('文件列表更新:', files)
},
// 处理单个文件上传成功
handleUploadSuccess(file) {
console.log('文件上传成功:', file)
},
// 处理上传错误
handleUploadError({ file, error }) {
this.$message.error(`文件 ${file.name} 上传失败:${error.message}`)
},
// 处理所有文件上传完成
handleUploadComplete(files) {
this.$message.success(`文件上传完成,共 ${files.length} 个文件`)
},
// 格式化文件大小
formatFileSize(bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
},
// 预览文件
previewFile(file) {
if (file.type && file.type.includes('image')) {
// 图片预览
const h = this.$createElement
this.$msgbox({
title: file.name,
message: h('img', {
attrs: {
src: file.url,
style: 'max-width: 100%; max-height: 400px;'
}
}),
showCancelButton: false,
confirmButtonText: '关闭'
})
} else if (file.type && file.type.includes('video')) {
// 视频预览
const h = this.$createElement
this.$msgbox({
title: file.name,
message: h('video', {
attrs: {
src: file.url,
controls: true,
style: 'max-width: 100%; max-height: 400px;'
}
}),
showCancelButton: false,
confirmButtonText: '关闭'
})
} else {
this.$message.info('该文件类型不支持预览')
}
},
// 删除文件
removeFile(index) {
this.$confirm('确定要删除这个文件吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.form.uploadedFiles.splice(index, 1)
this.$message.success('文件已删除')
}).catch(() => {
// 用户取消删除
})
},
// 重置过滤条件
resetFilter() {
this.condition = {
tags: []
}
this.fetchData()
},
// 处理分页大小变化
handleSizeChange(val) {
this.pagination.pageSize = val
this.fetchData()
},
// 处理页码变化
handleCurrentChange(val) {
this.pagination.currentPage = val
this.fetchData()
},
// 显示新增对话框
showAddDialog() {
// 先重置表单
this.form = {
id: null,
name: '',
uploadedFiles: [],
remark: ''
}
// 设置对话框标题并显示
this.dialogTitle = '新增资源组'
this.dialogVisible = true
// 重置表单验证
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.resetFields()
}
})
},
// 处理详情
handleDetail(row) {
this.currentResourceGroup = row
this.detailVisible = true
this.filePagination.currentPage = 1
this.fileSearchName = '' // 重置搜索条件
this.fetchFileList()
},
// 处理删除
handleDelete(row) {
this.$confirm('确认删除该资源组吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteResourceGroup(row.id).then(response => {
if (response.status === 200) {
this.$message.success('删除成功')
this.fetchData()
} else {
this.$message.error('删除失败: ' + response.message)
}
}).catch(error => {
console.error('删除资源组失败:', error)
this.$message.error('删除失败: ' + error.message)
})
}).catch(() => {
// 取消删除
})
},
// 提交表单
submitForm() {
this.$refs.form.validate(valid => {
if (valid) {
this.submitLoading = true
if (this.form.id) {
// 更新资源组
const formData = {
resourceGroupName: this.form.name,
remark: this.form.remark,
successFiles: this.form.uploadedFiles
}
updateResourceGroup(this.form.id, formData).then(response => {
if (response.status === 200) {
this.$message.success('更新成功')
this.dialogVisible = false
this.fetchData()
} else {
this.$message.error('更新失败: ' + (response.message || '未知错误'))
}
}).catch(error => {
console.error('更新资源组失败:', error)
this.$message.error('更新失败: ' + error.message)
}).finally(() => {
this.submitLoading = false
})
} else {
// 新增资源组
const formData = {
resourceGroupName: this.form.name,
remark: this.form.remark,
successFiles: this.form.uploadedFiles
}
createResourceGroup(formData).then(response => {
if (response.status === 200) {
this.$message.success('创建成功')
this.dialogVisible = false
this.fetchData()
} else {
this.$message.error('创建失败: ' + (response.message || '未知错误'))
}
}).catch(error => {
console.error('创建资源组失败:', error)
this.$message.error('创建失败: ' + error.message)
}).finally(() => {
this.submitLoading = false
})
}
} else {
return false
}
})
},
// 处理名称变化
handleNameChange() {
// 可以在这里添加防抖逻辑
},
// 处理搜索
handleSearch() {
this.pagination.currentPage = 1
this.fetchData()
},
// 处理重置
handleReset() {
this.condition.name = ''
this.pagination.currentPage = 1
this.fetchData()
},
// 获取文件列表
fetchFileList() {
if (!this.currentResourceGroup) return
this.fileListLoading = true
const params = {
resourceGroupId: this.currentResourceGroup.id,
materialName: this.fileSearchName || '',
page: this.filePagination.currentPage,
pageSize: this.filePagination.pageSize
}
getResourceGroupDetailPage(params).then(response => {
this.fileListLoading = false
if (response.status === 200 && response.result && response.result.data && response.result.data.data) {
const data = response.result.data.data
this.fileList = data.value || []
this.filePagination.total = data.count || 0
} else {
this.$message.error('获取文件列表失败')
}
}).catch(error => {
this.fileListLoading = false
console.error('获取文件列表失败:', error)
this.$message.error('获取文件列表失败: ' + error.message)
})
},
// 刷新文件列表
refreshFileList() {
this.fetchFileList()
},
// 处理详情页文件上传更新
handleDetailFilesUpdate(files) {
this.detailUploadFiles = files
console.log('详情页文件更新:', files)
},
// 处理详情页文件上传成功
handleDetailUploadSuccess(file) {
console.log('详情页文件上传成功:', file)
},
// 处理详情页文件上传完成
handleDetailUploadComplete(files) {
console.log('详情页文件上传完成:', files)
// 文件上传到CDN完成,但还需要用户点击确定上传按钮才保存到数据库
},
// 删除详情页待上传文件
removeDetailFile(index) {
this.detailUploadFiles.splice(index, 1)
},
// 确定上传详情页的文件
confirmDetailUpload() {
if (!this.detailUploadFiles || this.detailUploadFiles.length === 0) {
this.$message.warning('请先选择要上传的文件')
return
}
this.detailUploadLoading = true
const materialData = {
successFiles: this.detailUploadFiles,
tags: [], // 不需要标签
designer: null, // 不需要设计师
directoryId: null, // 不需要目录
resourceGroupId: this.currentResourceGroup.id // 关联到当前资源组
}
addMaterial(materialData).then(response => {
if (response.status === 200) {
this.$message.success('文件上传成功')
// 清空待上传文件列表
this.detailUploadFiles = []
// 重置上传组件状态
if (this.$refs.detailUploadComponent) {
this.$refs.detailUploadComponent.reset()
}
// 刷新文件列表
this.refreshFileList()
} else {
this.$message.error('文件上传失败: ' + (response.message || '未知错误'))
}
}).catch(error => {
console.error('文件上传失败:', error)
this.$message.error('文件上传失败: ' + error.message)
}).finally(() => {
this.detailUploadLoading = false
})
},
// 处理文件分页大小变化
handleFilePageSizeChange(val) {
this.filePagination.pageSize = val
this.fetchFileList()
},
// 处理文件页码变化
handleFileCurrentChange(val) {
this.filePagination.currentPage = val
this.fetchFileList()
},
// 从文件列表预览文件
previewFileFromList(file) {
if (file.resType === 1) {
// 图片预览
const h = this.$createElement
this.$msgbox({
title: file.materialName,
message: h('img', {
attrs: {
src: file.url,
style: 'max-width: 100%; max-height: 400px;'
}
}),
showCancelButton: false,
confirmButtonText: '关闭'
})
} else if (file.resType === 2) {
// 视频预览
const h = this.$createElement
this.$msgbox({
title: file.materialName,
message: h('video', {
attrs: {
src: file.url,
controls: true,
style: 'max-width: 100%; max-height: 400px;'
}
}),
showCancelButton: false,
confirmButtonText: '关闭'
})
} else {
this.$message.info('该文件类型不支持预览')
}
},
// 格式化日期
formatDate(date) {
if (!date) return ''
const d = new Date(date)
return d.toLocaleString('zh-CN')
},
// 处理文件搜索
handleFileSearch() {
this.filePagination.currentPage = 1
this.fetchFileList()
}
}
}
</script>
<style scoped>
.resource-group-container {
padding: 20px;
}
.filter-section {
margin-bottom: 20px;
}
.header-actions {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.pagination-container {
margin-top: 20px;
text-align: right;
}
.no-tags {
color: #999;
font-size: 12px;
}
.tag-item {
margin: 2px;
}
.uploaded-files-list {
margin-top: 15px;
padding: 15px;
border: 1px solid #EBEEF5;
border-radius: 4px;
background-color: #FAFAFA;
}
.uploaded-files-list h4 {
margin: 0 0 10px 0;
color: #303133;
font-size: 14px;
}
/* 详情页面样式 */
.detail-header {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
}
.detail-header h3 {
margin: 0 0 10px 0;
color: #303133;
font-size: 18px;
}
.detail-header .remark {
margin: 0;
color: #606266;
font-size: 14px;
}
.upload-section {
margin-bottom: 20px;
padding: 15px;
border: 1px dashed #d9d9d9;
border-radius: 4px;
background-color: #fafafa;
}
.upload-section h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
}
.files-section h4 {
margin: 0 0 15px 0;
color: #303133;
font-size: 16px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.section-header h4 {
margin: 0;
color: #303133;
font-size: 16px;
}
/* 待上传文件列表样式 */
.pending-upload-files {
margin-top: 20px;
padding: 15px;
border: 1px solid #e6f7ff;
border-radius: 4px;
background-color: #f6ffed;
}
.pending-upload-files h5 {
margin: 0 0 10px 0;
color: #52c41a;
font-size: 14px;
}
/* 文件搜索控件样式 */
.file-search-controls {
display: flex;
align-items: center;
}
</style>
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