From 17337b8d7897de78f82db6b7ab83cd00174c1e70 Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Wed, 5 Nov 2025 23:12:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=B3=BB=E7=BB=9F=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 9 - Yi.Ai.Vue3/.claude/settings.json | 7 - Yi.Ai.Vue3/src/App.vue | 28 + Yi.Ai.Vue3/src/api/announcement/index.ts | 31 + Yi.Ai.Vue3/src/api/announcement/types.ts | 51 ++ Yi.Ai.Vue3/src/api/index.ts | 1 + .../SystemAnnouncementDialog/index.vue | 598 ++++++++++++++++++ Yi.Ai.Vue3/src/data/mockAnnouncementData.ts | 301 +++++++++ .../Header/components/AnnouncementBtn.vue | 107 ++++ .../src/layouts/components/Header/index.vue | 2 + Yi.Ai.Vue3/src/pages/activity/detail.vue | 370 +++++++++++ Yi.Ai.Vue3/src/pages/announcement/detail.vue | 364 +++++++++++ .../src/routers/modules/staticRouter.ts | 20 + Yi.Ai.Vue3/src/stores/index.ts | 1 + Yi.Ai.Vue3/src/stores/modules/announcement.ts | 94 +++ Yi.Ai.Vue3/src/utils/request.ts | 7 +- Yi.Ai.Vue3/types/components.d.ts | 8 + Yi.Ai.Vue3/types/import_meta.d.ts | 1 + Yi.Ai.Vue3/系统公告API接口文档.md | 232 +++++++ Yi.Ai.Vue3/系统公告功能使用说明.md | 337 ++++++++++ Yi.Ai.Vue3/系统公告功能更新日志.md | 177 ++++++ 21 files changed, 2729 insertions(+), 17 deletions(-) delete mode 100644 .claude/settings.local.json delete mode 100644 Yi.Ai.Vue3/.claude/settings.json create mode 100644 Yi.Ai.Vue3/src/api/announcement/index.ts create mode 100644 Yi.Ai.Vue3/src/api/announcement/types.ts create mode 100644 Yi.Ai.Vue3/src/components/SystemAnnouncementDialog/index.vue create mode 100644 Yi.Ai.Vue3/src/data/mockAnnouncementData.ts create mode 100644 Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue create mode 100644 Yi.Ai.Vue3/src/pages/activity/detail.vue create mode 100644 Yi.Ai.Vue3/src/pages/announcement/detail.vue create mode 100644 Yi.Ai.Vue3/src/stores/modules/announcement.ts create mode 100644 Yi.Ai.Vue3/系统公告API接口文档.md create mode 100644 Yi.Ai.Vue3/系统公告功能使用说明.md create mode 100644 Yi.Ai.Vue3/系统公告功能更新日志.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 76069758..00000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "permissions": { - "allow": [ - "WebFetch(domain:www.donet5.com)" - ], - "deny": [], - "ask": [] - } -} diff --git a/Yi.Ai.Vue3/.claude/settings.json b/Yi.Ai.Vue3/.claude/settings.json deleted file mode 100644 index 8f592c09..00000000 --- a/Yi.Ai.Vue3/.claude/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "env": { - "ANTHROPIC_AUTH_TOKEN": "sk-lVcGFMOQfXYtZWp1rD4SaBNDNpM270UX2wDqWh", - "ANTHROPIC_BASE_URL": "https://api.token-ai.cn", - "ANTHROPIC_MODEL": "gpt-4o-mini" - } -} \ No newline at end of file diff --git a/Yi.Ai.Vue3/src/App.vue b/Yi.Ai.Vue3/src/App.vue index 5eca4cff..e1bab0c5 100644 --- a/Yi.Ai.Vue3/src/App.vue +++ b/Yi.Ai.Vue3/src/App.vue @@ -1,9 +1,37 @@ diff --git a/Yi.Ai.Vue3/src/api/announcement/index.ts b/Yi.Ai.Vue3/src/api/announcement/index.ts new file mode 100644 index 00000000..25b7b76c --- /dev/null +++ b/Yi.Ai.Vue3/src/api/announcement/index.ts @@ -0,0 +1,31 @@ +import { get } from '@/utils/request' +import type { + ActivityDetailResponse, + AnnouncementDetailResponse, + SystemAnnouncementResponse, +} from './types' + +/** + * 获取系统公告和活动数据 + */ +export function getSystemAnnouncements() { + return get('/announcement/system').json() +} + +/** + * 获取活动详情 + * @param id 活动ID + */ +export function getActivityDetail(id: string | number) { + return get(`/announcement/activity/${id}`).json() +} + +/** + * 获取公告详情 + * @param id 公告ID + */ +export function getAnnouncementDetail(id: string | number) { + return get(`/announcement/detail/${id}`).json() +} + +export * from './types' diff --git a/Yi.Ai.Vue3/src/api/announcement/types.ts b/Yi.Ai.Vue3/src/api/announcement/types.ts new file mode 100644 index 00000000..576cee6f --- /dev/null +++ b/Yi.Ai.Vue3/src/api/announcement/types.ts @@ -0,0 +1,51 @@ +// 轮播图类型 +export interface CarouselItem { + id: number | string + imageUrl: string + link?: string + title?: string +} + +// 活动类型 +export interface Activity { + id: number | string + title: string + description: string + content: string + coverImage?: string + startTime?: string + endTime?: string + status: 'active' | 'inactive' | 'expired' + createdAt: string + updatedAt?: string +} + +// 公告类型 +export interface Announcement { + id: number | string + title: string + content: string + type: 'latest' | 'history' + isImportant?: boolean + publishTime: string + createdAt: string + updatedAt?: string +} + +// 系统公告响应类型 +export interface SystemAnnouncementResponse { + carousels: CarouselItem[] + activities: Activity[] + announcements: Announcement[] +} + +// 公告详情响应类型 +export interface AnnouncementDetailResponse extends Announcement { + views?: number +} + +// 活动详情响应类型 +export interface ActivityDetailResponse extends Activity { + views?: number + participantCount?: number +} diff --git a/Yi.Ai.Vue3/src/api/index.ts b/Yi.Ai.Vue3/src/api/index.ts index e2242928..224904a0 100644 --- a/Yi.Ai.Vue3/src/api/index.ts +++ b/Yi.Ai.Vue3/src/api/index.ts @@ -1,3 +1,4 @@ +export * from './announcement' export * from './auth'; export * from './chat'; export * from './model'; diff --git a/Yi.Ai.Vue3/src/components/SystemAnnouncementDialog/index.vue b/Yi.Ai.Vue3/src/components/SystemAnnouncementDialog/index.vue new file mode 100644 index 00000000..f73bcdb0 --- /dev/null +++ b/Yi.Ai.Vue3/src/components/SystemAnnouncementDialog/index.vue @@ -0,0 +1,598 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/data/mockAnnouncementData.ts b/Yi.Ai.Vue3/src/data/mockAnnouncementData.ts new file mode 100644 index 00000000..94844252 --- /dev/null +++ b/Yi.Ai.Vue3/src/data/mockAnnouncementData.ts @@ -0,0 +1,301 @@ +import type { Activity, Announcement, CarouselItem, SystemAnnouncementResponse } from '@/api' + +/** + * 模拟数据 - 系统公告 + * 这些数据可以用于开发和测试,实际使用时应从后端API获取 + */ + +// 轮播图数据 +export const mockCarousels: CarouselItem[] = [ + { + id: 1, + imageUrl: 'https://images.unsplash.com/photo-1607827448387-a67db1383b59?w=800&h=400&fit=crop', + title: '新年特惠活动', + link: '/activity/1', + }, + { + id: 2, + imageUrl: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=800&h=400&fit=crop', + title: '新功能上线', + link: '/activity/2', + }, + { + id: 3, + imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&h=400&fit=crop', + title: '限时优惠', + link: '/activity/3', + }, +] + +// 活动数据 +export const mockActivities: Activity[] = [ + { + id: 1, + title: '新年特惠活动', + description: '参与新年特惠活动,即可获得丰厚奖励。活动期间充值即送额外积分,最高可获得50%额外积分奖励!', + content: ` +

活动详情

+

为了感谢各位用户的支持,我们特别推出新年特惠活动!

+ +

活动时间

+

2025年1月1日 00:00 - 2025年1月31日 23:59

+ +

活动规则

+
    +
  • 充值满100元,赠送10%积分
  • +
  • 充值满500元,赠送20%积分
  • +
  • 充值满1000元,赠送30%积分
  • +
  • 充值满5000元,赠送50%积分
  • +
+ +

注意事项

+
    +
  1. 每个用户在活动期间最多可参与5次
  2. +
  3. 赠送的积分将在充值成功后24小时内到账
  4. +
  5. 本活动最终解释权归平台所有
  6. +
+ +
+ 机会难得,不要错过! +
+ `, + coverImage: 'https://images.unsplash.com/photo-1607827448387-a67db1383b59?w=1200&h=600&fit=crop', + startTime: '2025-01-01T00:00:00Z', + endTime: '2025-01-31T23:59:59Z', + status: 'active', + createdAt: '2024-12-25T10:00:00Z', + updatedAt: '2024-12-26T15:30:00Z', + }, + { + id: 2, + title: 'AI功能免费体验', + description: '全新AI功能上线,限时免费体验。包括智能对话、图像生成、代码辅助等多项功能。', + content: ` +

新功能介绍

+

我们很高兴地宣布,全新的AI功能已经正式上线!

+ +

功能亮点

+
    +
  • 智能对话: 更自然的对话体验,支持上下文理解
  • +
  • 图像生成: 通过文字描述生成精美图片
  • +
  • 代码辅助: 智能代码补全和错误检测
  • +
  • 文档分析: 快速提取文档关键信息
  • +
+ +

体验时间

+

2025年1月1日 - 2025年2月28日

+ +

在体验期间,所有功能完全免费使用!

+ `, + coverImage: 'https://images.unsplash.com/photo-1557683316-973673baf926?w=1200&h=600&fit=crop', + startTime: '2025-01-01T00:00:00Z', + endTime: '2025-02-28T23:59:59Z', + status: 'active', + createdAt: '2024-12-28T08:00:00Z', + updatedAt: '2024-12-28T08:00:00Z', + }, + { + id: 3, + title: '推荐好友得奖励', + description: '邀请好友注册使用,双方均可获得积分奖励。推荐越多,奖励越多!', + content: ` +

推荐计划

+

邀请您的朋友一起体验我们的服务,双方都能获得丰厚奖励!

+ +

奖励规则

+
    +
  • 好友通过您的邀请链接注册,您和好友各得50积分
  • +
  • 好友首次充值,您额外获得其充值金额10%的积分
  • +
  • 推荐5位好友,额外奖励200积分
  • +
  • 推荐10位好友,额外奖励500积分
  • +
+ +

如何参与

+
    +
  1. 登录您的账户
  2. +
  3. 进入"推荐好友"页面
  4. +
  5. 复制您的专属邀请链接
  6. +
  7. 分享给您的朋友
  8. +
+ `, + status: 'active', + createdAt: '2024-12-20T10:00:00Z', + }, +] + +// 公告数据 +export const mockAnnouncements: Announcement[] = [ + { + id: 1, + title: '系统维护升级公告', + content: ` +

尊敬的用户:

+ +

为了给您提供更好的服务体验,我们计划于2025年1月10日 22:00 - 2025年1月11日 02:00对系统进行维护升级。

+ +

维护内容

+
    +
  • 优化系统性能,提升响应速度
  • +
  • 修复已知问题
  • +
  • 更新安全补丁
  • +
  • 新增部分功能
  • +
+ +

影响范围

+

维护期间,系统将暂时无法访问,给您带来不便敬请谅解。

+ +

如有紧急问题,请联系客服:service@example.com

+ +

感谢您的理解与支持!

+ `, + type: 'latest', + isImportant: true, + publishTime: '2025-01-05T10:00:00Z', + createdAt: '2025-01-05T09:30:00Z', + }, + { + id: 2, + title: '隐私政策更新通知', + content: ` +

尊敬的用户:

+ +

我们更新了隐私政策,新政策将于2025年1月15日生效。

+ +

主要变更

+
    +
  • 明确了数据收集和使用范围
  • +
  • 增加了用户数据控制权说明
  • +
  • 完善了第三方数据共享规则
  • +
+ +

详细内容请查看完整的隐私政策

+ +

如有疑问,欢迎联系我们。

+ `, + type: 'latest', + isImportant: false, + publishTime: '2025-01-03T14:00:00Z', + createdAt: '2025-01-03T13:30:00Z', + }, + { + id: 3, + title: 'API接口升级通知', + content: ` +

开发者们注意:

+ +

我们的API接口将进行版本升级,新版本为v2.0

+ +

升级时间

+

2025年2月1日

+ +

主要变化

+
    +
  • 优化了响应数据结构
  • +
  • 新增多个端点
  • +
  • 提升了并发处理能力
  • +
+ +

旧版本API将继续维护至2025年6月1日。

+ `, + type: 'history', + isImportant: false, + publishTime: '2024-12-28T10:00:00Z', + createdAt: '2024-12-28T09:00:00Z', + }, + { + id: 4, + title: '新年假期客服安排', + content: ` +

尊敬的用户:

+ +

新年假期期间(1月1日-1月3日),客服工作时间调整为:

+ +
    +
  • 在线客服:10:00 - 18:00
  • +
  • 邮件客服:正常响应,回复时间可能延迟
  • +
+ +

1月4日起恢复正常工作时间。

+ `, + type: 'history', + isImportant: false, + publishTime: '2024-12-25T15:00:00Z', + createdAt: '2024-12-25T14:00:00Z', + }, + { + id: 5, + title: '用户协议更新', + content: ` +

我们更新了用户服务协议,主要涉及:

+ +
    +
  • 账户安全责任条款
  • +
  • 服务使用规范
  • +
  • 争议解决方式
  • +
+ +

继续使用服务即表示您同意新的用户协议。

+ `, + type: 'history', + isImportant: false, + publishTime: '2024-12-20T10:00:00Z', + createdAt: '2024-12-20T09:00:00Z', + }, +] + +// 完整的系统公告响应数据 +export const mockSystemAnnouncementData: SystemAnnouncementResponse = { + carousels: mockCarousels, + activities: mockActivities, + announcements: mockAnnouncements, +} + +/** + * 模拟API延迟 + */ +export function mockDelay(ms: number = 500): Promise { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +/** + * 模拟获取系统公告API + */ +export async function getMockSystemAnnouncements(): Promise<{ data: SystemAnnouncementResponse }> { + await mockDelay() + return { data: mockSystemAnnouncementData } +} + +/** + * 模拟获取活动详情API + */ +export async function getMockActivityDetail(id: string | number) { + await mockDelay() + const activity = mockActivities.find(a => a.id.toString() === id.toString()) + if (!activity) { + throw new Error('活动不存在') + } + return { + data: { + ...activity, + views: Math.floor(Math.random() * 10000) + 1000, + participantCount: Math.floor(Math.random() * 1000) + 100, + }, + } +} + +/** + * 模拟获取公告详情API + */ +export async function getMockAnnouncementDetail(id: string | number) { + await mockDelay() + const announcement = mockAnnouncements.find(a => a.id.toString() === id.toString()) + if (!announcement) { + throw new Error('公告不存在') + } + return { + data: { + ...announcement, + views: Math.floor(Math.random() * 10000) + 1000, + }, + } +} diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue new file mode 100644 index 00000000..5502fddc --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index f47d7200..40f63023 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -4,6 +4,7 @@ import { onKeyStroke } from '@vueuse/core'; import { SIDE_BAR_WIDTH } from '@/config/index'; import { useDesignStore, useUserStore } from '@/stores'; import { useSessionStore } from '@/stores/modules/session'; +import AnnouncementBtn from './components/AnnouncementBtn.vue'; import Avatar from './components/Avatar.vue'; import Collapse from './components/Collapse.vue'; import CreateChat from './components/CreateChat.vue'; @@ -69,6 +70,7 @@ onKeyStroke(event => event.ctrlKey && event.key.toLowerCase() === 'k', handleCtr
+
diff --git a/Yi.Ai.Vue3/src/pages/activity/detail.vue b/Yi.Ai.Vue3/src/pages/activity/detail.vue new file mode 100644 index 00000000..249a8785 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/activity/detail.vue @@ -0,0 +1,370 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/announcement/detail.vue b/Yi.Ai.Vue3/src/pages/announcement/detail.vue new file mode 100644 index 00000000..17035801 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/announcement/detail.vue @@ -0,0 +1,364 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index f91cf8e7..33c16955 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -55,6 +55,26 @@ export const layoutRouter: RouteRecordRaw[] = [ }, }, + { + path: '/activity/:id', + name: 'activityDetail', + component: () => import('@/pages/activity/detail.vue'), + meta: { + title: '活动详情', + isDefaultChat: false, + layout: 'blankPage', + }, + }, + { + path: '/announcement/:id', + name: 'announcementDetail', + component: () => import('@/pages/announcement/detail.vue'), + meta: { + title: '公告详情', + isDefaultChat: false, + layout: 'blankPage', + }, + }, ], }, ]; diff --git a/Yi.Ai.Vue3/src/stores/index.ts b/Yi.Ai.Vue3/src/stores/index.ts index b0347c41..71f27da4 100644 --- a/Yi.Ai.Vue3/src/stores/index.ts +++ b/Yi.Ai.Vue3/src/stores/index.ts @@ -7,6 +7,7 @@ store.use(piniaPluginPersistedstate); export default store; +export * from './modules/announcement' // export * from './modules/chat'; export * from './modules/design'; export * from './modules/user'; diff --git a/Yi.Ai.Vue3/src/stores/modules/announcement.ts b/Yi.Ai.Vue3/src/stores/modules/announcement.ts new file mode 100644 index 00000000..b30583a0 --- /dev/null +++ b/Yi.Ai.Vue3/src/stores/modules/announcement.ts @@ -0,0 +1,94 @@ +import { defineStore } from 'pinia' +import type { Activity, Announcement, CarouselItem } from '@/api' + +export type CloseType = 'today' | 'week' | 'permanent' + +export const useAnnouncementStore = defineStore( + 'announcement', + () => { + // 弹窗显示状态 + const isDialogVisible = ref(false) + + // 公告数据 + const carousels = ref([]) + const activities = ref([]) + const announcements = ref([]) + + // 关闭记录 + const closeType = ref(null) + const closedAt = ref(null) + + // 打开弹窗 + const openDialog = () => { + isDialogVisible.value = true + } + + // 关闭弹窗 + const closeDialog = (type: CloseType) => { + isDialogVisible.value = false + closeType.value = type + closedAt.value = Date.now() + } + + // 检查是否应该显示弹窗 + const shouldShowDialog = computed(() => { + if (!closedAt.value || !closeType.value) + return true + + const now = Date.now() + const elapsed = now - closedAt.value + + if (closeType.value === 'permanent') + return false + + if (closeType.value === 'today') { + // 检查是否已过去一天(24小时) + return elapsed > 24 * 60 * 60 * 1000 + } + + if (closeType.value === 'week') { + // 检查是否已过去一周(7天) + return elapsed > 7 * 24 * 60 * 60 * 1000 + } + + return true + }) + + // 设置公告数据 + const setAnnouncementData = (data: { + carousels: CarouselItem[] + activities: Activity[] + announcements: Announcement[] + }) => { + carousels.value = data.carousels + activities.value = data.activities + announcements.value = data.announcements + } + + // 重置关闭状态(用于测试或管理员重置) + const resetCloseStatus = () => { + closeType.value = null + closedAt.value = null + } + + return { + isDialogVisible, + carousels, + activities, + announcements, + closeType, + closedAt, + shouldShowDialog, + openDialog, + closeDialog, + setAnnouncementData, + resetCloseStatus, + } + }, + { + persist: { + // 只持久化关闭状态相关的数据 + paths: ['closeType', 'closedAt'], + }, + }, +) diff --git a/Yi.Ai.Vue3/src/utils/request.ts b/Yi.Ai.Vue3/src/utils/request.ts index a770e9dd..aecd34bb 100644 --- a/Yi.Ai.Vue3/src/utils/request.ts +++ b/Yi.Ai.Vue3/src/utils/request.ts @@ -77,10 +77,11 @@ function jwtPlugin(): { afterResponse: (response: any) => Promise; beforeStream: (body: any, config: any) => Promise; } { - const userStore = useUserStore(); return { name: 'jwt', beforeRequest: async (config) => { + // 延迟获取 store,确保 Pinia 已经初始化 + const userStore = useUserStore(); config.headers = new Headers(config.headers); if (userStore.refreshToken) { config.headers.set('refresh_token', `${userStore.refreshToken}`); @@ -94,6 +95,8 @@ function jwtPlugin(): { // 响应后处理 afterResponse: async (response: any) => { + // 延迟获取 store,确保 Pinia 已经初始化 + const userStore = useUserStore(); if (response.response.headers.get('access_token')) { userStore.setToken(response.response.headers.get('access_token'), response.response.headers.get('refresh_token')); } @@ -118,6 +121,8 @@ function jwtPlugin(): { }, onError: async (error) => { + // 延迟获取 store,确保 Pinia 已经初始化 + const userStore = useUserStore(); if (error.status === 403) { const data = await (error.response.json()); // 弹窗提示 diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 3beab005..f3d68c9b 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -16,9 +16,12 @@ declare module 'vue' { DeepThinking: typeof import('./../src/components/DeepThinking/index.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] + ElBadge: typeof import('element-plus/es')['ElBadge'] ElButton: typeof import('element-plus/es')['ElButton'] ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElCard: typeof import('element-plus/es')['ElCard'] + ElCarousel: typeof import('element-plus/es')['ElCarousel'] + ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] @@ -39,7 +42,11 @@ declare module 'vue' { ElProgress: typeof import('element-plus/es')['ElProgress'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTabPane: typeof import('element-plus/es')['ElTabPane'] + ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] + ElTimeline: typeof import('element-plus/es')['ElTimeline'] + ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default'] IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default'] @@ -58,6 +65,7 @@ declare module 'vue' { RouterView: typeof import('vue-router')['RouterView'] SupportModelList: typeof import('./../src/components/userPersonalCenter/components/SupportModelList.vue')['default'] SvgIcon: typeof import('./../src/components/SvgIcon/index.vue')['default'] + SystemAnnouncementDialog: typeof import('./../src/components/SystemAnnouncementDialog/index.vue')['default'] UsageStatistics: typeof import('./../src/components/userPersonalCenter/components/UsageStatistics.vue')['default'] UserManagement: typeof import('./../src/components/userPersonalCenter/components/UserManagement.vue')['default'] VerificationCode: typeof import('./../src/components/LoginDialog/components/FormLogin/VerificationCode.vue')['default'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index b3e9d275..d8a60d41 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -6,6 +6,7 @@ interface ImportMetaEnv { readonly VITE_WEB_ENV: string; readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; + readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } diff --git a/Yi.Ai.Vue3/系统公告API接口文档.md b/Yi.Ai.Vue3/系统公告API接口文档.md new file mode 100644 index 00000000..416276b7 --- /dev/null +++ b/Yi.Ai.Vue3/系统公告API接口文档.md @@ -0,0 +1,232 @@ +# 系统公告 API 接口文档 + +## 概述 + +本文档定义了系统公告和活动功能所需的后端API接口。包括公告弹窗数据、活动详情、公告详情三个主要接口。 + +--- + +## 1. 获取系统公告和活动数据 + +### 接口信息 + +- **路径**: `GET /announcement/system` +- **描述**: 获取系统公告弹窗所需的所有数据,包括轮播图、活动列表、公告列表 +- **权限**: 无需登录即可访问 + +### 响应数据结构 + +```typescript +{ + "success": true, + "data": { + "carousels": [ + { + "id": 1, + "imageUrl": "https://example.com/carousel/1.jpg", + "link": "https://example.com/activity/1", + "title": "新年活动" + } + ], + "activities": [ + { + "id": 1, + "title": "新年特惠活动", + "description": "参与活动即可获得丰厚奖励", + "content": "

活动详细内容HTML格式

", + "coverImage": "https://example.com/activity/cover1.jpg", + "startTime": "2025-01-01T00:00:00Z", + "endTime": "2025-01-31T23:59:59Z", + "status": "active", + "createdAt": "2025-01-01T00:00:00Z", + "updatedAt": "2025-01-02T00:00:00Z" + } + ], + "announcements": [ + { + "id": 1, + "title": "系统升级公告", + "content": "

系统将于今晚进行维护升级

", + "type": "latest", + "isImportant": true, + "publishTime": "2025-01-05T10:00:00Z", + "createdAt": "2025-01-05T09:00:00Z", + "updatedAt": "2025-01-05T10:00:00Z" + } + ] + }, + "message": "获取成功" +} +``` + +### 字段说明 + +#### carousels(轮播图) + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | number/string | 是 | 轮播图ID | +| imageUrl | string | 是 | 图片URL | +| link | string | 否 | 点击跳转链接 | +| title | string | 否 | 轮播图标题 | + +#### activities(活动) + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | number/string | 是 | 活动ID | +| title | string | 是 | 活动标题 | +| description | string | 是 | 活动简介 | +| content | string | 是 | 活动详细内容(HTML格式) | +| coverImage | string | 否 | 封面图URL | +| startTime | string | 否 | 开始时间(ISO 8601格式) | +| endTime | string | 否 | 结束时间(ISO 8601格式) | +| status | string | 是 | 状态:active(进行中), inactive(未开始), expired(已结束) | +| createdAt | string | 是 | 创建时间(ISO 8601格式) | +| updatedAt | string | 否 | 更新时间(ISO 8601格式) | + +#### announcements(公告) + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| id | number/string | 是 | 公告ID | +| title | string | 是 | 公告标题 | +| content | string | 是 | 公告内容(HTML格式) | +| type | string | 是 | 类型:latest(最新公告), history(历史公告) | +| isImportant | boolean | 否 | 是否重要公告(显示警告图标) | +| publishTime | string | 是 | 发布时间(ISO 8601格式) | +| createdAt | string | 是 | 创建时间(ISO 8601格式) | +| updatedAt | string | 否 | 更新时间(ISO 8601格式) | + +--- + +## 2. 获取活动详情 + +### 接口信息 + +- **路径**: `GET /announcement/activity/{id}` +- **描述**: 获取单个活动的详细信息 +- **权限**: 无需登录即可访问 + +### 请求参数 + +| 参数 | 位置 | 类型 | 必填 | 说明 | +|------|------|------|------|------| +| id | path | string/number | 是 | 活动ID | + +### 响应数据结构 + +```typescript +{ + "success": true, + "data": { + "id": 1, + "title": "新年特惠活动", + "description": "参与活动即可获得丰厚奖励", + "content": "

活动详细内容HTML格式...

", + "coverImage": "https://example.com/activity/cover1.jpg", + "startTime": "2025-01-01T00:00:00Z", + "endTime": "2025-01-31T23:59:59Z", + "status": "active", + "createdAt": "2025-01-01T00:00:00Z", + "updatedAt": "2025-01-02T00:00:00Z", + "views": 1234, + "participantCount": 567 + }, + "message": "获取成功" +} +``` + +### 额外字段说明 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| views | number | 否 | 浏览次数 | +| participantCount | number | 否 | 参与人数 | + +--- + +## 3. 获取公告详情 + +### 接口信息 + +- **路径**: `GET /announcement/detail/{id}` +- **描述**: 获取单个公告的详细信息 +- **权限**: 无需登录即可访问 + +### 请求参数 + +| 参数 | 位置 | 类型 | 必填 | 说明 | +|------|------|------|------|------| +| id | path | string/number | 是 | 公告ID | + +### 响应数据结构 + +```typescript +{ + "success": true, + "data": { + "id": 1, + "title": "系统升级公告", + "content": "

系统将于今晚进行维护升级,预计时间为晚上10点至次日凌晨2点...

", + "type": "latest", + "isImportant": true, + "publishTime": "2025-01-05T10:00:00Z", + "createdAt": "2025-01-05T09:00:00Z", + "updatedAt": "2025-01-05T10:00:00Z", + "views": 5678 + }, + "message": "获取成功" +} +``` + +### 额外字段说明 + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| views | number | 否 | 浏览次数 | + +--- + +## 错误响应 + +所有接口在出错时应返回统一的错误格式: + +```typescript +{ + "success": false, + "data": null, + "message": "错误描述信息", + "errorCode": "ERROR_CODE" // 可选的错误码 +} +``` + +### 常见错误码 + +| 状态码 | 说明 | +|--------|------| +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | + +--- + +## 注意事项 + +1. **时间格式**: 所有时间字段使用 ISO 8601 格式(如:`2025-01-05T10:00:00Z`) +2. **HTML内容**: content 字段包含HTML格式的内容,前端会进行渲染,请确保内容安全,防止XSS攻击 +3. **图片URL**: 所有图片URL应该是完整的可访问地址 +4. **状态管理**: 活动状态(active/inactive/expired)应该根据当前时间和活动时间自动判断 +5. **公告分类**: 公告的 type 字段(latest/history)可以根据发布时间自动分类,或由管理员手动指定 +6. **浏览统计**: views 字段建议在每次访问详情页时自动递增 + +--- + +## 前端状态管理 + +前端使用 Pinia 管理公告弹窗的显示状态: + +- **今日关闭**: 24小时内不再显示 +- **本周关闭**: 7天内不再显示 +- **关闭公告**: 永久不再显示(除非用户清除浏览器缓存) + +这些状态存储在浏览器的 localStorage 中,不需要后端接口支持。 diff --git a/Yi.Ai.Vue3/系统公告功能使用说明.md b/Yi.Ai.Vue3/系统公告功能使用说明.md new file mode 100644 index 00000000..0d832488 --- /dev/null +++ b/Yi.Ai.Vue3/系统公告功能使用说明.md @@ -0,0 +1,337 @@ +# 系统公告功能使用说明 + +## 功能概述 + +本次更新为项目添加了完整的系统公告弹窗功能,包括活动展示、公告通知、轮播图等。用户可以通过不同的关闭选项来控制弹窗的显示频率。 + +--- + +## 已实现的功能 + +### 1. 系统公告弹窗 + +- **自动弹出**: 用户进入应用时自动检测并显示公告弹窗 +- **手动打开**: 右上角有公告按钮(铃铛图标),随时可以点击打开公告弹窗 +- **未读提示**: 公告按钮上显示红色徽章,标识最新公告数量 +- **Tab切换**: 支持"活动"和"公告"两个Tab页面 +- **关闭选项**: 提供三种关闭方式 + - 今日关闭:24小时内不再自动弹出,但仍可通过按钮手动打开 + - 本周关闭:7天内不再自动弹出,但仍可通过按钮手动打开 + - 关闭公告:永久不再自动弹出(除非清除浏览器缓存),但仍可通过按钮手动打开 + +### 2. 活动页面 + +- **轮播图**: 顶部展示活动轮播图,支持自动播放 +- **活动列表**: 显示所有活动,包含标题、简介、状态标签 +- **状态标识**: + - 进行中:绿色标签 + - 已结束:灰色标签 +- **查看详情**: 点击可跳转到活动详情页面 + +### 3. 公告页面 + +- **最新公告**: 顶部显示最新发布的公告,带蓝色边框高亮 +- **重要标识**: 重要公告显示警告图标 +- **历史公告**: 使用时间线形式展示历史公告 +- **查看详情**: 点击可跳转到公告详情页面 + +### 4. 详情页面 + +#### 活动详情页面 +- 活动标题和状态 +- 浏览次数和参与人数 +- 活动封面图 +- 活动简介 +- 活动时间信息(开始、结束、发布、更新时间) +- 完整的活动内容(支持HTML格式) + +#### 公告详情页面 +- 公告标题和类型 +- 重要公告标识(带动画效果) +- 浏览次数 +- 发布时间 +- 完整的公告内容(支持HTML格式) +- 创建和更新时间 + +--- + +## 文件结构 + +``` +Yi.Ai.Vue3/ +├── src/ +│ ├── api/ +│ │ └── announcement/ +│ │ ├── index.ts # API接口定义 +│ │ └── types.ts # TypeScript类型定义 +│ ├── components/ +│ │ └── SystemAnnouncementDialog/ +│ │ └── index.vue # 系统公告弹窗组件 +│ ├── layouts/ +│ │ └── components/ +│ │ └── Header/ +│ │ ├── components/ +│ │ │ └── AnnouncementBtn.vue # 公告按钮组件(新增) +│ │ └── index.vue # Header组件(已更新) +│ ├── pages/ +│ │ ├── activity/ +│ │ │ └── detail.vue # 活动详情页面 +│ │ └── announcement/ +│ │ └── detail.vue # 公告详情页面 +│ ├── stores/ +│ │ └── modules/ +│ │ └── announcement.ts # 公告状态管理 +│ ├── data/ +│ │ └── mockAnnouncementData.ts # 模拟数据 +│ ├── routers/ +│ │ └── modules/ +│ │ └── staticRouter.ts # 路由配置(已更新) +│ ├── utils/ +│ │ └── request.ts # HTTP请求工具(已修复Pinia初始化问题) +│ └── App.vue # 主应用文件(已更新) +├── 系统公告API接口文档.md # API接口文档 +└── 系统公告功能使用说明.md # 本文档 +``` + +--- + +## 如何使用 + +### 开发环境测试(使用模拟数据) + +1. **导入模拟数据** + +在 `src/App.vue` 中临时替换API调用: + +```typescript +// 导入模拟数据函数 +import { getMockSystemAnnouncements } from '@/data/mockAnnouncementData' + +// 替换 getSystemAnnouncements() 为 getMockSystemAnnouncements() +onMounted(async () => { + if (announcementStore.shouldShowDialog) { + try { + // 使用模拟数据 + const res = await getMockSystemAnnouncements() + if (res.data) { + announcementStore.setAnnouncementData(res.data) + announcementStore.openDialog() + } + } + catch (error) { + console.error('获取系统公告失败:', error) + } + } +}) +``` + +2. **同样的方式更新详情页面** + +在 `src/pages/activity/detail.vue` 和 `src/pages/announcement/detail.vue` 中也可以使用模拟数据函数进行测试。 + +3. **启动开发服务器** + +```bash +npm run dev +``` + +4. **测试功能** + +- 打开浏览器访问应用 +- 应该会自动弹出系统公告弹窗 +- 测试不同的关闭选项 +- 点击"查看详情"按钮测试跳转 + +### 生产环境部署(使用真实API) + +1. **后端实现API接口** + +参考 `系统公告API接口文档.md` 实现以下三个接口: +- `GET /announcement/system` - 获取系统公告数据 +- `GET /announcement/activity/{id}` - 获取活动详情 +- `GET /announcement/detail/{id}` - 获取公告详情 + +2. **确认API调用** + +确保 `src/App.vue` 和详情页面使用的是真实的API函数(默认配置): +- `getSystemAnnouncements()` +- `getActivityDetail(id)` +- `getAnnouncementDetail(id)` + +3. **构建部署** + +```bash +npm run build +``` + +--- + +## 自定义配置 + +### 修改关闭时间 + +在 `src/stores/modules/announcement.ts` 中修改时间计算逻辑: + +```typescript +const shouldShowDialog = computed(() => { + // ... 现有代码 + + if (closeType.value === 'today') { + // 修改为其他时间,如12小时 + return elapsed > 12 * 60 * 60 * 1000 + } + + if (closeType.value === 'week') { + // 修改为其他时间,如3天 + return elapsed > 3 * 24 * 60 * 60 * 1000 + } + + // ... 现有代码 +}) +``` + +### 修改弹窗样式 + +在 `src/components/SystemAnnouncementDialog/index.vue` 中修改样式: + +- 修改弹窗宽度:`width="700px"` 改为其他值 +- 修改轮播图高度:`height="250px"` 改为其他值 +- 修改CSS样式:在 `