feat: 小优化和作品库
This commit is contained in:
parent
b7d15052cd
commit
73f4fcdaf6
|
|
@ -1,13 +1,14 @@
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
import zh_HK from '@/lang/zh_HK/index.js'
|
import zh_HK from '@/lang/zh_HK/index.js'
|
||||||
|
/**
|
||||||
import en_US from '@/lang/en_US/index.js'
|
import en_US from '@/lang/en_US/index.js'
|
||||||
import es_ES from '@/lang/es_ES/index.js'
|
mport es_ES from '@/lang/es_ES/index.js'
|
||||||
import pt_BR from '@/lang/pt_BR/index.js'
|
import pt_BR from '@/lang/pt_BR/index.js'
|
||||||
import hi_IN from '@/lang/hi_IN/index.js'
|
import hi_IN from '@/lang/hi_IN/index.js'
|
||||||
import ru_RU from '@/lang/ru_RU/index.js'
|
import ru_RU from '@/lang/ru_RU/index.js'
|
||||||
import ar_SA from '@/lang/ar_SA/index.js'
|
import ar_SA from '@/lang/ar_SA/index.js'
|
||||||
import fr_FR from '@/lang/fr_FR/index.js'
|
import fr_FR from '@/lang/fr_FR/index.js'**/
|
||||||
|
|
||||||
// 多语言切换已禁用:全站固定繁体中文
|
// 多语言切换已禁用:全站固定繁体中文
|
||||||
let locale = 'zh_HK'
|
let locale = 'zh_HK'
|
||||||
|
|
@ -15,13 +16,13 @@ let locale = 'zh_HK'
|
||||||
/** 各语言在界面上的显示名称 */
|
/** 各语言在界面上的显示名称 */
|
||||||
export const LOCALE_NAMES = {
|
export const LOCALE_NAMES = {
|
||||||
zh_HK: '繁体中文',
|
zh_HK: '繁体中文',
|
||||||
en_US: 'English',
|
/** en_US: 'English',
|
||||||
es_ES: 'Español',
|
es_ES: 'Español',
|
||||||
pt_BR: 'Português',
|
pt_BR: 'Português',
|
||||||
hi_IN: 'हिन्दी',
|
hi_IN: 'हिन्दी',
|
||||||
ru_RU: 'Русский',
|
ru_RU: 'Русский',
|
||||||
ar_SA: 'العربية',
|
ar_SA: 'العربية',
|
||||||
fr_FR: 'Français'
|
fr_FR: 'Français'**/
|
||||||
}
|
}
|
||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
|
|
@ -29,13 +30,13 @@ const i18n = createI18n({
|
||||||
locale,
|
locale,
|
||||||
messages: {
|
messages: {
|
||||||
zh_HK,
|
zh_HK,
|
||||||
en_US,
|
/** en_US,
|
||||||
es_ES,
|
es_ES,
|
||||||
pt_BR,
|
pt_BR,
|
||||||
hi_IN,
|
hi_IN,
|
||||||
ru_RU,
|
ru_RU,
|
||||||
ar_SA,
|
ar_SA,
|
||||||
fr_FR
|
fr_FR**/
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import en_USLocale from './en/index'
|
//import en_USLocale from './en/index'
|
||||||
import zh_HKLocale from './zh_HK/index'
|
import zh_HKLocale from './zh_HK/index'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
en_US: en_USLocale,
|
//en_US: en_USLocale,
|
||||||
zh_HK: zh_HKLocale
|
zh_HK: zh_HKLocale
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@ export default {
|
||||||
help: '幫助中心',
|
help: '幫助中心',
|
||||||
moneyInvite: '有獎邀請',
|
moneyInvite: '有獎邀請',
|
||||||
assetGroupManage: '資源組管理',
|
assetGroupManage: '資源組管理',
|
||||||
assetManage: '素材管理'
|
assetManage: '素材管理',
|
||||||
|
generatedAssets: '作品库'
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ import { constantRoutes } from '@/router/index'
|
||||||
import { generateTitle, generateLang } from '@/utils/i18n'
|
import { generateTitle, generateLang } from '@/utils/i18n'
|
||||||
|
|
||||||
/** 左侧导航仅显示这些路由(name 与 router/index.js 一致) */
|
/** 左侧导航仅显示这些路由(name 与 router/index.js 一致) */
|
||||||
const SIDEBAR_ONLY_ROUTE_NAMES = ['video-gen', 'asset-group-manage', 'asset-manage']
|
const SIDEBAR_ONLY_ROUTE_NAMES = ['video-gen', 'asset-group-manage', 'asset-manage', 'generatedAssets']
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
collapsed: Boolean
|
collapsed: Boolean
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,16 @@ export const constantRoutes = [{
|
||||||
permission: "pass",
|
permission: "pass",
|
||||||
icon: 'btn_video'
|
icon: 'btn_video'
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
path: 'generated-assets',
|
||||||
|
name: 'generatedAssets',
|
||||||
|
component: () => import('@/views/GeneratedAssets.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'generatedAssets',
|
||||||
|
menuItem: true,
|
||||||
|
permission: "pass",
|
||||||
|
icon: 'btn_video'
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
path: 'recharge',
|
path: 'recharge',
|
||||||
name: 'recharge',
|
name: 'recharge',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,961 @@
|
||||||
|
<template>
|
||||||
|
<div class="generated-assets-page">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="panel-title">作品库</div>
|
||||||
|
</div>
|
||||||
|
<div class="view-tabs">
|
||||||
|
<a-tabs v-model="activeTab" @change="handleTabChange" type="card">
|
||||||
|
<a-tab-pane key="personal" title="个人" />
|
||||||
|
<a-tab-pane key="department" title="部门" />
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
<!-- 查询区域 -->
|
||||||
|
<div class="query-section">
|
||||||
|
<div class="form-grid">
|
||||||
|
<div class="field">
|
||||||
|
<label>收藏状态</label>
|
||||||
|
<a-select v-model="filters.isTop" clearable placeholder="全部">
|
||||||
|
<a-option :value="null">全部</a-option>
|
||||||
|
<a-option value="Y">已收藏</a-option>
|
||||||
|
<a-option value="N">未收藏</a-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>开始时间</label>
|
||||||
|
<a-date-picker v-model="filters.beginTime" placeholder="选择开始时间" style="width: 100%" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>结束时间</label>
|
||||||
|
<a-date-picker v-model="filters.endTime" placeholder="选择结束时间" style="width: 100%" />
|
||||||
|
</div>
|
||||||
|
<div class="field actions">
|
||||||
|
<a-button type="primary" :loading="loading" @click="search(1)">查询</a-button>
|
||||||
|
<a-button @click="resetFilters">重置</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表格区域 -->
|
||||||
|
<a-spin :loading="loading">
|
||||||
|
<div class="total-line">总数:{{ pagination.total }}</div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="asset-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>订单编号</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>模型参数</th>
|
||||||
|
<th>生成结果</th>
|
||||||
|
<th>创建时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="item in dataList" :key="item.id">
|
||||||
|
<td class="td-id">{{ item.id }}</td>
|
||||||
|
<td>{{ item.orderNum || '-' }}</td>
|
||||||
|
<td>{{ formatType(item.type) }}</td>
|
||||||
|
<td>
|
||||||
|
<a-tag :color="getStatusColor(item.status)">
|
||||||
|
{{ formatStatus(item.status) }}
|
||||||
|
</a-tag>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="params-cell">
|
||||||
|
<div v-if="item.model" class="param-tag">模型: {{ item.model }}</div>
|
||||||
|
<div v-if="item.duration" class="param-tag">时长: {{ item.duration }}s</div>
|
||||||
|
<div v-if="item.resolution" class="param-tag">分辨率: {{ item.resolution }}</div>
|
||||||
|
<div v-if="item.ratio" class="param-tag">比例: {{ item.ratio }}</div>
|
||||||
|
<div v-if="item.mode" class="param-tag">模式: {{ formatMode(item.mode) }}</div>
|
||||||
|
<div v-if="!item.model && !item.duration && !item.resolution && !item.ratio && !item.mode" class="param-empty">-</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="result-cell">
|
||||||
|
<div v-if="isVideoResult(item.result)" class="media-preview">
|
||||||
|
<video
|
||||||
|
class="video-thumb"
|
||||||
|
:src="item.result"
|
||||||
|
controls
|
||||||
|
preload="metadata"
|
||||||
|
@click.stop="openPreview(item.result, 'video')" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="isImageResult(item.result)" class="media-preview">
|
||||||
|
<img
|
||||||
|
class="image-thumb"
|
||||||
|
:src="item.result"
|
||||||
|
@click.stop="openPreview(item.result, 'image')" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="item.result && item.result.startsWith('cgt-')" class="task-id">
|
||||||
|
{{ item.result }}
|
||||||
|
</div>
|
||||||
|
<div v-else class="result-empty">-</div>
|
||||||
|
</td>
|
||||||
|
<td>{{ item.createTime || '-' }}</td>
|
||||||
|
<td>
|
||||||
|
<a-button
|
||||||
|
size="mini"
|
||||||
|
:type="item.isTop === 'Y' ? 'primary' : 'outline'"
|
||||||
|
:status="item.isTop === 'Y' ? 'success' : 'default'"
|
||||||
|
@click="toggleFavorite(item)">
|
||||||
|
<template v-if="item.isTop === 'Y'">
|
||||||
|
<a-icon name="star-fill" /> 已收藏
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-icon name="star" /> 收藏
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
size="mini"
|
||||||
|
type="outline"
|
||||||
|
@click="viewDetail(item)">
|
||||||
|
详情
|
||||||
|
</a-button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="!dataList.length">
|
||||||
|
<td colspan="8" class="empty-tip">暂无数据</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="pager">
|
||||||
|
<a-pagination
|
||||||
|
:total="pagination.total"
|
||||||
|
:current="pagination.pageNum"
|
||||||
|
:page-size="pagination.pageSize"
|
||||||
|
show-total
|
||||||
|
show-jumper
|
||||||
|
show-page-size
|
||||||
|
:page-size-options="pagination.pageSizes"
|
||||||
|
@change="changePage"
|
||||||
|
@page-size-change="changePageSize" />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<a-modal v-model:visible="detailVisible" title="订单详情" :footer="false" width="850px">
|
||||||
|
<div class="detail-content" v-if="detailData">
|
||||||
|
<div class="detail-form">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="detail-group">
|
||||||
|
<div class="group-title">基本信息</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">订单编号</div>
|
||||||
|
<div class="value">{{ detailData.orderNum || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">功能类型</div>
|
||||||
|
<div class="value">{{ formatType(detailData.type) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">扣除积分</div>
|
||||||
|
<div class="value">{{ detailData.amount ? detailData.amount + ' 积分' : '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">订单状态</div>
|
||||||
|
<div class="value">
|
||||||
|
<a-tag :color="getStatusColor(detailData.status)">
|
||||||
|
{{ formatStatus(detailData.status) }}
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">执行状态</div>
|
||||||
|
<div class="value">{{ formatExtStatus(detailData.extStatus) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">收藏状态</div>
|
||||||
|
<div class="value">
|
||||||
|
<a-tag v-if="detailData.isTop === 'Y'" color="orange">已收藏</a-tag>
|
||||||
|
<span v-else class="text-muted">未收藏</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">请求来源</div>
|
||||||
|
<div class="value">{{ detailData.source || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 生成参数 -->
|
||||||
|
<div class="detail-group">
|
||||||
|
<div class="group-title">生成参数</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">提示词</div>
|
||||||
|
<div class="value long-text">{{ detailData.text || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">生成模式</div>
|
||||||
|
<div class="value">{{ formatMode(detailData.mode) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">使用模型</div>
|
||||||
|
<div class="value">{{ detailData.model || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">视频时长</div>
|
||||||
|
<div class="value">{{ detailData.duration ? detailData.duration + ' 秒' : '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">分辨率</div>
|
||||||
|
<div class="value">{{ detailData.resolution || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">画面比例</div>
|
||||||
|
<div class="value">{{ detailData.ratio || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 生成结果 -->
|
||||||
|
<div class="detail-group">
|
||||||
|
<div class="group-title">生成结果</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">生成内容</div>
|
||||||
|
<div class="value media-preview">
|
||||||
|
<video
|
||||||
|
v-if="isVideoResult(detailData.result)"
|
||||||
|
class="detail-video"
|
||||||
|
:src="detailData.result"
|
||||||
|
controls
|
||||||
|
preload="metadata" />
|
||||||
|
<img
|
||||||
|
v-else-if="isImageResult(detailData.result)"
|
||||||
|
class="detail-image"
|
||||||
|
:src="detailData.result"
|
||||||
|
@click="viewImageFull(detailData.result)" />
|
||||||
|
<div v-else class="result-text">{{ detailData.result || '无结果' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row" v-if="detailData.img1">
|
||||||
|
<div class="label">首帧图片</div>
|
||||||
|
<div class="value media-preview">
|
||||||
|
<img class="detail-image small" :src="detailData.img1" @click="viewImageFull(detailData.img1)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row" v-if="detailData.img2">
|
||||||
|
<div class="label">尾帧图片</div>
|
||||||
|
<div class="value media-preview">
|
||||||
|
<img class="detail-image small" :src="detailData.img2" @click="viewImageFull(detailData.img2)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 完整参数 -->
|
||||||
|
<div class="detail-group" v-if="detailData.videoParams">
|
||||||
|
<div class="group-title">完整参数</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">JSON参数</div>
|
||||||
|
<div class="value">
|
||||||
|
<pre class="json-block">{{ formatVideoParams(detailData.videoParams) }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 时间信息 -->
|
||||||
|
<div class="detail-group">
|
||||||
|
<div class="group-title">时间信息</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">创建时间</div>
|
||||||
|
<div class="value">{{ detailData.createTime || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<div class="label">更新时间</div>
|
||||||
|
<div class="value">{{ detailData.updateTime || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 预览弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="previewVisible"
|
||||||
|
:title="previewType === 'video' ? '视频预览' : '图片预览'"
|
||||||
|
:footer="false"
|
||||||
|
width="800px"
|
||||||
|
@cancel="closePreview">
|
||||||
|
<div class="preview-content">
|
||||||
|
<video
|
||||||
|
v-if="previewType === 'video' && previewUrl"
|
||||||
|
class="preview-video"
|
||||||
|
:src="previewUrl"
|
||||||
|
controls
|
||||||
|
autoplay />
|
||||||
|
<img
|
||||||
|
v-else-if="previewType === 'image' && previewUrl"
|
||||||
|
class="preview-image"
|
||||||
|
:src="previewUrl" />
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'GeneratedAssets',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
dataList: [],
|
||||||
|
filters: {
|
||||||
|
isTop: null,
|
||||||
|
beginTime: '',
|
||||||
|
endTime: ''
|
||||||
|
},
|
||||||
|
activeTab: 'personal', // 'personal'个人, 'department'部门
|
||||||
|
pagination: {
|
||||||
|
total: 0,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
pageSizes: [10, 20, 50, 100]
|
||||||
|
},
|
||||||
|
detailVisible: false,
|
||||||
|
detailData: null,
|
||||||
|
previewVisible: false,
|
||||||
|
previewUrl: '',
|
||||||
|
previewType: 'video'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.search()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 查询数据
|
||||||
|
search(page = 1) {
|
||||||
|
this.pagination.pageNum = page
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
pageNum: this.pagination.pageNum,
|
||||||
|
pageSize: this.pagination.pageSize
|
||||||
|
}
|
||||||
|
console.log("this.activeTab", this.activeTab)
|
||||||
|
// 视图模式参数:personal = 个人, department = 部门视图(传 dept=true)
|
||||||
|
if (this.activeTab === 'department') {
|
||||||
|
params.dept = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 明确传递所有查询条件 - 使用 is_top 作为参数名
|
||||||
|
if (this.filters.isTop != null && String(this.filters.isTop) !== '') {
|
||||||
|
// 如果isTop是null或者空字符串或者全部,则不传递
|
||||||
|
if (this.filters.isTop === null || this.filters.isTop === '' || this.filters.isTop === '全部') {
|
||||||
|
delete params.is_top
|
||||||
|
} else {
|
||||||
|
params.is_top = String(this.filters.isTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.filters.beginTime) {
|
||||||
|
params.beginTime = this.formatDate(this.filters.beginTime)
|
||||||
|
}
|
||||||
|
if (this.filters.endTime) {
|
||||||
|
params.endTime = this.formatDate(this.filters.endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.$axios({
|
||||||
|
url: 'api/portal/assets/list',
|
||||||
|
method: 'GET',
|
||||||
|
data: params
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = false
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.dataList = res.rows || []
|
||||||
|
this.pagination.total = res.total || 0
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '查询失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = false
|
||||||
|
this.$message.error(err?.message || '查询失败')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 重置筛选
|
||||||
|
resetFilters() {
|
||||||
|
this.filters.isTop = null
|
||||||
|
this.filters.beginTime = ''
|
||||||
|
this.filters.endTime = ''
|
||||||
|
this.search(1)
|
||||||
|
},
|
||||||
|
// Tabs 切换处理(personal/部门视图)
|
||||||
|
handleTabChange(tabKey) {
|
||||||
|
if (tabKey) {
|
||||||
|
this.activeTab = tabKey
|
||||||
|
}
|
||||||
|
this.pagination.pageNum = 1
|
||||||
|
this.search(1)
|
||||||
|
},
|
||||||
|
// 分页变化
|
||||||
|
changePage(page) {
|
||||||
|
this.pagination.pageNum = page
|
||||||
|
this.search(page)
|
||||||
|
},
|
||||||
|
// 每页条数变化
|
||||||
|
changePageSize(pageSize) {
|
||||||
|
this.pagination.pageSize = pageSize
|
||||||
|
this.pagination.pageNum = 1
|
||||||
|
this.search(1)
|
||||||
|
},
|
||||||
|
// 格式化日期
|
||||||
|
formatDate(date) {
|
||||||
|
if (!date) return ''
|
||||||
|
const d = new Date(date)
|
||||||
|
const year = d.getFullYear()
|
||||||
|
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(d.getDate()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
},
|
||||||
|
// 格式化类型
|
||||||
|
formatType(type) {
|
||||||
|
const typeMap = {
|
||||||
|
'11': '图生图',
|
||||||
|
'12': '图生图2',
|
||||||
|
'13': '换脸',
|
||||||
|
'1': '快速生图',
|
||||||
|
'21': '快速生视频'
|
||||||
|
}
|
||||||
|
return typeMap[String(type)] || type || '-'
|
||||||
|
},
|
||||||
|
// 格式化状态
|
||||||
|
formatStatus(status) {
|
||||||
|
const statusMap = {
|
||||||
|
0: '进行中',
|
||||||
|
1: '已完成',
|
||||||
|
2: '失败'
|
||||||
|
}
|
||||||
|
return statusMap[status] || status || '-'
|
||||||
|
},
|
||||||
|
// 格式化扩展状态
|
||||||
|
formatExtStatus(extStatus) {
|
||||||
|
const statusMap = {
|
||||||
|
'running': '执行中',
|
||||||
|
'queued': '队列中',
|
||||||
|
'succeeded': '已完成',
|
||||||
|
'failed': '失败',
|
||||||
|
'cancelled': '已取消',
|
||||||
|
'expired': '超时'
|
||||||
|
}
|
||||||
|
return statusMap[extStatus] || '未知'
|
||||||
|
},
|
||||||
|
// 获取状态颜色
|
||||||
|
getStatusColor(status) {
|
||||||
|
const colorMap = {
|
||||||
|
0: 'blue',
|
||||||
|
1: 'green',
|
||||||
|
2: 'red'
|
||||||
|
}
|
||||||
|
return colorMap[status] || 'default'
|
||||||
|
},
|
||||||
|
// 格式化模式
|
||||||
|
formatMode(mode) {
|
||||||
|
const modeMap = {
|
||||||
|
'text-to-video': '文生视频',
|
||||||
|
'image-first-frame': '图生视频·首帧',
|
||||||
|
'image-first-last-frame': '图生视频·首尾帧',
|
||||||
|
'image-reference': '图生视频·参考图'
|
||||||
|
}
|
||||||
|
return modeMap[mode] || mode || '-'
|
||||||
|
},
|
||||||
|
// 格式化videoParams
|
||||||
|
formatVideoParams(params) {
|
||||||
|
if (!params) return ''
|
||||||
|
try {
|
||||||
|
const obj = typeof params === 'string' ? JSON.parse(params) : params
|
||||||
|
return JSON.stringify(obj, null, 2)
|
||||||
|
} catch (e) {
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 判断是否为视频结果
|
||||||
|
isVideoResult(url) {
|
||||||
|
if (!url) return false
|
||||||
|
return /\.(mp4|mov|webm|ogg|m4v|avi|mkv)(\?.*)?$/i.test(url)
|
||||||
|
},
|
||||||
|
// 判断是否为图片结果
|
||||||
|
isImageResult(url) {
|
||||||
|
if (!url) return false
|
||||||
|
return /\.(jpeg|jpg|png|gif|webp|bmp)(\?.*)?$/i.test(url)
|
||||||
|
},
|
||||||
|
// 打开预览
|
||||||
|
openPreview(url, type) {
|
||||||
|
this.previewUrl = url
|
||||||
|
this.previewType = type
|
||||||
|
this.previewVisible = true
|
||||||
|
},
|
||||||
|
// 关闭预览
|
||||||
|
closePreview() {
|
||||||
|
this.previewVisible = false
|
||||||
|
this.previewUrl = ''
|
||||||
|
},
|
||||||
|
// 查看图片全屏
|
||||||
|
viewImageFull(url) {
|
||||||
|
this.$viewerApi({
|
||||||
|
options: {
|
||||||
|
initialViewIndex: 0,
|
||||||
|
toolbar: true
|
||||||
|
},
|
||||||
|
images: [url]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 查看详情
|
||||||
|
viewDetail(item) {
|
||||||
|
this.detailData = item
|
||||||
|
this.detailVisible = true
|
||||||
|
},
|
||||||
|
// 切换收藏状态
|
||||||
|
toggleFavorite(item) {
|
||||||
|
const newIsTop = item.isTop === 'Y' ? 'N' : 'Y'
|
||||||
|
this.$axios({
|
||||||
|
url: '/api/portal/assets/favorite',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
id: item.id,
|
||||||
|
isTop: newIsTop
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
item.isTop = newIsTop
|
||||||
|
this.$message.success(newIsTop === 'Y' ? '收藏成功' : '取消收藏成功')
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '操作失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.$message.error(err?.message || '操作失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.generated-assets-page {
|
||||||
|
padding: 16px;
|
||||||
|
min-height: 100%;
|
||||||
|
background: #0a0b0d;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-section {
|
||||||
|
background: rgba(22, 24, 30, 0.92);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(180px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
grid-template-columns: repeat(3, minmax(180px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
grid-template-columns: repeat(2, minmax(180px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.actions {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-line {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrap {
|
||||||
|
overflow: auto;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 12px 10px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: rgba(255, 255, 255, 0.72);
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列宽定义
|
||||||
|
th:nth-child(1), td:nth-child(1) { width: 60px; }
|
||||||
|
th:nth-child(2), td:nth-child(2) { width: 120px; }
|
||||||
|
th:nth-child(3), td:nth-child(3) { width: 80px; }
|
||||||
|
th:nth-child(4), td:nth-child(4) { width: 80px; }
|
||||||
|
th:nth-child(5), td:nth-child(5) { width: 180px; }
|
||||||
|
th:nth-child(6), td:nth-child(6) { width: 200px; }
|
||||||
|
th:nth-child(7), td:nth-child(7) { width: 140px; }
|
||||||
|
th:nth-child(8), td:nth-child(8) { width: 140px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.td-id {
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.params-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.param-tag {
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.param-empty {
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-cell {
|
||||||
|
.media-preview {
|
||||||
|
.video-thumb {
|
||||||
|
width: 160px;
|
||||||
|
height: 90px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #000;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-thumb {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-id {
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-empty {
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pager {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 详情弹窗 - label:value 形式(深色背景白字) */
|
||||||
|
.detail-content {
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
background: #1a1f2e;
|
||||||
|
color: rgba(255, 255, 255, 0.95);
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
|
.detail-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-group {
|
||||||
|
background: rgba(22, 24, 30, 0.8);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #00cae0;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid rgba(0, 202, 224, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 100px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 13px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
flex: 1;
|
||||||
|
color: rgba(255, 255, 255, 0.95);
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
&.long-text {
|
||||||
|
max-height: 110px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-muted {
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-preview {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-video {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 260px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.03);
|
||||||
|
box-shadow: 0 0 0 3px rgba(0, 202, 224, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
max-height: 160px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-text {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-style: italic;
|
||||||
|
padding: 12px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-block {
|
||||||
|
background: #1a1f2e;
|
||||||
|
color: #a5d6ff;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre;
|
||||||
|
font-family: 'Consolas', monospace;
|
||||||
|
line-height: 1.4;
|
||||||
|
border: 1px solid rgba(165, 214, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览弹窗样式
|
||||||
|
// Tabs 样式 - 深色主题卡片风格(适配 Arco Design Vue + 项目深色UI)
|
||||||
|
.view-tabs {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
|
::v-deep(.arco-tabs) {
|
||||||
|
.arco-tabs-nav {
|
||||||
|
background: rgba(22, 24, 30, 0.98) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
padding: 6px !important;
|
||||||
|
margin-bottom: 0;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-tabs-tab {
|
||||||
|
color: rgba(255, 255, 255, 0.85) !important;
|
||||||
|
margin: 0 4px !important;
|
||||||
|
padding: 10px 28px !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
transition: all 0.2s ease !important;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: 1px solid transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-tabs-tab:hover {
|
||||||
|
color: white !important;
|
||||||
|
background: rgba(0, 102, 204, 0.25) !important;
|
||||||
|
border-color: rgba(0, 102, 204, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-tabs-tab-active {
|
||||||
|
color: white !important;
|
||||||
|
background: #0066cc !important;
|
||||||
|
border-color: #0066cc !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 102, 204, 0.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-tabs-ink-bar {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前 tabs 仅用于视图切换,不需要内容区域,避免出现白色空框
|
||||||
|
.arco-tabs-content {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-field {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.preview-video {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 500px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 500px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 作品详情弹窗深色底样式 - 严格符合 GeneratedAssets.vue 深色主题(适配 Arco Design) */
|
||||||
|
::v-deep(.arco-modal) {
|
||||||
|
.arco-modal-content {
|
||||||
|
background-color: #1a1f2e !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-header {
|
||||||
|
background: transparent !important;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
padding: 20px 24px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-title {
|
||||||
|
color: rgba(255, 255, 255, 0.95) !important;
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-close {
|
||||||
|
color: rgba(255, 255, 255, 0.65) !important;
|
||||||
|
top: 18px;
|
||||||
|
right: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-body {
|
||||||
|
padding: 0 !important;
|
||||||
|
background: transparent;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 detail-content 与页面卡片风格一致(深色背景白字)
|
||||||
|
.detail-content {
|
||||||
|
background: #1a1f2e !important;
|
||||||
|
color: rgba(255, 255, 255, 0.95) !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -68,6 +68,18 @@
|
||||||
@click="regenerateFromTaskRow(row)">
|
@click="regenerateFromTaskRow(row)">
|
||||||
重新生成
|
重新生成
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="vg-link vg-chat-action-btn favorite-btn"
|
||||||
|
:class="{ 'is-favorited': row.isTop === 'Y' }"
|
||||||
|
@click="toggleFavorite(row)">
|
||||||
|
<template v-if="row.isTop === 'Y'">
|
||||||
|
★ 已收藏
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
☆ 收藏
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
|
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -118,6 +130,18 @@
|
||||||
@click="regenerateFromTaskRow(row)">
|
@click="regenerateFromTaskRow(row)">
|
||||||
重新生成
|
重新生成
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="vg-link vg-chat-action-btn favorite-btn"
|
||||||
|
:class="{ 'is-favorited': row.isTop === 'Y' }"
|
||||||
|
@click="toggleFavorite(row)">
|
||||||
|
<template v-if="row.isTop === 'Y'">
|
||||||
|
★ 已收藏
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
☆ 收藏
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
|
<div class="vg-chat-time">{{ formatCreateTime(row.createTime) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1132,8 +1156,9 @@ export default {
|
||||||
// 展示用仍然可以是 https url,但提交 content/reference_url 要与 assetId 关联。
|
// 展示用仍然可以是 https url,但提交 content/reference_url 要与 assetId 关联。
|
||||||
const firstPreview = attachments.find((x) => x?.mediaType === 'image')
|
const firstPreview = attachments.find((x) => x?.mediaType === 'image')
|
||||||
if (firstPreview) {
|
if (firstPreview) {
|
||||||
|
const au = String(firstPreview?.assetUrl || '').trim()
|
||||||
const aid = String(firstPreview?.assetId || '').trim()
|
const aid = String(firstPreview?.assetId || '').trim()
|
||||||
params.referenceUrl = aid ? `asset://${aid}` : firstPreview.url
|
params.referenceUrl = au || (aid ? `asset://${aid}` : firstPreview.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1285,6 +1310,37 @@ export default {
|
||||||
onCancel: () => resolve()
|
onCancel: () => resolve()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 收藏/取消收藏 - 复用生成库接口
|
||||||
|
async toggleFavorite(row) {
|
||||||
|
if (!row || !row.id) {
|
||||||
|
this.$message.error('无效的任务记录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newIsTop = row.isTop === 'Y' ? 'N' : 'Y'
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await this.$axios({
|
||||||
|
url: '/api/portal/assets/favorite',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
id: row.id,
|
||||||
|
isTop: newIsTop
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 更新本地数据
|
||||||
|
row.isTop = newIsTop
|
||||||
|
this.$message.success(newIsTop === 'Y' ? '收藏成功' : '已取消收藏')
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '操作失败')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.$message.error(err?.message || '收藏操作失败')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2055,6 +2111,15 @@ export default {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favorite-btn {
|
||||||
|
color: #ff9800 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.favorite-btn.is-favorited {
|
||||||
|
color: #ffeb3b !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.vg-chat-ai-top {
|
.vg-chat-ai-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,146 @@
|
||||||
|
package com.ruoyi.api;
|
||||||
|
|
||||||
|
import com.ruoyi.ai.domain.AiOrder;
|
||||||
|
import com.ruoyi.ai.service.IAiOrderService;
|
||||||
|
import com.ruoyi.ai.service.IAiUserService;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.domain.entity.AiUser;
|
||||||
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门户-生成资产管理:展示当前用户的AI订单记录,支持收藏功能
|
||||||
|
*/
|
||||||
|
@Api(tags = "门户-生成资产")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/portal/assets")
|
||||||
|
@RequiredArgsConstructor(onConstructor_ = @Autowired)
|
||||||
|
public class PortalAssetsController extends BaseController {
|
||||||
|
|
||||||
|
private final IAiOrderService aiOrderService;
|
||||||
|
|
||||||
|
private final IAiUserService aiUserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前用户的生成资产列表
|
||||||
|
* 支持dept=true参数查询当前用户所在部门的所有作品
|
||||||
|
*/
|
||||||
|
@GetMapping("/list")
|
||||||
|
@ApiOperation("查询生成资产列表(支持收藏筛选、时间范围、dept部门查询)")
|
||||||
|
public TableDataInfo list(
|
||||||
|
@RequestParam(required = false) String is_top,
|
||||||
|
@RequestParam(required = false) String beginTime,
|
||||||
|
@RequestParam(required = false) String endTime,
|
||||||
|
@RequestParam(required = false, defaultValue = "false") Boolean dept) {
|
||||||
|
|
||||||
|
AiOrder query = new AiOrder();
|
||||||
|
Long currentUserId = SecurityUtils.getAiUserId();
|
||||||
|
|
||||||
|
if (Boolean.TRUE.equals(dept)) {
|
||||||
|
// 查询当前用户所在部门的所有作品(包括自己)
|
||||||
|
AiUser currentUser = aiUserService.selectAiUserById(currentUserId);
|
||||||
|
if (currentUser != null && currentUser.getDeptId() != null) {
|
||||||
|
// 通过部门查询所有用户(在XML中通过IN子查询实现)
|
||||||
|
query.getParams().put("deptId", currentUser.getDeptId());
|
||||||
|
System.out.println("=== 查询部门作品,deptId=" + currentUser.getDeptId() + " ===");
|
||||||
|
} else {
|
||||||
|
// 兜底只查个人
|
||||||
|
query.setUserId(currentUserId);
|
||||||
|
System.out.println("=== 部门ID为空,兜底查询个人作品 ===");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 默认只查询当前用户的数据
|
||||||
|
query.setUserId(currentUserId);
|
||||||
|
System.out.println("=== 查询个人作品 userId=" + currentUserId + " ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== PortalAssetsController DEBUG START ===");
|
||||||
|
System.out.println("收到参数: is_top=" + is_top + ", beginTime=" + beginTime + ", endTime=" + endTime + ", dept=" + dept);
|
||||||
|
|
||||||
|
// 收藏状态筛选 - is_top: Y=已收藏, N=未收藏
|
||||||
|
if (is_top != null && !is_top.trim().isEmpty()) {
|
||||||
|
String trimmedIsTop = is_top.trim();
|
||||||
|
query.setIsTop(trimmedIsTop);
|
||||||
|
System.out.println("✓ 设置 isTop = [" + trimmedIsTop + "]");
|
||||||
|
System.out.println(" query.getIsTop() = " + query.getIsTop());
|
||||||
|
} else {
|
||||||
|
System.out.println("✗ is_top 参数为空或未提供,不设置筛选条件");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间范围筛选
|
||||||
|
if (beginTime != null && !beginTime.isEmpty()) {
|
||||||
|
query.getParams().put("beginTime", beginTime);
|
||||||
|
System.out.println("✓ 设置 beginTime = " + beginTime);
|
||||||
|
}
|
||||||
|
if (endTime != null && !endTime.isEmpty()) {
|
||||||
|
query.getParams().put("endTime", endTime);
|
||||||
|
System.out.println("✓ 设置 endTime = " + endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("查询对象状态: isTop=" + query.getIsTop() + ", userId=" + query.getUserId() + ", params=" + query.getParams());
|
||||||
|
System.out.println("=== PortalAssetsController DEBUG END ===");
|
||||||
|
|
||||||
|
startPage();
|
||||||
|
List<AiOrder> list = aiOrderService.selectAiOrderList(query);
|
||||||
|
System.out.println("PortalAssetsController - 查询返回 " + (list != null ? list.size() : 0) + " 条记录");
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏/取消收藏
|
||||||
|
*/
|
||||||
|
@PostMapping("/favorite")
|
||||||
|
@ApiOperation("收藏或取消收藏生成资产")
|
||||||
|
public AjaxResult favorite(@RequestBody FavoriteRequest request) {
|
||||||
|
Long userId = SecurityUtils.getAiUserId();
|
||||||
|
|
||||||
|
// 验证订单归属
|
||||||
|
AiOrder order = aiOrderService.selectAiOrderById(request.getId());
|
||||||
|
if (order == null) {
|
||||||
|
return AjaxResult.error("订单不存在");
|
||||||
|
}
|
||||||
|
if (!userId.equals(order.getUserId())) {
|
||||||
|
return AjaxResult.error("无权操作该订单");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新收藏状态
|
||||||
|
AiOrder update = new AiOrder();
|
||||||
|
update.setId(request.getId());
|
||||||
|
update.setIsTop(request.getIsTop());
|
||||||
|
|
||||||
|
int result = aiOrderService.updateAiOrder(update);
|
||||||
|
return toAjax(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收藏请求体
|
||||||
|
*/
|
||||||
|
public static class FavoriteRequest {
|
||||||
|
private Long id;
|
||||||
|
private String isTop;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIsTop() {
|
||||||
|
return isTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsTop(String isTop) {
|
||||||
|
this.isTop = isTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,7 +94,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="type != null">type = #{type},</if>
|
<if test="type != null">type = #{type},</if>
|
||||||
<if test="jumpUrl != null">jump_url = #{jumpUrl},</if>
|
<if test="jumpUrl != null">jump_url = #{jumpUrl},</if>
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="position != null">status = #{position},</if>
|
<if test="position != null">position = #{position},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="result != null and result != ''"> and ao.result = #{result}</if>
|
<if test="result != null and result != ''"> and ao.result = #{result}</if>
|
||||||
<if test="status != null "> and ao.status = #{status}</if>
|
<if test="status != null "> and ao.status = #{status}</if>
|
||||||
<if test="source != null "> and ao.source = #{source}</if>
|
<if test="source != null "> and ao.source = #{source}</if>
|
||||||
|
<if test="extStatus != null "> and ao.ext_status = #{extStatus}</if>
|
||||||
|
<if test="params.deptId != null">
|
||||||
|
<!-- 查询部门下所有用户的作品 -->
|
||||||
|
AND ao.user_id IN (SELECT id FROM ai_user WHERE dept_id = #{params.deptId} AND del_flag = '0')
|
||||||
|
</if>
|
||||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||||
AND date_format(ao.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
AND date_format(ao.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||||
</if>
|
</if>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue