diff --git a/portal-ui/src/lang/i18n.js b/portal-ui/src/lang/i18n.js
index 6062337..b29b0da 100644
--- a/portal-ui/src/lang/i18n.js
+++ b/portal-ui/src/lang/i18n.js
@@ -1,13 +1,14 @@
import { createI18n } from 'vue-i18n'
import Cookies from 'js-cookie'
import zh_HK from '@/lang/zh_HK/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 hi_IN from '@/lang/hi_IN/index.js'
import ru_RU from '@/lang/ru_RU/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'
@@ -15,13 +16,13 @@ let locale = 'zh_HK'
/** 各语言在界面上的显示名称 */
export const LOCALE_NAMES = {
zh_HK: '繁体中文',
- en_US: 'English',
+/** en_US: 'English',
es_ES: 'Español',
pt_BR: 'Português',
hi_IN: 'हिन्दी',
ru_RU: 'Русский',
ar_SA: 'العربية',
- fr_FR: 'Français'
+ fr_FR: 'Français'**/
}
const i18n = createI18n({
@@ -29,13 +30,13 @@ const i18n = createI18n({
locale,
messages: {
zh_HK,
- en_US,
+/** en_US,
es_ES,
pt_BR,
hi_IN,
ru_RU,
ar_SA,
- fr_FR
+ fr_FR**/
}
})
diff --git a/portal-ui/src/lang/index.js b/portal-ui/src/lang/index.js
index e2712c4..de73253 100644
--- a/portal-ui/src/lang/index.js
+++ b/portal-ui/src/lang/index.js
@@ -1,7 +1,7 @@
-import en_USLocale from './en/index'
+//import en_USLocale from './en/index'
import zh_HKLocale from './zh_HK/index'
export default {
- en_US: en_USLocale,
+ //en_US: en_USLocale,
zh_HK: zh_HKLocale
}
diff --git a/portal-ui/src/lang/zh_HK/route.js b/portal-ui/src/lang/zh_HK/route.js
index d452cbc..7a96fc3 100644
--- a/portal-ui/src/lang/zh_HK/route.js
+++ b/portal-ui/src/lang/zh_HK/route.js
@@ -11,5 +11,6 @@ export default {
help: '幫助中心',
moneyInvite: '有獎邀請',
assetGroupManage: '資源組管理',
- assetManage: '素材管理'
+ assetManage: '素材管理',
+ generatedAssets: '作品库'
}
\ No newline at end of file
diff --git a/portal-ui/src/layout/components/Menu.vue b/portal-ui/src/layout/components/Menu.vue
index 9ff8af2..3727cf5 100644
--- a/portal-ui/src/layout/components/Menu.vue
+++ b/portal-ui/src/layout/components/Menu.vue
@@ -47,7 +47,7 @@ import { constantRoutes } from '@/router/index'
import { generateTitle, generateLang } from '@/utils/i18n'
/** 左侧导航仅显示这些路由(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({
collapsed: Boolean
diff --git a/portal-ui/src/router/index.js b/portal-ui/src/router/index.js
index c3c96ae..15d0920 100644
--- a/portal-ui/src/router/index.js
+++ b/portal-ui/src/router/index.js
@@ -149,6 +149,16 @@ export const constantRoutes = [{
permission: "pass",
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',
name: 'recharge',
diff --git a/portal-ui/src/views/GeneratedAssets.vue b/portal-ui/src/views/GeneratedAssets.vue
new file mode 100644
index 0000000..8dc24b5
--- /dev/null
+++ b/portal-ui/src/views/GeneratedAssets.vue
@@ -0,0 +1,961 @@
+
+
+
+
+
+
+
+
+
+ 总数:{{ pagination.total }}
+
+
+
+
+ | ID |
+ 订单编号 |
+ 类型 |
+ 状态 |
+ 模型参数 |
+ 生成结果 |
+ 创建时间 |
+ 操作 |
+
+
+
+
+ | {{ item.id }} |
+ {{ item.orderNum || '-' }} |
+ {{ formatType(item.type) }} |
+
+
+ {{ formatStatus(item.status) }}
+
+ |
+
+
+ 模型: {{ item.model }}
+ 时长: {{ item.duration }}s
+ 分辨率: {{ item.resolution }}
+ 比例: {{ item.ratio }}
+ 模式: {{ formatMode(item.mode) }}
+ -
+
+ |
+
+
+
+
+
+
+ {{ item.result }}
+
+ -
+ |
+ {{ item.createTime || '-' }} |
+
+
+
+ 已收藏
+
+
+ 收藏
+
+
+
+ 详情
+
+ |
+
+
+ | 暂无数据 |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
diff --git a/portal-ui/src/views/VideoGen.vue b/portal-ui/src/views/VideoGen.vue
index 7d4b582..4ff0f6e 100644
--- a/portal-ui/src/views/VideoGen.vue
+++ b/portal-ui/src/views/VideoGen.vue
@@ -68,6 +68,18 @@
@click="regenerateFromTaskRow(row)">
重新生成
+
{{ formatCreateTime(row.createTime) }}
@@ -118,6 +130,18 @@
@click="regenerateFromTaskRow(row)">
重新生成
+
{{ formatCreateTime(row.createTime) }}
@@ -1132,8 +1156,9 @@ export default {
// 展示用仍然可以是 https url,但提交 content/reference_url 要与 assetId 关联。
const firstPreview = attachments.find((x) => x?.mediaType === 'image')
if (firstPreview) {
+ const au = String(firstPreview?.assetUrl || '').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()
})
})
+ },
+
+ // 收藏/取消收藏 - 复用生成库接口
+ 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;
}
+.favorite-btn {
+ color: #ff9800 !important;
+}
+
+.favorite-btn.is-favorited {
+ color: #ffeb3b !important;
+ font-weight: 600;
+}
+
.vg-chat-ai-top {
display: flex;
align-items: center;
diff --git a/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/PortalAssetsController.java b/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/PortalAssetsController.java
new file mode 100644
index 0000000..0118864
--- /dev/null
+++ b/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/PortalAssetsController.java
@@ -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 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;
+ }
+ }
+}
diff --git a/web-api/ruoyi-system/src/main/resources/mapper/system/AiBannerMapper.xml b/web-api/ruoyi-system/src/main/resources/mapper/system/AiBannerMapper.xml
index 62b694e..472eb2f 100644
--- a/web-api/ruoyi-system/src/main/resources/mapper/system/AiBannerMapper.xml
+++ b/web-api/ruoyi-system/src/main/resources/mapper/system/AiBannerMapper.xml
@@ -94,7 +94,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
type = #{type},
jump_url = #{jumpUrl},
status = #{status},
- status = #{position},
+ position = #{position},
where id = #{id}
diff --git a/web-api/ruoyi-system/src/main/resources/mapper/system/AiOrderMapper.xml b/web-api/ruoyi-system/src/main/resources/mapper/system/AiOrderMapper.xml
index 293cf2e..89122fb 100644
--- a/web-api/ruoyi-system/src/main/resources/mapper/system/AiOrderMapper.xml
+++ b/web-api/ruoyi-system/src/main/resources/mapper/system/AiOrderMapper.xml
@@ -49,6 +49,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and ao.result = #{result}
and ao.status = #{status}
and ao.source = #{source}
+ and ao.ext_status = #{extStatus}
+
+
+ AND ao.user_id IN (SELECT id FROM ai_user WHERE dept_id = #{params.deptId} AND del_flag = '0')
+
AND date_format(ao.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')