From 411a9058cac1f79a71dbc0b7eaa7cdacabfdc3f8 Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Sun, 28 Dec 2025 22:42:17 +0800 Subject: [PATCH 01/43] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=9E=B6=E6=9E=84=E9=87=8D=E6=9E=84=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Ai.Vue3/.claude/settings.local.json | 3 +- .../components/ActivationCode.vue | 54 +- .../components/UsageStatistics.vue | 1 + .../components/UserManagement.vue | 39 +- Yi.Ai.Vue3/src/config/design.ts | 14 +- Yi.Ai.Vue3/src/hooks/useResponsive.ts | 72 ++ .../src/hooks/useWindowWidthObserver.ts | 5 +- .../src/layouts/LayoutDefault/index.vue | 35 + Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue | 288 +++++- .../src/layouts/LayoutVertical/index.vue | 56 +- .../src/layouts/components/Aside/index.vue | 392 -------- .../layouts/components/ChatAside/index.vue | 918 ++++++++++++++++++ .../components/Header/components/Avatar.vue | 72 +- .../Header/components/StartChatBtn.vue | 84 ++ .../components/Header/components/ThemeBtn.vue | 95 ++ .../src/layouts/components/Header/index.vue | 583 +++++++++-- .../src/layouts/components0/Aside/index.vue | 725 ++++++++++++++ .../components0/DesignConfig/index.vue | 8 + .../Header/components/AiTutorialBtn.vue | 88 ++ .../Header/components/AnnouncementBtn.vue | 112 +++ .../components0/Header/components/Avatar.vue | 499 ++++++++++ .../components0/Header/components/BuyBtn.vue | 68 ++ .../Header/components/Collapse.vue | 39 + .../Header/components/ConsoleBtn.vue | 91 ++ .../Header/components/CreateChat.vue | 45 + .../Header/components/LoginBtn.vue | 29 + .../Header/components/ModelLibraryBtn.vue | 88 ++ .../Header/components/StartChatBtn.vue | 84 ++ .../Header/components/ThemeBtn.vue | 95 ++ .../Header/components/TitleEditing.vue | 87 ++ .../Header/components/TutorialBtn.vue | 75 ++ .../src/layouts/components0/Header/index.vue | 122 +++ .../src/layouts/components0/Logo/index.vue | 8 + .../src/layouts/components0/Main/index.vue | 89 ++ Yi.Ai.Vue3/src/layouts/index.vue | 28 +- .../chat/components/ConversationList.vue | 226 +++++ .../src/pages/chat/conversation/index.vue | 44 + Yi.Ai.Vue3/src/pages/chat/image/index.vue | 26 + Yi.Ai.Vue3/src/pages/chat/index.vue | 305 +++++- .../pages/chat/layouts/chatDefaul/index.vue | 330 ++++--- .../pages/chat/layouts/chatWithId/index.vue | 4 +- Yi.Ai.Vue3/src/pages/chat/video/index.vue | 26 + Yi.Ai.Vue3/src/pages/console/index.vue | 313 ++++++ Yi.Ai.Vue3/src/pages/modelLibrary/index.vue | 51 +- Yi.Ai.Vue3/src/routers/index.ts | 34 +- .../src/routers/modules/staticRouter.ts | 183 +++- Yi.Ai.Vue3/src/stores/modules/design.ts | 56 +- Yi.Ai.Vue3/src/stores/modules/session.ts | 6 +- Yi.Ai.Vue3/src/stores/modules/user.ts | 2 +- Yi.Ai.Vue3/src/styles/index.scss | 2 +- Yi.Ai.Vue3/src/styles/var.scss | 238 ++++- Yi.Ai.Vue3/src/styles/yixin-markdown.scss | 3 +- Yi.Ai.Vue3/types/components.d.ts | 3 +- 53 files changed, 6098 insertions(+), 845 deletions(-) create mode 100644 Yi.Ai.Vue3/src/hooks/useResponsive.ts create mode 100644 Yi.Ai.Vue3/src/layouts/LayoutDefault/index.vue delete mode 100644 Yi.Ai.Vue3/src/layouts/components/Aside/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Header/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue create mode 100644 Yi.Ai.Vue3/src/layouts/components0/Main/index.vue create mode 100644 Yi.Ai.Vue3/src/pages/chat/components/ConversationList.vue create mode 100644 Yi.Ai.Vue3/src/pages/chat/conversation/index.vue create mode 100644 Yi.Ai.Vue3/src/pages/chat/image/index.vue create mode 100644 Yi.Ai.Vue3/src/pages/chat/video/index.vue create mode 100644 Yi.Ai.Vue3/src/pages/console/index.vue diff --git a/Yi.Ai.Vue3/.claude/settings.local.json b/Yi.Ai.Vue3/.claude/settings.local.json index 131db92e..13e6d318 100644 --- a/Yi.Ai.Vue3/.claude/settings.local.json +++ b/Yi.Ai.Vue3/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(npx vue-tsc --noEmit)", - "Bash(timeout 60 npx vue-tsc:*)" + "Bash(timeout 60 npx vue-tsc:*)", + "Bash(npm run dev:*)" ], "deny": [], "ask": [] diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/ActivationCode.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/ActivationCode.vue index 88541897..48c2766d 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/ActivationCode.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/ActivationCode.vue @@ -31,33 +31,33 @@ class Particle { this.x = x; this.y = y; this.hue = hue; - + // Explosive physics const angle = Math.random() * Math.PI * 2; - const speed = Math.random() * 15 + 2; + const speed = Math.random() * 15 + 2; this.vx = Math.cos(angle) * speed; this.vy = Math.sin(angle) * speed; - + this.alpha = 1; this.decay = Math.random() * 0.015 + 0.005; this.gravity = 0.05; - this.friction = 0.96; - + this.friction = 0.96; + this.size = Math.random() * 3 + 1; this.brightness = 50; // Standard brightness for white bg visibility (0-100% HSL L value) - this.flicker = Math.random() > 0.5; + this.flicker = Math.random() > 0.5; } update() { this.vx *= this.friction; this.vy *= this.friction; this.vy += this.gravity; - + this.x += this.vx; this.y += this.vy; - + this.alpha -= this.decay; - this.hue += 0.5; + this.hue += 0.5; } draw(ctx: CanvasRenderingContext2D) { @@ -65,9 +65,9 @@ class Particle { // On white background: // We want high saturation (100%) and medium lightness (50%) to make colors pop against white. // If lightness is too high (like 80-100), it fades into white. - const lightness = this.flicker ? Math.random() * 20 + 40 : this.brightness; + const lightness = this.flicker ? Math.random() * 20 + 40 : this.brightness; ctx.fillStyle = `hsla(${this.hue}, 100%, ${lightness}%, ${this.alpha})`; - + ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); @@ -93,7 +93,7 @@ class Shockwave { } update() { - this.radius += 15; + this.radius += 15; this.alpha -= 0.05; this.lineWidth -= 0.2; } @@ -132,10 +132,10 @@ function animate() { if (!ctx) return; // Clear with transparent fade for trails on white - // 'destination-out' erases content. + // 'destination-out' erases content. // To leave a trail on a white background (canvas is transparent over white gradient): // We need to gently erase what's there. - + ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = 'rgba(255, 255, 255, 0.1)'; // Alpha controls trail length ctx.fillRect(0, 0, canvas.width, canvas.height); @@ -177,14 +177,14 @@ function triggerCelebration() { canvas.width = parent.clientWidth; canvas.height = parent.clientHeight; } - + const cx = canvas.width / 2; const cy = canvas.height / 2; // 1. Initial Mega Explosion triggerShake(); createExplosion(cx, cy, Math.random() * 360); - + // Start loop animate(); @@ -194,15 +194,15 @@ function triggerCelebration() { count++; const rx = cx + (Math.random() - 0.5) * canvas.width * 0.8; const ry = cy + (Math.random() - 0.5) * canvas.height * 0.8; - + createExplosion(rx, ry, Math.random() * 360); - - if (count % 3 === 0) triggerShake(); + + if (count % 3 === 0) triggerShake(); if (count > 25) { clearInterval(timer); } - }, 120); + }, 120); } async function handleRedeem() { @@ -221,7 +221,7 @@ async function handleRedeem() { duration: 3000, showClose: true, }); - activationCode.value = ''; + activationCode.value = ''; } catch (error: any) { // console.error(error); } finally { @@ -238,13 +238,13 @@ onUnmounted(() => {
- +
🎁
- +

激活码兑换

开启您的专属惊喜权益

@@ -258,9 +258,9 @@ onUnmounted(() => { clearable @keyup.enter="handleRedeem" /> - - @@ -321,7 +321,7 @@ onUnmounted(() => { .content-wrapper { position: relative; - z-index: 11; + z-index: 11; width: 100%; max-width: 480px; padding: 40px; diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/UsageStatistics.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/UsageStatistics.vue index 8be7425f..e0baea70 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/UsageStatistics.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/UsageStatistics.vue @@ -549,6 +549,7 @@ onBeforeUnmount(() => { @@ -180,7 +187,9 @@ function bindWechat() { 昵称
-
{{ userNick }}
+
+ {{ userNick }} +
@@ -215,7 +224,9 @@ function bindWechat() {
- + + + 微信绑定
diff --git a/Yi.Ai.Vue3/src/config/design.ts b/Yi.Ai.Vue3/src/config/design.ts index 44995efd..6d597f54 100644 --- a/Yi.Ai.Vue3/src/config/design.ts +++ b/Yi.Ai.Vue3/src/config/design.ts @@ -1,4 +1,4 @@ -export type LayoutType = | 'vertical' | 'blankPage'; +export type LayoutType = 'default' | 'vertical' | 'blankPage' | 'blankPage'; // 仿豆包折叠逻辑 export type CollapseType @@ -25,7 +25,7 @@ export interface DesignConfigState { // 是否折叠菜单 isCollapse: boolean; // 安全区是否被悬停 - isSafeAreaHover: boolean; + isCollapseConversationList: boolean; // 跟踪是否首次激活悬停 hasActivatedHover: boolean; } @@ -65,15 +65,13 @@ const design: DesignConfigState = { // 需要自定义路由动画可以把 Main 组件样式代码注释放开,从新对话切换到带id的路由时,会执行这个动画样式 pageAnimateType: 'zoom-fade', // 布局模式 (纵向:vertical | ... | 自己定义) - layout: 'vertical', + layout: 'default', // 折叠类型 collapseType: 'followSystem', - // 是否折叠菜单 + // 是否折叠对话记录菜单 isCollapse: false, - // 安全区是否被悬停 - isSafeAreaHover: false, - // 跟踪是否首次激活悬停 - hasActivatedHover: false, + // 是否折叠对话记录菜单 + isCollapseConversationList: false, }; export default design; diff --git a/Yi.Ai.Vue3/src/hooks/useResponsive.ts b/Yi.Ai.Vue3/src/hooks/useResponsive.ts new file mode 100644 index 00000000..2c57ef32 --- /dev/null +++ b/Yi.Ai.Vue3/src/hooks/useResponsive.ts @@ -0,0 +1,72 @@ +import { useBreakpoints, useWindowSize } from '@vueuse/core'; +import { computed } from 'vue'; + +// 断点定义 +export const breakpoints = { + xs: 0, // 手机竖屏 < 640px + sm: 640, // 手机横屏 ≥ 640px + md: 768, // 平板 ≥ 768px + lg: 1024, // 小桌面 ≥ 1024px + xl: 1280, // 桌面 ≥ 1280px + xxl: 1536, // 大桌面 ≥ 1536px +}; + +export function useResponsive() { + const bp = useBreakpoints(breakpoints); + + // 设备类型判断 + const isMobile = bp.smaller('md'); // < 768px + const isTablet = bp.between('md', 'lg'); // 768px - 1024px + const isDesktop = bp.greaterOrEqual('lg'); // ≥ 1024px + + // 精确断点 + const isXs = bp.smaller('sm'); // < 640px + const isSm = bp.between('sm', 'md'); // 640px - 768px + const isMd = bp.between('md', 'lg'); // 768px - 1024px + const isLg = bp.between('lg', 'xl'); // 1024px - 1280px + const isXl = bp.between('xl', 'xxl'); // 1280px - 1536px + const isXxl = bp.greater('xxl'); // > 1536px + + // 监听窗口变化 + const { width, height } = useWindowSize(); + + // 方向检测 + const isPortrait = computed(() => height.value > width.value); + const isLandscape = computed(() => width.value > height.value); + + // 当前断点名称 + const currentBreakpoint = computed(() => { + if (isXs.value) return 'xs'; + if (isSm.value) return 'sm'; + if (isMd.value) return 'md'; + if (isLg.value) return 'lg'; + if (isXl.value) return 'xl'; + return 'xxl'; + }); + + return { + // 设备类型 + isMobile, + isTablet, + isDesktop, + + // 精确断点 + isXs, + isSm, + isMd, + isLg, + isXl, + isXxl, + + // 尺寸 + width, + height, + + // 方向 + isPortrait, + isLandscape, + + // 当前断点名称 + currentBreakpoint, + }; +} diff --git a/Yi.Ai.Vue3/src/hooks/useWindowWidthObserver.ts b/Yi.Ai.Vue3/src/hooks/useWindowWidthObserver.ts index a5c4bc52..5adf34b3 100644 --- a/Yi.Ai.Vue3/src/hooks/useWindowWidthObserver.ts +++ b/Yi.Ai.Vue3/src/hooks/useWindowWidthObserver.ts @@ -19,8 +19,9 @@ export function useWindowWidthObserver( const isAboveThreshold = ref(false); const thresholdRef = ref(threshold); let prevIsAbove = false; // 记录上一次状态,避免重复触发 - + // 待定 待梳理 1227 // 默认逻辑:修改全局折叠状态 + // eslint-disable-next-line unused-imports/no-unused-vars const updateCollapseState = (isAbove: boolean) => { // 判断当前的折叠状态 switch (designStore.collapseType) { @@ -70,7 +71,7 @@ export function useWindowWidthObserver( onChange(newIsAbove); } else { - updateCollapseState(newIsAbove); + // updateCollapseState(newIsAbove); } } }; diff --git a/Yi.Ai.Vue3/src/layouts/LayoutDefault/index.vue b/Yi.Ai.Vue3/src/layouts/LayoutDefault/index.vue new file mode 100644 index 00000000..cb358436 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/LayoutDefault/index.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue b/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue index 29fc25ab..60d7f8dd 100644 --- a/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue +++ b/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue @@ -1,8 +1,288 @@ - - + + - + diff --git a/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue b/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue index 32264f60..5d752ac4 100644 --- a/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue +++ b/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue @@ -2,8 +2,6 @@ - - - - diff --git a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue new file mode 100644 index 00000000..c0e8710d --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue @@ -0,0 +1,918 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue index 26675ad5..0b98e38e 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -30,40 +30,40 @@ const popoverRef = ref(); // 弹出面板内容 const popoverList = ref([ - { - key: '5', - title: '控制台', - icon: 'settings-4-fill', - }, - { - key: '3', - divider: true, - }, - { - key: '7', - title: '公告', - icon: 'notification-fill', - }, - { - key: '8', - title: '模型库', - icon: 'apps-fill', - }, - { - key: '9', - title: '文档', - icon: 'book-fill', - }, - - { - key: '6', - title: '新手引导', - icon: 'dashboard-fill', - }, - { - key: '3', - divider: true, - }, + // { + // key: '5', + // title: '控制台', + // icon: 'settings-4-fill', + // }, + // { + // key: '3', + // divider: true, + // }, + // { + // key: '7', + // title: '公告', + // icon: 'notification-fill', + // }, + // { + // key: '8', + // title: '模型库', + // icon: 'apps-fill', + // }, + // { + // key: '9', + // title: '文档', + // icon: 'book-fill', + // }, + // + // { + // key: '6', + // title: '新手引导', + // icon: 'dashboard-fill', + // }, + // { + // key: '3', + // divider: true, + // }, { key: '4', title: '退出登录', @@ -130,7 +130,9 @@ function handleClick(item: any) { ElMessage.warning('暂未开放'); break; case '5': - openDialog(); + // 打开控制台 + popoverRef.value?.hide?.(); + router.push('/console'); break; case '6': handleStartTutorial(); diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue new file mode 100644 index 00000000..970e1509 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue new file mode 100644 index 00000000..975fd521 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index aaacdec3..1db0ae27 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -1,94 +1,50 @@ - + -
- - -
-
- - -
- -
-
+
+ <!– 左侧logo和品牌区域 –> +
+
+ + 意心AI
+
- -
- - - - - - - -
+ <!– 右侧功能按钮区域 –> +
+ + + + + + + + +
@@ -98,20 +54,483 @@ function handleOpenConsole() { .header-container { display: flex; flex-shrink: 0; - flex-direction: column; width: 100%; - height: fit-content; + height: var(--header-container-default-height, 60px); + .header-box { + display: flex; + align-items: center; + justify-content: space-between; width: 100%; - width: calc( - 100% - var(--sidebar-left-container-default-width, 0px) - var( - --sidebar-right-container-default-width, - 0px - ) - ); - height: var(--header-container-default-heigth); - margin: 0 var(--sidebar-right-container-default-width, 0) 0 - var(--sidebar-left-container-default-width, 0); + height: 100%; + padding: 0 16px; + background: var(--header-bg-color, #ffffff); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); + } + + // 左侧品牌区域 + .left-section { + display: flex; + align-items: center; + min-width: fit-content; + flex-shrink: 0; + + .brand-container { + display: flex; + align-items: center; + gap: 8px; + + .logo-img { + width: 36px; // 优化为更合适的大小 + height: 36px; + flex-shrink: 0; + transition: transform 0.2s ease; + + &:hover { + transform: scale(1.05); + } + } + + .brand-text { + font-size: 22px; // 减小字体大小 + font-weight: bold; + color: var(--brand-color, #000000); + white-space: nowrap; + letter-spacing: -0.5px; + transition: color 0.2s ease; + + &:hover { + //color: var(--brand-hover-color, #40a9ff); + } + } + } + } + + // 右侧功能区域 + .right-section { + display: flex; + align-items: center; + gap: 12px; // 优化按钮间距 + height: 100%; + flex-shrink: 0; + + // 统一按钮样式 + :deep(.menu-button) { + height: 36px; + display: flex; + align-items: center; + justify-content: center; + } + } +} + +--> + + + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue new file mode 100644 index 00000000..be93e273 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue @@ -0,0 +1,725 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue b/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue new file mode 100644 index 00000000..f3409aa6 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue @@ -0,0 +1,8 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue new file mode 100644 index 00000000..bd7f2647 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue new file mode 100644 index 00000000..a6c6adda --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue new file mode 100644 index 00000000..7f1ed3c1 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue @@ -0,0 +1,499 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue new file mode 100644 index 00000000..9e5b512a --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue new file mode 100644 index 00000000..9868df5d --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue new file mode 100644 index 00000000..3091d22e --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue new file mode 100644 index 00000000..5ec82222 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue @@ -0,0 +1,45 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue new file mode 100644 index 00000000..64b43c4c --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue @@ -0,0 +1,29 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue new file mode 100644 index 00000000..73347ac9 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue new file mode 100644 index 00000000..970e1509 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue new file mode 100644 index 00000000..975fd521 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue new file mode 100644 index 00000000..767e2d53 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue @@ -0,0 +1,87 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue new file mode 100644 index 00000000..524ae1cd --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue new file mode 100644 index 00000000..d8dc7806 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue new file mode 100644 index 00000000..a84746fd --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue @@ -0,0 +1,8 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue new file mode 100644 index 00000000..59622a62 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue @@ -0,0 +1,89 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/index.vue b/Yi.Ai.Vue3/src/layouts/index.vue index ed682c5e..50a80f49 100644 --- a/Yi.Ai.Vue3/src/layouts/index.vue +++ b/Yi.Ai.Vue3/src/layouts/index.vue @@ -1,21 +1,41 @@ + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue b/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue new file mode 100644 index 00000000..815896e9 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/image/index.vue b/Yi.Ai.Vue3/src/pages/chat/image/index.vue new file mode 100644 index 00000000..b319c725 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/image/index.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/index.vue b/Yi.Ai.Vue3/src/pages/chat/index.vue index 933c9a65..82619c11 100644 --- a/Yi.Ai.Vue3/src/pages/chat/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/index.vue @@ -1,31 +1,304 @@ - diff --git a/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue b/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue index 44180051..2ec17b1c 100644 --- a/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue @@ -7,162 +7,259 @@ import { ElMessage } from 'element-plus'; import { nextTick, ref, watch } from 'vue'; import ModelSelect from '@/components/ModelSelect/index.vue'; import WelecomeText from '@/components/WelecomeText/index.vue'; -import { useGuideTourStore, useUserStore } from '@/stores'; +import { useUserStore } from '@/stores'; import { useFilesStore } from '@/stores/modules/files'; - import { useSessionStore } from '@/stores/modules/session'; +// Store 实例 const userStore = useUserStore(); const sessionStore = useSessionStore(); const filesStore = useFilesStore(); -const guideTourStore = useGuideTourStore(); -const senderValue = ref(''); -const senderRef = ref(); +// 响应式数据 +const senderValue = ref(''); // 输入框内容 +const senderRef = ref(); // Sender 组件引用 const isSending = ref(false); // 发送状态标志 -// 防抖发送函数 -const debouncedSend = useDebounceFn(async () => { - if (!senderValue.value.trim()) { - ElMessage.warning('消息内容不能为空'); - return; - } +/** + * 防抖发送消息函数 + */ +const debouncedSend = useDebounceFn( + async () => { + // 1. 验证输入 + if (!senderValue.value.trim()) { + ElMessage.warning('消息内容不能为空'); + return; + } - if (isSending.value) { - ElMessage.warning('请等待上一条消息发送完成'); - return; - } + // 2. 检查是否正在发送 + if (isSending.value) { + ElMessage.warning('请等待上一条消息发送完成'); + return; + } - const content = senderValue.value; - isSending.value = true; + // 3. 准备发送数据 + const content = senderValue.value.trim(); + isSending.value = true; - try { - localStorage.setItem('chatContent', content); - await sessionStore.createSessionList({ - userId: userStore.userInfo?.userId as number, - sessionContent: content, - sessionTitle: content.slice(0, 10), - remark: content.slice(0, 10), - }); - senderValue.value = ''; // 清空输入框 - } - catch (error: any) { - console.error('发送消息失败:', error); - ElMessage.error(error); - } - finally { - isSending.value = false; - } -}, 800, { leading: true, trailing: false }); // 800ms防抖 + try { + // 4. 保存到本地存储(可选,用于页面刷新后恢复) + localStorage.setItem('chatContent', content); -// 处理发送事件 + // 5. 创建会话 + await sessionStore.createSessionList({ + userId: userStore.userInfo?.userId as number, + sessionContent: content, + sessionTitle: content.slice(0, 10), + remark: content.slice(0, 10), + }); + + // 6. 清空输入框 + senderValue.value = ''; + } + catch (error: any) { + console.error('发送消息失败:', error); + ElMessage.error(error.message || '发送消息失败'); + } + finally { + // 7. 重置发送状态 + isSending.value = false; + } + }, + 800, // 防抖延迟 + { leading: true, trailing: false }, // 立即执行第一次,忽略后续快速点击 +); + +/** + * 触发发送消息 + */ function handleSend() { debouncedSend(); } +/** + * 删除文件卡片 + * @param _item 文件项 + * @param index 文件索引 + */ function handleDeleteCard(_item: FilesCardProps, index: number) { filesStore.deleteFileByIndex(index); } +/** + * 监听文件列表变化,自动展开/收起 Sender 头部 + */ watch( () => filesStore.filesList.length, (val) => { - if (val > 0) { - nextTick(() => { + nextTick(() => { + if (val > 0) { senderRef.value?.openHeader(); - }); - } - else { - nextTick(() => { + } + else { senderRef.value?.closeHeader(); - }); - } + } + }); }, ); diff --git a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue index 0ecb2ee1..fd6d0046 100644 --- a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue @@ -562,14 +562,16 @@ function handleImagePreview(url: string) { diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue new file mode 100644 index 00000000..44aece8f --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue index df8dc070..264aeb12 100644 --- a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue +++ b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue @@ -266,6 +266,7 @@ onMounted(() => {
{ diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 44aece8f..15ae21dd 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -19,6 +19,7 @@ const navItems = [ { name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' }, { name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' }, { name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' }, + { name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index b94431ec..496bb214 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -207,6 +207,14 @@ export const layoutRouter: RouteRecordRaw[] = [ title: '激活码兑换', }, }, + { + path: 'channel', + name: 'consoleChannel', + component: () => import('@/pages/console/channel/index.vue'), + meta: { + title: '渠道商管理', + }, + }, ], }, ], diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 9ef439fb..16d28441 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,6 +25,8 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index c98d612e..8f2a798b 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,7 +7,6 @@ interface ImportMetaEnv { readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; readonly VITE_FILE_UPLOAD_API: string; - readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From 0c31b978240fd3ead89a6f0802ddeddd4ebf3859 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Wed, 31 Dec 2025 00:10:44 +0800 Subject: [PATCH 03/43] =?UTF-8?q?Revert=20"feat:=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B0=8A=E4=BA=AB=E5=8C=85=E6=B8=A0=E9=81=93"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 70ae2fab44f8de01437aa060d404b072f8b157b9. --- .../Dtos/Channel/AiAppCreateInput.cs | 42 -- .../Dtos/Channel/AiAppDto.cs | 42 -- .../Dtos/Channel/AiAppGetListInput.cs | 14 - .../Dtos/Channel/AiAppUpdateInput.cs | 48 -- .../Dtos/Channel/AiModelCreateInput.cs | 96 --- .../Dtos/Channel/AiModelDto.cs | 84 --- .../Dtos/Channel/AiModelGetListInput.cs | 24 - .../Dtos/Channel/AiModelUpdateInput.cs | 102 ---- .../IServices/IChannelService.cs | 86 --- .../Services/Activities/DailyTaskService.cs | 8 +- .../Services/ChannelService.cs | 233 -------- .../Services/Chat/AiChatService.cs | 27 +- .../Services/Chat/AiImageService.cs | 3 - .../Services/Chat/ModelService.cs | 5 +- .../Services/Chat/TokenService.cs | 5 +- .../Services/OpenApiService.cs | 7 +- .../Services/UsageStatisticsService.cs | 7 +- .../Entities/Model/AiModelEntity.cs | 5 - .../Managers/AiGateWayManager.cs | 39 +- .../Managers/ChatManager.cs | 12 +- .../Managers/TokenManager.cs | 30 +- Yi.Ai.Vue3/src/api/channel/index.ts | 100 ---- Yi.Ai.Vue3/src/api/channel/types.ts | 121 ---- .../src/pages/console/channel/index.vue | 548 ------------------ Yi.Ai.Vue3/src/pages/console/index.vue | 1 - .../src/routers/modules/staticRouter.ts | 8 - Yi.Ai.Vue3/types/components.d.ts | 2 - Yi.Ai.Vue3/types/import_meta.d.ts | 1 + 28 files changed, 34 insertions(+), 1666 deletions(-) delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs delete mode 100644 Yi.Ai.Vue3/src/api/channel/index.ts delete mode 100644 Yi.Ai.Vue3/src/api/channel/types.ts delete mode 100644 Yi.Ai.Vue3/src/pages/console/channel/index.vue diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs deleted file mode 100644 index f3c8ee6f..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 创建AI应用输入 -/// -public class AiAppCreateInput -{ - /// - /// 应用名称 - /// - [Required(ErrorMessage = "应用名称不能为空")] - [StringLength(100, ErrorMessage = "应用名称不能超过100个字符")] - public string Name { get; set; } - - /// - /// 应用终结点 - /// - [Required(ErrorMessage = "应用终结点不能为空")] - [StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")] - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - [StringLength(500, ErrorMessage = "额外URL不能超过500个字符")] - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - [Required(ErrorMessage = "应用Key不能为空")] - [StringLength(500, ErrorMessage = "应用Key不能超过500个字符")] - public string ApiKey { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs deleted file mode 100644 index 95be653d..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// AI应用DTO -/// -public class AiAppDto -{ - /// - /// 应用ID - /// - public Guid Id { get; set; } - - /// - /// 应用名称 - /// - public string Name { get; set; } - - /// - /// 应用终结点 - /// - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - public string ApiKey { get; set; } - - /// - /// 排序 - /// - public int OrderNum { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreationTime { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs deleted file mode 100644 index c1e98085..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Yi.Framework.Ddd.Application.Contracts; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 获取AI应用列表输入 -/// -public class AiAppGetListInput : PagedAllResultRequestDto -{ - /// - /// 搜索关键词(搜索应用名称) - /// - public string? SearchKey { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs deleted file mode 100644 index ee14667c..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 更新AI应用输入 -/// -public class AiAppUpdateInput -{ - /// - /// 应用ID - /// - [Required(ErrorMessage = "应用ID不能为空")] - public Guid Id { get; set; } - - /// - /// 应用名称 - /// - [Required(ErrorMessage = "应用名称不能为空")] - [StringLength(100, ErrorMessage = "应用名称不能超过100个字符")] - public string Name { get; set; } - - /// - /// 应用终结点 - /// - [Required(ErrorMessage = "应用终结点不能为空")] - [StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")] - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - [StringLength(500, ErrorMessage = "额外URL不能超过500个字符")] - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - [Required(ErrorMessage = "应用Key不能为空")] - [StringLength(500, ErrorMessage = "应用Key不能超过500个字符")] - public string ApiKey { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs deleted file mode 100644 index 9e956c52..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 创建AI模型输入 -/// -public class AiModelCreateInput -{ - /// - /// 处理名 - /// - [Required(ErrorMessage = "处理名不能为空")] - [StringLength(100, ErrorMessage = "处理名不能超过100个字符")] - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - [StringLength(200, ErrorMessage = "模型ID不能超过200个字符")] - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - [Required(ErrorMessage = "模型名称不能为空")] - [StringLength(200, ErrorMessage = "模型名称不能超过200个字符")] - public string Name { get; set; } - - /// - /// 模型描述 - /// - [StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")] - public string? Description { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - [Required(ErrorMessage = "AI应用ID不能为空")] - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - [StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")] - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - [Required(ErrorMessage = "模型类型不能为空")] - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - [Required(ErrorMessage = "模型API类型不能为空")] - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")] - public decimal Multiplier { get; set; } = 1; - - /// - /// 模型显示倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")] - public decimal MultiplierShow { get; set; } = 1; - - /// - /// 供应商分组名称 - /// - [StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")] - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - [StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")] - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs deleted file mode 100644 index 3b93b680..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// AI模型DTO -/// -public class AiModelDto -{ - /// - /// 模型ID - /// - public Guid Id { get; set; } - - /// - /// 处理名 - /// - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - public string Name { get; set; } - - /// - /// 模型描述 - /// - public string? Description { get; set; } - - /// - /// 排序 - /// - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - public decimal Multiplier { get; set; } - - /// - /// 模型显示倍率 - /// - public decimal MultiplierShow { get; set; } - - /// - /// 供应商分组名称 - /// - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs deleted file mode 100644 index c71cfc9b..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Yi.Framework.Ddd.Application.Contracts; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 获取AI模型列表输入 -/// -public class AiModelGetListInput : PagedAllResultRequestDto -{ - /// - /// 搜索关键词(搜索模型名称、模型ID) - /// - public string? SearchKey { get; set; } - - /// - /// AI应用ID筛选 - /// - public Guid? AiAppId { get; set; } - - /// - /// 是否只显示尊享模型 - /// - public bool? IsPremiumOnly { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs deleted file mode 100644 index b35065d9..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 更新AI模型输入 -/// -public class AiModelUpdateInput -{ - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - public Guid Id { get; set; } - - /// - /// 处理名 - /// - [Required(ErrorMessage = "处理名不能为空")] - [StringLength(100, ErrorMessage = "处理名不能超过100个字符")] - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - [StringLength(200, ErrorMessage = "模型ID不能超过200个字符")] - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - [Required(ErrorMessage = "模型名称不能为空")] - [StringLength(200, ErrorMessage = "模型名称不能超过200个字符")] - public string Name { get; set; } - - /// - /// 模型描述 - /// - [StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")] - public string? Description { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - [Required(ErrorMessage = "AI应用ID不能为空")] - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - [StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")] - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - [Required(ErrorMessage = "模型类型不能为空")] - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - [Required(ErrorMessage = "模型API类型不能为空")] - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")] - public decimal Multiplier { get; set; } - - /// - /// 模型显示倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")] - public decimal MultiplierShow { get; set; } - - /// - /// 供应商分组名称 - /// - [StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")] - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - [StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")] - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs deleted file mode 100644 index d45b542e..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Volo.Abp.Application.Dtos; -using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -namespace Yi.Framework.AiHub.Application.Contracts.IServices; - -/// -/// 渠道商管理服务接口 -/// -public interface IChannelService -{ - #region AI应用管理 - - /// - /// 获取AI应用列表 - /// - /// 查询参数 - /// 分页应用列表 - Task> GetAppListAsync(AiAppGetListInput input); - - /// - /// 根据ID获取AI应用 - /// - /// 应用ID - /// 应用详情 - Task GetAppByIdAsync(Guid id); - - /// - /// 创建AI应用 - /// - /// 创建输入 - /// 创建的应用 - Task CreateAppAsync(AiAppCreateInput input); - - /// - /// 更新AI应用 - /// - /// 更新输入 - /// 更新后的应用 - Task UpdateAppAsync(AiAppUpdateInput input); - - /// - /// 删除AI应用 - /// - /// 应用ID - Task DeleteAppAsync(Guid id); - - #endregion - - #region AI模型管理 - - /// - /// 获取AI模型列表 - /// - /// 查询参数 - /// 分页模型列表 - Task> GetModelListAsync(AiModelGetListInput input); - - /// - /// 根据ID获取AI模型 - /// - /// 模型ID - /// 模型详情 - Task GetModelByIdAsync(Guid id); - - /// - /// 创建AI模型 - /// - /// 创建输入 - /// 创建的模型 - Task CreateModelAsync(AiModelCreateInput input); - - /// - /// 更新AI模型 - /// - /// 更新输入 - /// 更新后的模型 - Task UpdateModelAsync(AiModelUpdateInput input); - - /// - /// 删除AI模型(软删除) - /// - /// 模型ID - Task DeleteModelAsync(Guid id); - - #endregion -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs index a7e65a0a..013a82f4 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs @@ -179,16 +179,10 @@ public class DailyTaskService : ApplicationService var tomorrow = today.AddDays(1); // 查询今日所有使用尊享包模型的消息(role=system 表示消耗) - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); - var totalTokens = await _messageRepository._DbQueryable .Where(x => x.UserId == userId) .Where(x => x.Role == "system") // system角色表示实际消耗 - .Where(x => premiumModelIds.Contains(x.ModelId)) // 尊享包模型 + .Where(x => PremiumPackageConst.ModeIds.Contains(x.ModelId)) // 尊享包模型 .Where(x => x.CreationTime >= today && x.CreationTime < tomorrow) .SumAsync(x => x.TokenUsage.TotalTokenCount); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs deleted file mode 100644 index 7e7a2f9b..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs +++ /dev/null @@ -1,233 +0,0 @@ -using Mapster; -using Microsoft.AspNetCore.Authorization; -using SqlSugar; -using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; -using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; -using Yi.Framework.AiHub.Application.Contracts.IServices; -using Yi.Framework.AiHub.Domain.Entities.Model; -using Yi.Framework.SqlSugarCore.Abstractions; - -namespace Yi.Framework.AiHub.Application.Services; - -/// -/// 渠道商管理服务实现 -/// -[Authorize] -public class ChannelService : ApplicationService, IChannelService -{ - private readonly ISqlSugarRepository _appRepository; - private readonly ISqlSugarRepository _modelRepository; - - public ChannelService( - ISqlSugarRepository appRepository, - ISqlSugarRepository modelRepository) - { - _appRepository = appRepository; - _modelRepository = modelRepository; - } - - #region AI应用管理 - - /// - /// 获取AI应用列表 - /// - public async Task> GetAppListAsync(AiAppGetListInput input) - { - RefAsync total = 0; - - var entities = await _appRepository._DbQueryable - .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => x.Name.Contains(input.SearchKey)) - .OrderBy(x => x.OrderNum) - .OrderByDescending(x => x.CreationTime) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - - var output = entities.Adapt>(); - return new PagedResultDto(total, output); - } - - /// - /// 根据ID获取AI应用 - /// - public async Task GetAppByIdAsync(Guid id) - { - var entity = await _appRepository.GetByIdAsync(id); - return entity.Adapt(); - } - - /// - /// 创建AI应用 - /// - public async Task CreateAppAsync(AiAppCreateInput input) - { - var entity = new AiAppAggregateRoot - { - Name = input.Name, - Endpoint = input.Endpoint, - ExtraUrl = input.ExtraUrl, - ApiKey = input.ApiKey, - OrderNum = input.OrderNum - }; - - await _appRepository.InsertAsync(entity); - return entity.Adapt(); - } - - /// - /// 更新AI应用 - /// - public async Task UpdateAppAsync(AiAppUpdateInput input) - { - var entity = await _appRepository.GetByIdAsync(input.Id); - - entity.Name = input.Name; - entity.Endpoint = input.Endpoint; - entity.ExtraUrl = input.ExtraUrl; - entity.ApiKey = input.ApiKey; - entity.OrderNum = input.OrderNum; - - await _appRepository.UpdateAsync(entity); - return entity.Adapt(); - } - - /// - /// 删除AI应用 - /// - public async Task DeleteAppAsync(Guid id) - { - // 检查是否有关联的模型 - var hasModels = await _modelRepository._DbQueryable - .Where(x => x.AiAppId == id && !x.IsDeleted) - .AnyAsync(); - - if (hasModels) - { - throw new Volo.Abp.UserFriendlyException("该应用下存在模型,无法删除"); - } - - await _appRepository.DeleteAsync(id); - } - - #endregion - - #region AI模型管理 - - /// - /// 获取AI模型列表 - /// - public async Task> GetModelListAsync(AiModelGetListInput input) - { - RefAsync total = 0; - - var query = _modelRepository._DbQueryable - .Where(x => !x.IsDeleted) - .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => - x.Name.Contains(input.SearchKey) || x.ModelId.Contains(input.SearchKey)) - .WhereIF(input.AiAppId.HasValue, x => x.AiAppId == input.AiAppId.Value) - .WhereIF(input.IsPremiumOnly == true, x => x.IsPremium); - - var entities = await query - .OrderBy(x => x.OrderNum) - .OrderByDescending(x => x.Id) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - - var output = entities.Adapt>(); - return new PagedResultDto(total, output); - } - - /// - /// 根据ID获取AI模型 - /// - public async Task GetModelByIdAsync(Guid id) - { - var entity = await _modelRepository.GetByIdAsync(id); - return entity.Adapt(); - } - - /// - /// 创建AI模型 - /// - public async Task CreateModelAsync(AiModelCreateInput input) - { - // 验证应用是否存在 - var appExists = await _appRepository._DbQueryable - .Where(x => x.Id == input.AiAppId) - .AnyAsync(); - - if (!appExists) - { - throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在"); - } - - var entity = new AiModelEntity - { - HandlerName = input.HandlerName, - ModelId = input.ModelId, - Name = input.Name, - Description = input.Description, - OrderNum = input.OrderNum, - AiAppId = input.AiAppId, - ExtraInfo = input.ExtraInfo, - ModelType = input.ModelType, - ModelApiType = input.ModelApiType, - Multiplier = input.Multiplier, - MultiplierShow = input.MultiplierShow, - ProviderName = input.ProviderName, - IconUrl = input.IconUrl, - IsPremium = input.IsPremium, - IsDeleted = false - }; - - await _modelRepository.InsertAsync(entity); - return entity.Adapt(); - } - - /// - /// 更新AI模型 - /// - public async Task UpdateModelAsync(AiModelUpdateInput input) - { - var entity = await _modelRepository.GetByIdAsync(input.Id); - - // 验证应用是否存在 - if (entity.AiAppId != input.AiAppId) - { - var appExists = await _appRepository._DbQueryable - .Where(x => x.Id == input.AiAppId) - .AnyAsync(); - - if (!appExists) - { - throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在"); - } - } - - entity.HandlerName = input.HandlerName; - entity.ModelId = input.ModelId; - entity.Name = input.Name; - entity.Description = input.Description; - entity.OrderNum = input.OrderNum; - entity.AiAppId = input.AiAppId; - entity.ExtraInfo = input.ExtraInfo; - entity.ModelType = input.ModelType; - entity.ModelApiType = input.ModelApiType; - entity.Multiplier = input.Multiplier; - entity.MultiplierShow = input.MultiplierShow; - entity.ProviderName = input.ProviderName; - entity.IconUrl = input.IconUrl; - entity.IsPremium = input.IsPremium; - - await _modelRepository.UpdateAsync(entity); - return entity.Adapt(); - } - - /// - /// 删除AI模型(软删除) - /// - public async Task DeleteModelAsync(Guid id) - { - await _modelRepository.DeleteByIdAsync(id); - } - - #endregion -} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs index 42f0b1a5..341638ce 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs @@ -109,7 +109,7 @@ public class AiChatService : ApplicationService ApiHost = null, ApiKey = null, Remark = x.Description, - IsPremiumPackage = x.IsPremium + IsPremiumPackage = PremiumPackageConst.ModeIds.Contains(x.ModelId) }).ToListAsync(); return output; } @@ -144,21 +144,13 @@ public class AiChatService : ApplicationService } //如果是尊享包服务,需要校验是是否尊享包足够 - if (CurrentUser.IsAuthenticated) + if (CurrentUser.IsAuthenticated && PremiumPackageConst.ModeIds.Contains(input.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + // 检查尊享token包用量 + var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId()); + if (availableTokens <= 0) { - // 检查尊享token包用量 - var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId()); - if (availableTokens <= 0) - { - throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); - } + throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } } @@ -227,12 +219,7 @@ public class AiChatService : ApplicationService } //如果是尊享包服务,需要校验是是否尊享包足够 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.ModelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) { // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index caa7313d..5bffa03d 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -69,9 +69,6 @@ public class AiImageService : ApplicationService } // 尊享包校验 - // 注意: AiImageService目前没有注入AiModelEntity仓储 - // 由于图片生成功能使用频率较低,且当前所有图片模型都不是尊享模型 - // 暂时保留原逻辑,等待后续重构时再注入仓储 if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) { var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs index 0eee3438..98faf61c 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs @@ -41,7 +41,8 @@ public class ModelService : ApplicationService, IModelService input.ModelTypes.Contains(x.ModelType)) .WhereIF(input.ModelApiTypes is not null, x => input.ModelApiTypes.Contains(x.ModelApiType)) - .WhereIF(input.IsPremiumOnly == true, x => x.IsPremium) + .WhereIF(input.IsPremiumOnly == true, x => + PremiumPackageConst.ModeIds.Contains(x.ModelId)) .GroupBy(x => x.ModelId) .Select(x => x.ModelId) .ToPageListAsync(input.SkipCount, input.MaxResultCount, total)); @@ -60,7 +61,7 @@ public class ModelService : ApplicationService, IModelService MultiplierShow = x.First().MultiplierShow, ProviderName = x.First().ProviderName, IconUrl = x.First().IconUrl, - IsPremium = x.First().IsPremium, + IsPremium = PremiumPackageConst.ModeIds.Contains(x.First().ModelId), OrderNum = x.First().OrderNum }).ToList(); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs index d18de550..23e7e666 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs @@ -52,10 +52,7 @@ public class TokenService : ApplicationService } // 获取尊享包模型ID列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; // 批量查询所有Token的尊享包已使用额度 var tokenIds = tokens.Select(t => t.Id).ToList(); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs index eef91f3b..ccb05089 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs @@ -65,12 +65,7 @@ public class OpenApiService : ApplicationService await _aiBlacklistManager.VerifiyAiBlacklist(userId); //如果是尊享包服务,需要校验是是否尊享包足够 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(input.Model)) { // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs index f8c0b3a4..bffce1e6 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs @@ -181,12 +181,7 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic public async Task> GetPremiumTokenUsageByTokenAsync() { var userId = CurrentUser.GetId(); - - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; // 从UsageStatistics表获取尊享模型的token消耗统计(按TokenId聚合) var tokenUsages = await _usageStatisticsRepository._DbQueryable diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs index ef0f1ebd..991d0ff2 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs @@ -80,9 +80,4 @@ public class AiModelEntity : Entity, IOrderNum, ISoftDelete /// 模型图标URL /// public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs index 34cf3486..971fef75 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs @@ -150,12 +150,7 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage, tokenId); // 扣减尊享token包用量 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(request.Model)) { var totalTokens = data.Usage?.TotalTokens ?? 0; if (totalTokens > 0) @@ -305,20 +300,12 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, request.Model, tokenUsage, tokenId); // 扣减尊享token包用量 - if (userId is not null) + if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var totalTokens = tokenUsage.TotalTokens ?? 0; + if (totalTokens > 0) { - var totalTokens = tokenUsage.TotalTokens ?? 0; - if (totalTokens > 0) - { - await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); - } + await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); } } } @@ -376,20 +363,12 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, model, response.Usage, tokenId); // 扣减尊享token包用量 - if (userId is not null) + if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var totalTokens = response.Usage.TotalTokens ?? 0; + if (totalTokens > 0) { - var totalTokens = response.Usage.TotalTokens ?? 0; - if (totalTokens > 0) - { - await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); - } + await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); } } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs index 5e411fbb..b4d9110c 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs @@ -15,7 +15,6 @@ using Volo.Abp.Domain.Services; using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; using Yi.Framework.AiHub.Domain.AiGateWay; using Yi.Framework.AiHub.Domain.Entities.Chat; -using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Shared.Attributes; using Yi.Framework.AiHub.Domain.Shared.Consts; @@ -35,13 +34,12 @@ public class ChatManager : DomainService private readonly UsageStatisticsManager _usageStatisticsManager; private readonly PremiumPackageManager _premiumPackageManager; private readonly AiGateWayManager _aiGateWayManager; - private readonly ISqlSugarRepository _aiModelRepository; public ChatManager(ILoggerFactory loggerFactory, ISqlSugarRepository messageRepository, ISqlSugarRepository agentStoreRepository, AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager, PremiumPackageManager premiumPackageManager, - AiGateWayManager aiGateWayManager, ISqlSugarRepository aiModelRepository) + AiGateWayManager aiGateWayManager) { _loggerFactory = loggerFactory; _messageRepository = messageRepository; @@ -50,7 +48,6 @@ public class ChatManager : DomainService _usageStatisticsManager = usageStatisticsManager; _premiumPackageManager = premiumPackageManager; _aiGateWayManager = aiGateWayManager; - _aiModelRepository = aiModelRepository; } /// @@ -210,12 +207,7 @@ public class ChatManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, modelId, usage, tokenId); //扣减尊享token包用量 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == modelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(modelId)) { var totalTokens = usage?.TotalTokens ?? 0; if (totalTokens > 0) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs index 28595a48..dd2ae774 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs @@ -1,7 +1,6 @@ using SqlSugar; using Volo.Abp.Domain.Services; using Yi.Framework.AiHub.Domain.Entities; -using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Shared.Consts; using Yi.Framework.SqlSugarCore.Abstractions; @@ -28,16 +27,13 @@ public class TokenManager : DomainService { private readonly ISqlSugarRepository _tokenRepository; private readonly ISqlSugarRepository _usageStatisticsRepository; - private readonly ISqlSugarRepository _aiModelRepository; public TokenManager( ISqlSugarRepository tokenRepository, - ISqlSugarRepository usageStatisticsRepository, - ISqlSugarRepository aiModelRepository) + ISqlSugarRepository usageStatisticsRepository) { _tokenRepository = tokenRepository; _usageStatisticsRepository = usageStatisticsRepository; - _aiModelRepository = aiModelRepository; } /// @@ -80,20 +76,14 @@ public class TokenManager : DomainService } // 如果是尊享模型且Token设置了额度限制,检查是否超限 - if (!string.IsNullOrEmpty(modelId) && entity.PremiumQuotaLimit.HasValue) + if (!string.IsNullOrEmpty(modelId) && + PremiumPackageConst.ModeIds.Contains(modelId) && + entity.PremiumQuotaLimit.HasValue) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == modelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id); + if (usedQuota >= entity.PremiumQuotaLimit.Value) { - var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id); - if (usedQuota >= entity.PremiumQuotaLimit.Value) - { - throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403"); - } + throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403"); } } @@ -109,11 +99,7 @@ public class TokenManager : DomainService /// private async Task GetTokenPremiumUsedQuotaAsync(Guid userId, Guid tokenId) { - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; var usedQuota = await _usageStatisticsRepository._DbQueryable .Where(x => x.UserId == userId && x.TokenId == tokenId && premiumModelIds.Contains(x.ModelId)) diff --git a/Yi.Ai.Vue3/src/api/channel/index.ts b/Yi.Ai.Vue3/src/api/channel/index.ts deleted file mode 100644 index cd31154f..00000000 --- a/Yi.Ai.Vue3/src/api/channel/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { del, get, post, put } from '@/utils/request'; -import type { - AiAppDto, - AiAppCreateInput, - AiAppUpdateInput, - AiAppGetListInput, - AiModelDto, - AiModelCreateInput, - AiModelUpdateInput, - AiModelGetListInput, - PagedResultDto, -} from './types'; - -// ==================== AI应用管理 ==================== - -// 获取AI应用列表 -export function getAppList(params?: AiAppGetListInput) { - const queryParams = new URLSearchParams(); - if (params?.searchKey) { - queryParams.append('SearchKey', params.searchKey); - } - if (params?.skipCount !== undefined) { - queryParams.append('SkipCount', params.skipCount.toString()); - } - if (params?.maxResultCount !== undefined) { - queryParams.append('MaxResultCount', params.maxResultCount.toString()); - } - - const queryString = queryParams.toString(); - const url = queryString ? `/channel/app?${queryString}` : '/channel/app'; - - return get>(url).json(); -} - -// 根据ID获取AI应用 -export function getAppById(id: string) { - return get(`/channel/app/${id}`).json(); -} - -// 创建AI应用 -export function createApp(data: AiAppCreateInput) { - return post('/channel/app', data).json(); -} - -// 更新AI应用 -export function updateApp(data: AiAppUpdateInput) { - return put('/channel/app', data).json(); -} - -// 删除AI应用 -export function deleteApp(id: string) { - return del(`/channel/app/${id}`).json(); -} - -// ==================== AI模型管理 ==================== - -// 获取AI模型列表 -export function getModelList(params?: AiModelGetListInput) { - const queryParams = new URLSearchParams(); - if (params?.searchKey) { - queryParams.append('SearchKey', params.searchKey); - } - if (params?.aiAppId) { - queryParams.append('AiAppId', params.aiAppId); - } - if (params?.isPremiumOnly !== undefined) { - queryParams.append('IsPremiumOnly', params.isPremiumOnly.toString()); - } - if (params?.skipCount !== undefined) { - queryParams.append('SkipCount', params.skipCount.toString()); - } - if (params?.maxResultCount !== undefined) { - queryParams.append('MaxResultCount', params.maxResultCount.toString()); - } - - const queryString = queryParams.toString(); - const url = queryString ? `/channel/model?${queryString}` : '/channel/model'; - - return get>(url).json(); -} - -// 根据ID获取AI模型 -export function getModelById(id: string) { - return get(`/channel/model/${id}`).json(); -} - -// 创建AI模型 -export function createModel(data: AiModelCreateInput) { - return post('/channel/model', data).json(); -} - -// 更新AI模型 -export function updateModel(data: AiModelUpdateInput) { - return put('/channel/model', data).json(); -} - -// 删除AI模型 -export function deleteModel(id: string) { - return del(`/channel/model/${id}`).json(); -} diff --git a/Yi.Ai.Vue3/src/api/channel/types.ts b/Yi.Ai.Vue3/src/api/channel/types.ts deleted file mode 100644 index 76a6306a..00000000 --- a/Yi.Ai.Vue3/src/api/channel/types.ts +++ /dev/null @@ -1,121 +0,0 @@ -// 模型类型枚举 -export enum ModelTypeEnum { - Chat = 0, - Image = 1, - Embedding = 2, - PremiumChat = 3, -} - -// 模型API类型枚举 -export enum ModelApiTypeEnum { - OpenAi = 0, - Claude = 1, -} - -// AI应用DTO -export interface AiAppDto { - id: string; - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; - creationTime: string; -} - -// 创建AI应用输入 -export interface AiAppCreateInput { - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; -} - -// 更新AI应用输入 -export interface AiAppUpdateInput { - id: string; - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; -} - -// 获取AI应用列表输入 -export interface AiAppGetListInput { - searchKey?: string; - skipCount?: number; - maxResultCount?: number; -} - -// AI模型DTO -export interface AiModelDto { - id: string; - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 创建AI模型输入 -export interface AiModelCreateInput { - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 更新AI模型输入 -export interface AiModelUpdateInput { - id: string; - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 获取AI模型列表输入 -export interface AiModelGetListInput { - searchKey?: string; - aiAppId?: string; - isPremiumOnly?: boolean; - skipCount?: number; - maxResultCount?: number; -} - -// 分页结果 -export interface PagedResultDto { - items: T[]; - totalCount: number; -} diff --git a/Yi.Ai.Vue3/src/pages/console/channel/index.vue b/Yi.Ai.Vue3/src/pages/console/channel/index.vue deleted file mode 100644 index c780fe70..00000000 --- a/Yi.Ai.Vue3/src/pages/console/channel/index.vue +++ /dev/null @@ -1,548 +0,0 @@ - - - - - diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 15ae21dd..44aece8f 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -19,7 +19,6 @@ const navItems = [ { name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' }, { name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' }, { name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' }, - { name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index 496bb214..b94431ec 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -207,14 +207,6 @@ export const layoutRouter: RouteRecordRaw[] = [ title: '激活码兑换', }, }, - { - path: 'channel', - name: 'consoleChannel', - component: () => import('@/pages/console/channel/index.vue'), - meta: { - title: '渠道商管理', - }, - }, ], }, ], diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 16d28441..9ef439fb 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,8 +25,6 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] - ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index 8f2a798b..c98d612e 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,6 +7,7 @@ interface ImportMetaEnv { readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; readonly VITE_FILE_UPLOAD_API: string; + readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From 77a9a64a410fa7b4f904c98c992edf3ec311a5bf Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Wed, 31 Dec 2025 00:59:06 +0800 Subject: [PATCH 04/43] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=9E=B6=E6=9E=84=E9=87=8D=E6=9E=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layouts/components/ChatAside/index.vue | 138 +----------------- .../Header/components/Collapse.vue | 38 ++--- .../Header/components/CreateChat.vue | 2 + .../pages/chat/layouts/chatDefaul/index.vue | 54 ++++++- .../pages/chat/layouts/chatWithId/index.vue | 62 ++++++-- Yi.Ai.Vue3/src/stores/modules/session.ts | 2 +- 6 files changed, 125 insertions(+), 171 deletions(-) diff --git a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue index c0e8710d..90bc5731 100644 --- a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue @@ -124,141 +124,7 @@ function handleMenuCommand(command: string, item: ConversationItem) { - event.preventDefault(); - // 在折叠状态下触发删除确认 - ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - confirmButtonClass: 'el-button--danger', - cancelButtonClass: 'el-button--info', - roundButton: true, - autofocus: false, - }) - .then(() => { - sessionStore.deleteSessions([item.id!]); - nextTick(() => { - if (item.id === active.value) { - sessionStore.createSessionBtn(); - } - }); - }) - .catch(() => { - // 取消删除 - }); -} - -// 折叠状态下点击更多按钮 -function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem) { - event.stopPropagation(); - - // 创建一个简单的菜单 - ElMessageBox({ - title: '对话操作', - message: ` -
- - -
- `, - showConfirmButton: false, - showCancelButton: false, - dangerouslyUseHTMLString: true, - customClass: 'collapsed-menu-dialog', - closeOnClickModal: true, - closeOnPressEscape: true, - }).then(() => { - // 对话框关闭 - }).catch(() => { - // 对话框关闭 - }); - - // 添加菜单项点击事件 - nextTick(() => { - const menuItems = document.querySelectorAll('.menu-item'); - menuItems.forEach((itemEl) => { - itemEl.addEventListener('click', (e) => { - const action = (e.currentTarget as HTMLElement).dataset.action; - if (action === 'delete') { - ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - confirmButtonClass: 'el-button--danger', - cancelButtonClass: 'el-button--info', - roundButton: true, - autofocus: false, - }) - .then(() => { - sessionStore.deleteSessions([item.id!]); - nextTick(() => { - if (item.id === active.value) { - sessionStore.createSessionBtn(); - } - }); - }) - .catch(() => { - // 取消删除 - }); - } - else if (action === 'rename') { - ElMessageBox.prompt('', '编辑对话名称', { - confirmButtonText: '确定', - cancelButtonText: '取消', - inputErrorMessage: '请输入对话名称', - confirmButtonClass: 'el-button--primary', - cancelButtonClass: 'el-button--info', - roundButton: true, - inputValue: item.sessionTitle, - autofocus: false, - inputValidator: (value) => { - return !!value; - }, - }).then(({ value }) => { - sessionStore - .updateSession({ - id: item.id!, - sessionTitle: value, - sessionContent: item.sessionContent, - }) - .then(() => { - ElMessage({ - type: 'success', - message: '修改成功', - }); - nextTick(() => { - if (sessionStore.currentSession?.id === item.id) { - sessionStore.setCurrentSession({ - ...item, - sessionTitle: value, - }); - } - }); - }); - }); - } - - // 关闭菜单对话框 - document.querySelector('.collapsed-menu-dialog .el-message-box__headerbtn')?.dispatchEvent(new Event('click')); - }); - }); - }); + designStore.setIsCollapseConversationList(!designStore.isCollapseConversationList); } @@ -446,7 +312,7 @@ function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue index 5ec82222..9553e1bf 100644 --- a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue @@ -1,5 +1,7 @@ + + + + diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 44aece8f..15ae21dd 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -19,6 +19,7 @@ const navItems = [ { name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' }, { name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' }, { name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' }, + { name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index b94431ec..496bb214 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -207,6 +207,14 @@ export const layoutRouter: RouteRecordRaw[] = [ title: '激活码兑换', }, }, + { + path: 'channel', + name: 'consoleChannel', + component: () => import('@/pages/console/channel/index.vue'), + meta: { + title: '渠道商管理', + }, + }, ], }, ], diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 9ef439fb..16d28441 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,6 +25,8 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index c98d612e..8f2a798b 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,7 +7,6 @@ interface ImportMetaEnv { readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; readonly VITE_FILE_UPLOAD_API: string; - readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From 953fbc043b4a6e1e25b2a0bae5a448cff22047a6 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Thu, 1 Jan 2026 18:25:43 +0800 Subject: [PATCH 07/43] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E5=95=86=E7=AE=A1=E7=90=86=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/{ => Activities}/ActivationCodeService.cs | 0 Yi.Ai.Vue3/src/pages/console/channel/index.vue | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/{ => Activities}/ActivationCodeService.cs (100%) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ActivationCodeService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/ActivationCodeService.cs similarity index 100% rename from Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ActivationCodeService.cs rename to Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/ActivationCodeService.cs diff --git a/Yi.Ai.Vue3/src/pages/console/channel/index.vue b/Yi.Ai.Vue3/src/pages/console/channel/index.vue index c780fe70..b177512a 100644 --- a/Yi.Ai.Vue3/src/pages/console/channel/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/channel/index.vue @@ -338,6 +338,7 @@ onMounted(() => { + @@ -450,8 +451,7 @@ onMounted(() => { diff --git a/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue b/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue index 29fc25ab..60d7f8dd 100644 --- a/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue +++ b/Yi.Ai.Vue3/src/layouts/LayoutMobile/index.vue @@ -1,8 +1,288 @@ - - + + - + diff --git a/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue b/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue index 32264f60..5d752ac4 100644 --- a/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue +++ b/Yi.Ai.Vue3/src/layouts/LayoutVertical/index.vue @@ -2,8 +2,6 @@ - - - - diff --git a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue new file mode 100644 index 00000000..c0e8710d --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue @@ -0,0 +1,918 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue index 26675ad5..0b98e38e 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -30,40 +30,40 @@ const popoverRef = ref(); // 弹出面板内容 const popoverList = ref([ - { - key: '5', - title: '控制台', - icon: 'settings-4-fill', - }, - { - key: '3', - divider: true, - }, - { - key: '7', - title: '公告', - icon: 'notification-fill', - }, - { - key: '8', - title: '模型库', - icon: 'apps-fill', - }, - { - key: '9', - title: '文档', - icon: 'book-fill', - }, - - { - key: '6', - title: '新手引导', - icon: 'dashboard-fill', - }, - { - key: '3', - divider: true, - }, + // { + // key: '5', + // title: '控制台', + // icon: 'settings-4-fill', + // }, + // { + // key: '3', + // divider: true, + // }, + // { + // key: '7', + // title: '公告', + // icon: 'notification-fill', + // }, + // { + // key: '8', + // title: '模型库', + // icon: 'apps-fill', + // }, + // { + // key: '9', + // title: '文档', + // icon: 'book-fill', + // }, + // + // { + // key: '6', + // title: '新手引导', + // icon: 'dashboard-fill', + // }, + // { + // key: '3', + // divider: true, + // }, { key: '4', title: '退出登录', @@ -130,7 +130,9 @@ function handleClick(item: any) { ElMessage.warning('暂未开放'); break; case '5': - openDialog(); + // 打开控制台 + popoverRef.value?.hide?.(); + router.push('/console'); break; case '6': handleStartTutorial(); diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue new file mode 100644 index 00000000..970e1509 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/StartChatBtn.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue new file mode 100644 index 00000000..975fd521 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index aaacdec3..1db0ae27 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -1,94 +1,50 @@ - + -
- - -
-
- - -
- -
-
+
+ <!– 左侧logo和品牌区域 –> +
+
+ + 意心AI
+
- -
- - - - - - - -
+ <!– 右侧功能按钮区域 –> +
+ + + + + + + + +
@@ -98,20 +54,483 @@ function handleOpenConsole() { .header-container { display: flex; flex-shrink: 0; - flex-direction: column; width: 100%; - height: fit-content; + height: var(--header-container-default-height, 60px); + .header-box { + display: flex; + align-items: center; + justify-content: space-between; width: 100%; - width: calc( - 100% - var(--sidebar-left-container-default-width, 0px) - var( - --sidebar-right-container-default-width, - 0px - ) - ); - height: var(--header-container-default-heigth); - margin: 0 var(--sidebar-right-container-default-width, 0) 0 - var(--sidebar-left-container-default-width, 0); + height: 100%; + padding: 0 16px; + background: var(--header-bg-color, #ffffff); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); + } + + // 左侧品牌区域 + .left-section { + display: flex; + align-items: center; + min-width: fit-content; + flex-shrink: 0; + + .brand-container { + display: flex; + align-items: center; + gap: 8px; + + .logo-img { + width: 36px; // 优化为更合适的大小 + height: 36px; + flex-shrink: 0; + transition: transform 0.2s ease; + + &:hover { + transform: scale(1.05); + } + } + + .brand-text { + font-size: 22px; // 减小字体大小 + font-weight: bold; + color: var(--brand-color, #000000); + white-space: nowrap; + letter-spacing: -0.5px; + transition: color 0.2s ease; + + &:hover { + //color: var(--brand-hover-color, #40a9ff); + } + } + } + } + + // 右侧功能区域 + .right-section { + display: flex; + align-items: center; + gap: 12px; // 优化按钮间距 + height: 100%; + flex-shrink: 0; + + // 统一按钮样式 + :deep(.menu-button) { + height: 36px; + display: flex; + align-items: center; + justify-content: center; + } + } +} + +--> + + + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue new file mode 100644 index 00000000..be93e273 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Aside/index.vue @@ -0,0 +1,725 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue b/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue new file mode 100644 index 00000000..f3409aa6 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/DesignConfig/index.vue @@ -0,0 +1,8 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue new file mode 100644 index 00000000..bd7f2647 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AiTutorialBtn.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue new file mode 100644 index 00000000..a6c6adda --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/AnnouncementBtn.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue new file mode 100644 index 00000000..7f1ed3c1 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Avatar.vue @@ -0,0 +1,499 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue new file mode 100644 index 00000000..9e5b512a --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/BuyBtn.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue new file mode 100644 index 00000000..9868df5d --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/Collapse.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue new file mode 100644 index 00000000..3091d22e --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ConsoleBtn.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue new file mode 100644 index 00000000..5ec82222 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue @@ -0,0 +1,45 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue new file mode 100644 index 00000000..64b43c4c --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/LoginBtn.vue @@ -0,0 +1,29 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue new file mode 100644 index 00000000..73347ac9 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ModelLibraryBtn.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue new file mode 100644 index 00000000..970e1509 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/StartChatBtn.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue new file mode 100644 index 00000000..975fd521 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/ThemeBtn.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue new file mode 100644 index 00000000..767e2d53 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TitleEditing.vue @@ -0,0 +1,87 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue new file mode 100644 index 00000000..524ae1cd --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/TutorialBtn.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue new file mode 100644 index 00000000..d8dc7806 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/index.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue new file mode 100644 index 00000000..a84746fd --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Logo/index.vue @@ -0,0 +1,8 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue b/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue new file mode 100644 index 00000000..59622a62 --- /dev/null +++ b/Yi.Ai.Vue3/src/layouts/components0/Main/index.vue @@ -0,0 +1,89 @@ + + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/index.vue b/Yi.Ai.Vue3/src/layouts/index.vue index ed682c5e..50a80f49 100644 --- a/Yi.Ai.Vue3/src/layouts/index.vue +++ b/Yi.Ai.Vue3/src/layouts/index.vue @@ -1,21 +1,41 @@ + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue b/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue new file mode 100644 index 00000000..815896e9 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/conversation/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/image/index.vue b/Yi.Ai.Vue3/src/pages/chat/image/index.vue new file mode 100644 index 00000000..b319c725 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/image/index.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/index.vue b/Yi.Ai.Vue3/src/pages/chat/index.vue index 933c9a65..82619c11 100644 --- a/Yi.Ai.Vue3/src/pages/chat/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/index.vue @@ -1,31 +1,304 @@ - diff --git a/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue b/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue index 44180051..2ec17b1c 100644 --- a/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/layouts/chatDefaul/index.vue @@ -7,162 +7,259 @@ import { ElMessage } from 'element-plus'; import { nextTick, ref, watch } from 'vue'; import ModelSelect from '@/components/ModelSelect/index.vue'; import WelecomeText from '@/components/WelecomeText/index.vue'; -import { useGuideTourStore, useUserStore } from '@/stores'; +import { useUserStore } from '@/stores'; import { useFilesStore } from '@/stores/modules/files'; - import { useSessionStore } from '@/stores/modules/session'; +// Store 实例 const userStore = useUserStore(); const sessionStore = useSessionStore(); const filesStore = useFilesStore(); -const guideTourStore = useGuideTourStore(); -const senderValue = ref(''); -const senderRef = ref(); +// 响应式数据 +const senderValue = ref(''); // 输入框内容 +const senderRef = ref(); // Sender 组件引用 const isSending = ref(false); // 发送状态标志 -// 防抖发送函数 -const debouncedSend = useDebounceFn(async () => { - if (!senderValue.value.trim()) { - ElMessage.warning('消息内容不能为空'); - return; - } +/** + * 防抖发送消息函数 + */ +const debouncedSend = useDebounceFn( + async () => { + // 1. 验证输入 + if (!senderValue.value.trim()) { + ElMessage.warning('消息内容不能为空'); + return; + } - if (isSending.value) { - ElMessage.warning('请等待上一条消息发送完成'); - return; - } + // 2. 检查是否正在发送 + if (isSending.value) { + ElMessage.warning('请等待上一条消息发送完成'); + return; + } - const content = senderValue.value; - isSending.value = true; + // 3. 准备发送数据 + const content = senderValue.value.trim(); + isSending.value = true; - try { - localStorage.setItem('chatContent', content); - await sessionStore.createSessionList({ - userId: userStore.userInfo?.userId as number, - sessionContent: content, - sessionTitle: content.slice(0, 10), - remark: content.slice(0, 10), - }); - senderValue.value = ''; // 清空输入框 - } - catch (error: any) { - console.error('发送消息失败:', error); - ElMessage.error(error); - } - finally { - isSending.value = false; - } -}, 800, { leading: true, trailing: false }); // 800ms防抖 + try { + // 4. 保存到本地存储(可选,用于页面刷新后恢复) + localStorage.setItem('chatContent', content); -// 处理发送事件 + // 5. 创建会话 + await sessionStore.createSessionList({ + userId: userStore.userInfo?.userId as number, + sessionContent: content, + sessionTitle: content.slice(0, 10), + remark: content.slice(0, 10), + }); + + // 6. 清空输入框 + senderValue.value = ''; + } + catch (error: any) { + console.error('发送消息失败:', error); + ElMessage.error(error.message || '发送消息失败'); + } + finally { + // 7. 重置发送状态 + isSending.value = false; + } + }, + 800, // 防抖延迟 + { leading: true, trailing: false }, // 立即执行第一次,忽略后续快速点击 +); + +/** + * 触发发送消息 + */ function handleSend() { debouncedSend(); } +/** + * 删除文件卡片 + * @param _item 文件项 + * @param index 文件索引 + */ function handleDeleteCard(_item: FilesCardProps, index: number) { filesStore.deleteFileByIndex(index); } +/** + * 监听文件列表变化,自动展开/收起 Sender 头部 + */ watch( () => filesStore.filesList.length, (val) => { - if (val > 0) { - nextTick(() => { + nextTick(() => { + if (val > 0) { senderRef.value?.openHeader(); - }); - } - else { - nextTick(() => { + } + else { senderRef.value?.closeHeader(); - }); - } + } + }); }, ); diff --git a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue index 0ecb2ee1..fd6d0046 100644 --- a/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/layouts/chatWithId/index.vue @@ -562,14 +562,16 @@ function handleImagePreview(url: string) { diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue new file mode 100644 index 00000000..44aece8f --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue index df8dc070..264aeb12 100644 --- a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue +++ b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue @@ -266,6 +266,7 @@ onMounted(() => { { diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 44aece8f..15ae21dd 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -19,6 +19,7 @@ const navItems = [ { name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' }, { name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' }, { name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' }, + { name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index b94431ec..496bb214 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -207,6 +207,14 @@ export const layoutRouter: RouteRecordRaw[] = [ title: '激活码兑换', }, }, + { + path: 'channel', + name: 'consoleChannel', + component: () => import('@/pages/console/channel/index.vue'), + meta: { + title: '渠道商管理', + }, + }, ], }, ], diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 9ef439fb..16d28441 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,6 +25,8 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index c98d612e..8f2a798b 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,7 +7,6 @@ interface ImportMetaEnv { readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; readonly VITE_FILE_UPLOAD_API: string; - readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From 6cc00596916a8b5b9d8c4cf665f4cc317da0507b Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Wed, 31 Dec 2025 00:10:44 +0800 Subject: [PATCH 10/43] =?UTF-8?q?Revert=20"feat:=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B0=8A=E4=BA=AB=E5=8C=85=E6=B8=A0=E9=81=93"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 70ae2fab44f8de01437aa060d404b072f8b157b9. --- .../Dtos/Channel/AiAppCreateInput.cs | 42 -- .../Dtos/Channel/AiAppDto.cs | 42 -- .../Dtos/Channel/AiAppGetListInput.cs | 14 - .../Dtos/Channel/AiAppUpdateInput.cs | 48 -- .../Dtos/Channel/AiModelCreateInput.cs | 96 --- .../Dtos/Channel/AiModelDto.cs | 84 --- .../Dtos/Channel/AiModelGetListInput.cs | 24 - .../Dtos/Channel/AiModelUpdateInput.cs | 102 ---- .../IServices/IChannelService.cs | 86 --- .../Services/Activities/DailyTaskService.cs | 8 +- .../Services/ChannelService.cs | 233 -------- .../Services/Chat/AiChatService.cs | 27 +- .../Services/Chat/AiImageService.cs | 3 - .../Services/Chat/ModelService.cs | 5 +- .../Services/Chat/TokenService.cs | 5 +- .../Services/OpenApiService.cs | 7 +- .../Services/UsageStatisticsService.cs | 7 +- .../Entities/Model/AiModelEntity.cs | 5 - .../Managers/AiGateWayManager.cs | 39 +- .../Managers/ChatManager.cs | 12 +- .../Managers/TokenManager.cs | 30 +- Yi.Ai.Vue3/src/api/channel/index.ts | 100 ---- Yi.Ai.Vue3/src/api/channel/types.ts | 121 ---- .../src/pages/console/channel/index.vue | 548 ------------------ Yi.Ai.Vue3/src/pages/console/index.vue | 1 - .../src/routers/modules/staticRouter.ts | 8 - Yi.Ai.Vue3/types/components.d.ts | 2 - Yi.Ai.Vue3/types/import_meta.d.ts | 1 + 28 files changed, 34 insertions(+), 1666 deletions(-) delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs delete mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs delete mode 100644 Yi.Ai.Vue3/src/api/channel/index.ts delete mode 100644 Yi.Ai.Vue3/src/api/channel/types.ts delete mode 100644 Yi.Ai.Vue3/src/pages/console/channel/index.vue diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs deleted file mode 100644 index f3c8ee6f..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppCreateInput.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 创建AI应用输入 -/// -public class AiAppCreateInput -{ - /// - /// 应用名称 - /// - [Required(ErrorMessage = "应用名称不能为空")] - [StringLength(100, ErrorMessage = "应用名称不能超过100个字符")] - public string Name { get; set; } - - /// - /// 应用终结点 - /// - [Required(ErrorMessage = "应用终结点不能为空")] - [StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")] - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - [StringLength(500, ErrorMessage = "额外URL不能超过500个字符")] - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - [Required(ErrorMessage = "应用Key不能为空")] - [StringLength(500, ErrorMessage = "应用Key不能超过500个字符")] - public string ApiKey { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs deleted file mode 100644 index 95be653d..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppDto.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// AI应用DTO -/// -public class AiAppDto -{ - /// - /// 应用ID - /// - public Guid Id { get; set; } - - /// - /// 应用名称 - /// - public string Name { get; set; } - - /// - /// 应用终结点 - /// - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - public string ApiKey { get; set; } - - /// - /// 排序 - /// - public int OrderNum { get; set; } - - /// - /// 创建时间 - /// - public DateTime CreationTime { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs deleted file mode 100644 index c1e98085..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppGetListInput.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Yi.Framework.Ddd.Application.Contracts; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 获取AI应用列表输入 -/// -public class AiAppGetListInput : PagedAllResultRequestDto -{ - /// - /// 搜索关键词(搜索应用名称) - /// - public string? SearchKey { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs deleted file mode 100644 index ee14667c..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiAppUpdateInput.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 更新AI应用输入 -/// -public class AiAppUpdateInput -{ - /// - /// 应用ID - /// - [Required(ErrorMessage = "应用ID不能为空")] - public Guid Id { get; set; } - - /// - /// 应用名称 - /// - [Required(ErrorMessage = "应用名称不能为空")] - [StringLength(100, ErrorMessage = "应用名称不能超过100个字符")] - public string Name { get; set; } - - /// - /// 应用终结点 - /// - [Required(ErrorMessage = "应用终结点不能为空")] - [StringLength(500, ErrorMessage = "应用终结点不能超过500个字符")] - public string Endpoint { get; set; } - - /// - /// 额外URL - /// - [StringLength(500, ErrorMessage = "额外URL不能超过500个字符")] - public string? ExtraUrl { get; set; } - - /// - /// 应用Key - /// - [Required(ErrorMessage = "应用Key不能为空")] - [StringLength(500, ErrorMessage = "应用Key不能超过500个字符")] - public string ApiKey { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs deleted file mode 100644 index 9e956c52..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelCreateInput.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 创建AI模型输入 -/// -public class AiModelCreateInput -{ - /// - /// 处理名 - /// - [Required(ErrorMessage = "处理名不能为空")] - [StringLength(100, ErrorMessage = "处理名不能超过100个字符")] - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - [StringLength(200, ErrorMessage = "模型ID不能超过200个字符")] - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - [Required(ErrorMessage = "模型名称不能为空")] - [StringLength(200, ErrorMessage = "模型名称不能超过200个字符")] - public string Name { get; set; } - - /// - /// 模型描述 - /// - [StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")] - public string? Description { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - [Required(ErrorMessage = "AI应用ID不能为空")] - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - [StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")] - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - [Required(ErrorMessage = "模型类型不能为空")] - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - [Required(ErrorMessage = "模型API类型不能为空")] - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")] - public decimal Multiplier { get; set; } = 1; - - /// - /// 模型显示倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")] - public decimal MultiplierShow { get; set; } = 1; - - /// - /// 供应商分组名称 - /// - [StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")] - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - [StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")] - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs deleted file mode 100644 index 3b93b680..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelDto.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// AI模型DTO -/// -public class AiModelDto -{ - /// - /// 模型ID - /// - public Guid Id { get; set; } - - /// - /// 处理名 - /// - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - public string Name { get; set; } - - /// - /// 模型描述 - /// - public string? Description { get; set; } - - /// - /// 排序 - /// - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - public decimal Multiplier { get; set; } - - /// - /// 模型显示倍率 - /// - public decimal MultiplierShow { get; set; } - - /// - /// 供应商分组名称 - /// - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs deleted file mode 100644 index c71cfc9b..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelGetListInput.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Yi.Framework.Ddd.Application.Contracts; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 获取AI模型列表输入 -/// -public class AiModelGetListInput : PagedAllResultRequestDto -{ - /// - /// 搜索关键词(搜索模型名称、模型ID) - /// - public string? SearchKey { get; set; } - - /// - /// AI应用ID筛选 - /// - public Guid? AiAppId { get; set; } - - /// - /// 是否只显示尊享模型 - /// - public bool? IsPremiumOnly { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs deleted file mode 100644 index b35065d9..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Channel/AiModelUpdateInput.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Yi.Framework.AiHub.Domain.Shared.Enums; - -namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -/// -/// 更新AI模型输入 -/// -public class AiModelUpdateInput -{ - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - public Guid Id { get; set; } - - /// - /// 处理名 - /// - [Required(ErrorMessage = "处理名不能为空")] - [StringLength(100, ErrorMessage = "处理名不能超过100个字符")] - public string HandlerName { get; set; } - - /// - /// 模型ID - /// - [Required(ErrorMessage = "模型ID不能为空")] - [StringLength(200, ErrorMessage = "模型ID不能超过200个字符")] - public string ModelId { get; set; } - - /// - /// 模型名称 - /// - [Required(ErrorMessage = "模型名称不能为空")] - [StringLength(200, ErrorMessage = "模型名称不能超过200个字符")] - public string Name { get; set; } - - /// - /// 模型描述 - /// - [StringLength(1000, ErrorMessage = "模型描述不能超过1000个字符")] - public string? Description { get; set; } - - /// - /// 排序 - /// - [Range(0, int.MaxValue, ErrorMessage = "排序必须大于等于0")] - public int OrderNum { get; set; } - - /// - /// AI应用ID - /// - [Required(ErrorMessage = "AI应用ID不能为空")] - public Guid AiAppId { get; set; } - - /// - /// 额外信息 - /// - [StringLength(2000, ErrorMessage = "额外信息不能超过2000个字符")] - public string? ExtraInfo { get; set; } - - /// - /// 模型类型 - /// - [Required(ErrorMessage = "模型类型不能为空")] - public ModelTypeEnum ModelType { get; set; } - - /// - /// 模型API类型 - /// - [Required(ErrorMessage = "模型API类型不能为空")] - public ModelApiTypeEnum ModelApiType { get; set; } - - /// - /// 模型倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型倍率必须大于0")] - public decimal Multiplier { get; set; } - - /// - /// 模型显示倍率 - /// - [Range(0.01, double.MaxValue, ErrorMessage = "模型显示倍率必须大于0")] - public decimal MultiplierShow { get; set; } - - /// - /// 供应商分组名称 - /// - [StringLength(100, ErrorMessage = "供应商分组名称不能超过100个字符")] - public string? ProviderName { get; set; } - - /// - /// 模型图标URL - /// - [StringLength(500, ErrorMessage = "模型图标URL不能超过500个字符")] - public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs deleted file mode 100644 index d45b542e..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/IChannelService.cs +++ /dev/null @@ -1,86 +0,0 @@ -using Volo.Abp.Application.Dtos; -using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; - -namespace Yi.Framework.AiHub.Application.Contracts.IServices; - -/// -/// 渠道商管理服务接口 -/// -public interface IChannelService -{ - #region AI应用管理 - - /// - /// 获取AI应用列表 - /// - /// 查询参数 - /// 分页应用列表 - Task> GetAppListAsync(AiAppGetListInput input); - - /// - /// 根据ID获取AI应用 - /// - /// 应用ID - /// 应用详情 - Task GetAppByIdAsync(Guid id); - - /// - /// 创建AI应用 - /// - /// 创建输入 - /// 创建的应用 - Task CreateAppAsync(AiAppCreateInput input); - - /// - /// 更新AI应用 - /// - /// 更新输入 - /// 更新后的应用 - Task UpdateAppAsync(AiAppUpdateInput input); - - /// - /// 删除AI应用 - /// - /// 应用ID - Task DeleteAppAsync(Guid id); - - #endregion - - #region AI模型管理 - - /// - /// 获取AI模型列表 - /// - /// 查询参数 - /// 分页模型列表 - Task> GetModelListAsync(AiModelGetListInput input); - - /// - /// 根据ID获取AI模型 - /// - /// 模型ID - /// 模型详情 - Task GetModelByIdAsync(Guid id); - - /// - /// 创建AI模型 - /// - /// 创建输入 - /// 创建的模型 - Task CreateModelAsync(AiModelCreateInput input); - - /// - /// 更新AI模型 - /// - /// 更新输入 - /// 更新后的模型 - Task UpdateModelAsync(AiModelUpdateInput input); - - /// - /// 删除AI模型(软删除) - /// - /// 模型ID - Task DeleteModelAsync(Guid id); - - #endregion -} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs index a7e65a0a..013a82f4 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Activities/DailyTaskService.cs @@ -179,16 +179,10 @@ public class DailyTaskService : ApplicationService var tomorrow = today.AddDays(1); // 查询今日所有使用尊享包模型的消息(role=system 表示消耗) - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); - var totalTokens = await _messageRepository._DbQueryable .Where(x => x.UserId == userId) .Where(x => x.Role == "system") // system角色表示实际消耗 - .Where(x => premiumModelIds.Contains(x.ModelId)) // 尊享包模型 + .Where(x => PremiumPackageConst.ModeIds.Contains(x.ModelId)) // 尊享包模型 .Where(x => x.CreationTime >= today && x.CreationTime < tomorrow) .SumAsync(x => x.TokenUsage.TotalTokenCount); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs deleted file mode 100644 index 7e7a2f9b..00000000 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs +++ /dev/null @@ -1,233 +0,0 @@ -using Mapster; -using Microsoft.AspNetCore.Authorization; -using SqlSugar; -using Volo.Abp.Application.Dtos; -using Volo.Abp.Application.Services; -using Yi.Framework.AiHub.Application.Contracts.Dtos.Channel; -using Yi.Framework.AiHub.Application.Contracts.IServices; -using Yi.Framework.AiHub.Domain.Entities.Model; -using Yi.Framework.SqlSugarCore.Abstractions; - -namespace Yi.Framework.AiHub.Application.Services; - -/// -/// 渠道商管理服务实现 -/// -[Authorize] -public class ChannelService : ApplicationService, IChannelService -{ - private readonly ISqlSugarRepository _appRepository; - private readonly ISqlSugarRepository _modelRepository; - - public ChannelService( - ISqlSugarRepository appRepository, - ISqlSugarRepository modelRepository) - { - _appRepository = appRepository; - _modelRepository = modelRepository; - } - - #region AI应用管理 - - /// - /// 获取AI应用列表 - /// - public async Task> GetAppListAsync(AiAppGetListInput input) - { - RefAsync total = 0; - - var entities = await _appRepository._DbQueryable - .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => x.Name.Contains(input.SearchKey)) - .OrderBy(x => x.OrderNum) - .OrderByDescending(x => x.CreationTime) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - - var output = entities.Adapt>(); - return new PagedResultDto(total, output); - } - - /// - /// 根据ID获取AI应用 - /// - public async Task GetAppByIdAsync(Guid id) - { - var entity = await _appRepository.GetByIdAsync(id); - return entity.Adapt(); - } - - /// - /// 创建AI应用 - /// - public async Task CreateAppAsync(AiAppCreateInput input) - { - var entity = new AiAppAggregateRoot - { - Name = input.Name, - Endpoint = input.Endpoint, - ExtraUrl = input.ExtraUrl, - ApiKey = input.ApiKey, - OrderNum = input.OrderNum - }; - - await _appRepository.InsertAsync(entity); - return entity.Adapt(); - } - - /// - /// 更新AI应用 - /// - public async Task UpdateAppAsync(AiAppUpdateInput input) - { - var entity = await _appRepository.GetByIdAsync(input.Id); - - entity.Name = input.Name; - entity.Endpoint = input.Endpoint; - entity.ExtraUrl = input.ExtraUrl; - entity.ApiKey = input.ApiKey; - entity.OrderNum = input.OrderNum; - - await _appRepository.UpdateAsync(entity); - return entity.Adapt(); - } - - /// - /// 删除AI应用 - /// - public async Task DeleteAppAsync(Guid id) - { - // 检查是否有关联的模型 - var hasModels = await _modelRepository._DbQueryable - .Where(x => x.AiAppId == id && !x.IsDeleted) - .AnyAsync(); - - if (hasModels) - { - throw new Volo.Abp.UserFriendlyException("该应用下存在模型,无法删除"); - } - - await _appRepository.DeleteAsync(id); - } - - #endregion - - #region AI模型管理 - - /// - /// 获取AI模型列表 - /// - public async Task> GetModelListAsync(AiModelGetListInput input) - { - RefAsync total = 0; - - var query = _modelRepository._DbQueryable - .Where(x => !x.IsDeleted) - .WhereIF(!string.IsNullOrWhiteSpace(input.SearchKey), x => - x.Name.Contains(input.SearchKey) || x.ModelId.Contains(input.SearchKey)) - .WhereIF(input.AiAppId.HasValue, x => x.AiAppId == input.AiAppId.Value) - .WhereIF(input.IsPremiumOnly == true, x => x.IsPremium); - - var entities = await query - .OrderBy(x => x.OrderNum) - .OrderByDescending(x => x.Id) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - - var output = entities.Adapt>(); - return new PagedResultDto(total, output); - } - - /// - /// 根据ID获取AI模型 - /// - public async Task GetModelByIdAsync(Guid id) - { - var entity = await _modelRepository.GetByIdAsync(id); - return entity.Adapt(); - } - - /// - /// 创建AI模型 - /// - public async Task CreateModelAsync(AiModelCreateInput input) - { - // 验证应用是否存在 - var appExists = await _appRepository._DbQueryable - .Where(x => x.Id == input.AiAppId) - .AnyAsync(); - - if (!appExists) - { - throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在"); - } - - var entity = new AiModelEntity - { - HandlerName = input.HandlerName, - ModelId = input.ModelId, - Name = input.Name, - Description = input.Description, - OrderNum = input.OrderNum, - AiAppId = input.AiAppId, - ExtraInfo = input.ExtraInfo, - ModelType = input.ModelType, - ModelApiType = input.ModelApiType, - Multiplier = input.Multiplier, - MultiplierShow = input.MultiplierShow, - ProviderName = input.ProviderName, - IconUrl = input.IconUrl, - IsPremium = input.IsPremium, - IsDeleted = false - }; - - await _modelRepository.InsertAsync(entity); - return entity.Adapt(); - } - - /// - /// 更新AI模型 - /// - public async Task UpdateModelAsync(AiModelUpdateInput input) - { - var entity = await _modelRepository.GetByIdAsync(input.Id); - - // 验证应用是否存在 - if (entity.AiAppId != input.AiAppId) - { - var appExists = await _appRepository._DbQueryable - .Where(x => x.Id == input.AiAppId) - .AnyAsync(); - - if (!appExists) - { - throw new Volo.Abp.UserFriendlyException("指定的AI应用不存在"); - } - } - - entity.HandlerName = input.HandlerName; - entity.ModelId = input.ModelId; - entity.Name = input.Name; - entity.Description = input.Description; - entity.OrderNum = input.OrderNum; - entity.AiAppId = input.AiAppId; - entity.ExtraInfo = input.ExtraInfo; - entity.ModelType = input.ModelType; - entity.ModelApiType = input.ModelApiType; - entity.Multiplier = input.Multiplier; - entity.MultiplierShow = input.MultiplierShow; - entity.ProviderName = input.ProviderName; - entity.IconUrl = input.IconUrl; - entity.IsPremium = input.IsPremium; - - await _modelRepository.UpdateAsync(entity); - return entity.Adapt(); - } - - /// - /// 删除AI模型(软删除) - /// - public async Task DeleteModelAsync(Guid id) - { - await _modelRepository.DeleteByIdAsync(id); - } - - #endregion -} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs index 42f0b1a5..341638ce 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiChatService.cs @@ -109,7 +109,7 @@ public class AiChatService : ApplicationService ApiHost = null, ApiKey = null, Remark = x.Description, - IsPremiumPackage = x.IsPremium + IsPremiumPackage = PremiumPackageConst.ModeIds.Contains(x.ModelId) }).ToListAsync(); return output; } @@ -144,21 +144,13 @@ public class AiChatService : ApplicationService } //如果是尊享包服务,需要校验是是否尊享包足够 - if (CurrentUser.IsAuthenticated) + if (CurrentUser.IsAuthenticated && PremiumPackageConst.ModeIds.Contains(input.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + // 检查尊享token包用量 + var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId()); + if (availableTokens <= 0) { - // 检查尊享token包用量 - var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(CurrentUser.GetId()); - if (availableTokens <= 0) - { - throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); - } + throw new UserFriendlyException("尊享token包用量不足,请先购买尊享token包"); } } @@ -227,12 +219,7 @@ public class AiChatService : ApplicationService } //如果是尊享包服务,需要校验是是否尊享包足够 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.ModelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) { // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(tokenValidation.UserId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index caa7313d..5bffa03d 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -69,9 +69,6 @@ public class AiImageService : ApplicationService } // 尊享包校验 - // 注意: AiImageService目前没有注入AiModelEntity仓储 - // 由于图片生成功能使用频率较低,且当前所有图片模型都不是尊享模型 - // 暂时保留原逻辑,等待后续重构时再注入仓储 if (PremiumPackageConst.ModeIds.Contains(input.ModelId)) { var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs index 0eee3438..98faf61c 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/ModelService.cs @@ -41,7 +41,8 @@ public class ModelService : ApplicationService, IModelService input.ModelTypes.Contains(x.ModelType)) .WhereIF(input.ModelApiTypes is not null, x => input.ModelApiTypes.Contains(x.ModelApiType)) - .WhereIF(input.IsPremiumOnly == true, x => x.IsPremium) + .WhereIF(input.IsPremiumOnly == true, x => + PremiumPackageConst.ModeIds.Contains(x.ModelId)) .GroupBy(x => x.ModelId) .Select(x => x.ModelId) .ToPageListAsync(input.SkipCount, input.MaxResultCount, total)); @@ -60,7 +61,7 @@ public class ModelService : ApplicationService, IModelService MultiplierShow = x.First().MultiplierShow, ProviderName = x.First().ProviderName, IconUrl = x.First().IconUrl, - IsPremium = x.First().IsPremium, + IsPremium = PremiumPackageConst.ModeIds.Contains(x.First().ModelId), OrderNum = x.First().OrderNum }).ToList(); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs index d18de550..23e7e666 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/TokenService.cs @@ -52,10 +52,7 @@ public class TokenService : ApplicationService } // 获取尊享包模型ID列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; // 批量查询所有Token的尊享包已使用额度 var tokenIds = tokens.Select(t => t.Id).ToList(); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs index eef91f3b..ccb05089 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/OpenApiService.cs @@ -65,12 +65,7 @@ public class OpenApiService : ApplicationService await _aiBlacklistManager.VerifiyAiBlacklist(userId); //如果是尊享包服务,需要校验是是否尊享包足够 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == input.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(input.Model)) { // 检查尊享token包用量 var availableTokens = await _premiumPackageManager.GetAvailableTokensAsync(userId); diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs index f8c0b3a4..bffce1e6 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/UsageStatisticsService.cs @@ -181,12 +181,7 @@ public class UsageStatisticsService : ApplicationService, IUsageStatisticsServic public async Task> GetPremiumTokenUsageByTokenAsync() { var userId = CurrentUser.GetId(); - - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; // 从UsageStatistics表获取尊享模型的token消耗统计(按TokenId聚合) var tokenUsages = await _usageStatisticsRepository._DbQueryable diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs index ef0f1ebd..991d0ff2 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Model/AiModelEntity.cs @@ -80,9 +80,4 @@ public class AiModelEntity : Entity, IOrderNum, ISoftDelete /// 模型图标URL ///
public string? IconUrl { get; set; } - - /// - /// 是否为尊享模型 - /// - public bool IsPremium { get; set; } } \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs index 34cf3486..971fef75 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/AiGateWayManager.cs @@ -150,12 +150,7 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage, tokenId); // 扣减尊享token包用量 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(request.Model)) { var totalTokens = data.Usage?.TotalTokens ?? 0; if (totalTokens > 0) @@ -305,20 +300,12 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, request.Model, tokenUsage, tokenId); // 扣减尊享token包用量 - if (userId is not null) + if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var totalTokens = tokenUsage.TotalTokens ?? 0; + if (totalTokens > 0) { - var totalTokens = tokenUsage.TotalTokens ?? 0; - if (totalTokens > 0) - { - await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); - } + await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); } } } @@ -376,20 +363,12 @@ public class AiGateWayManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, model, response.Usage, tokenId); // 扣减尊享token包用量 - if (userId is not null) + if (userId is not null && PremiumPackageConst.ModeIds.Contains(request.Model)) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == request.Model) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var totalTokens = response.Usage.TotalTokens ?? 0; + if (totalTokens > 0) { - var totalTokens = response.Usage.TotalTokens ?? 0; - if (totalTokens > 0) - { - await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); - } + await PremiumPackageManager.TryConsumeTokensAsync(userId.Value, totalTokens); } } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs index 5e411fbb..b4d9110c 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/ChatManager.cs @@ -15,7 +15,6 @@ using Volo.Abp.Domain.Services; using Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; using Yi.Framework.AiHub.Domain.AiGateWay; using Yi.Framework.AiHub.Domain.Entities.Chat; -using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Shared.Attributes; using Yi.Framework.AiHub.Domain.Shared.Consts; @@ -35,13 +34,12 @@ public class ChatManager : DomainService private readonly UsageStatisticsManager _usageStatisticsManager; private readonly PremiumPackageManager _premiumPackageManager; private readonly AiGateWayManager _aiGateWayManager; - private readonly ISqlSugarRepository _aiModelRepository; public ChatManager(ILoggerFactory loggerFactory, ISqlSugarRepository messageRepository, ISqlSugarRepository agentStoreRepository, AiMessageManager aiMessageManager, UsageStatisticsManager usageStatisticsManager, PremiumPackageManager premiumPackageManager, - AiGateWayManager aiGateWayManager, ISqlSugarRepository aiModelRepository) + AiGateWayManager aiGateWayManager) { _loggerFactory = loggerFactory; _messageRepository = messageRepository; @@ -50,7 +48,6 @@ public class ChatManager : DomainService _usageStatisticsManager = usageStatisticsManager; _premiumPackageManager = premiumPackageManager; _aiGateWayManager = aiGateWayManager; - _aiModelRepository = aiModelRepository; } /// @@ -210,12 +207,7 @@ public class ChatManager : DomainService await _usageStatisticsManager.SetUsageAsync(userId, modelId, usage, tokenId); //扣减尊享token包用量 - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == modelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + if (PremiumPackageConst.ModeIds.Contains(modelId)) { var totalTokens = usage?.TotalTokens ?? 0; if (totalTokens > 0) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs index 28595a48..dd2ae774 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/TokenManager.cs @@ -1,7 +1,6 @@ using SqlSugar; using Volo.Abp.Domain.Services; using Yi.Framework.AiHub.Domain.Entities; -using Yi.Framework.AiHub.Domain.Entities.Model; using Yi.Framework.AiHub.Domain.Entities.OpenApi; using Yi.Framework.AiHub.Domain.Shared.Consts; using Yi.Framework.SqlSugarCore.Abstractions; @@ -28,16 +27,13 @@ public class TokenManager : DomainService { private readonly ISqlSugarRepository _tokenRepository; private readonly ISqlSugarRepository _usageStatisticsRepository; - private readonly ISqlSugarRepository _aiModelRepository; public TokenManager( ISqlSugarRepository tokenRepository, - ISqlSugarRepository usageStatisticsRepository, - ISqlSugarRepository aiModelRepository) + ISqlSugarRepository usageStatisticsRepository) { _tokenRepository = tokenRepository; _usageStatisticsRepository = usageStatisticsRepository; - _aiModelRepository = aiModelRepository; } /// @@ -80,20 +76,14 @@ public class TokenManager : DomainService } // 如果是尊享模型且Token设置了额度限制,检查是否超限 - if (!string.IsNullOrEmpty(modelId) && entity.PremiumQuotaLimit.HasValue) + if (!string.IsNullOrEmpty(modelId) && + PremiumPackageConst.ModeIds.Contains(modelId) && + entity.PremiumQuotaLimit.HasValue) { - var isPremium = await _aiModelRepository._DbQueryable - .Where(x => x.ModelId == modelId) - .Select(x => x.IsPremium) - .FirstAsync(); - - if (isPremium) + var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id); + if (usedQuota >= entity.PremiumQuotaLimit.Value) { - var usedQuota = await GetTokenPremiumUsedQuotaAsync(entity.UserId, entity.Id); - if (usedQuota >= entity.PremiumQuotaLimit.Value) - { - throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403"); - } + throw new UserFriendlyException($"当前Token的尊享包额度已用完(已使用:{usedQuota},限制:{entity.PremiumQuotaLimit.Value}),请调整额度限制或使用其他Token", "403"); } } @@ -109,11 +99,7 @@ public class TokenManager : DomainService /// private async Task GetTokenPremiumUsedQuotaAsync(Guid userId, Guid tokenId) { - // 先获取所有尊享模型的ModelId列表 - var premiumModelIds = await _aiModelRepository._DbQueryable - .Where(x => x.IsPremium) - .Select(x => x.ModelId) - .ToListAsync(); + var premiumModelIds = PremiumPackageConst.ModeIds; var usedQuota = await _usageStatisticsRepository._DbQueryable .Where(x => x.UserId == userId && x.TokenId == tokenId && premiumModelIds.Contains(x.ModelId)) diff --git a/Yi.Ai.Vue3/src/api/channel/index.ts b/Yi.Ai.Vue3/src/api/channel/index.ts deleted file mode 100644 index cd31154f..00000000 --- a/Yi.Ai.Vue3/src/api/channel/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { del, get, post, put } from '@/utils/request'; -import type { - AiAppDto, - AiAppCreateInput, - AiAppUpdateInput, - AiAppGetListInput, - AiModelDto, - AiModelCreateInput, - AiModelUpdateInput, - AiModelGetListInput, - PagedResultDto, -} from './types'; - -// ==================== AI应用管理 ==================== - -// 获取AI应用列表 -export function getAppList(params?: AiAppGetListInput) { - const queryParams = new URLSearchParams(); - if (params?.searchKey) { - queryParams.append('SearchKey', params.searchKey); - } - if (params?.skipCount !== undefined) { - queryParams.append('SkipCount', params.skipCount.toString()); - } - if (params?.maxResultCount !== undefined) { - queryParams.append('MaxResultCount', params.maxResultCount.toString()); - } - - const queryString = queryParams.toString(); - const url = queryString ? `/channel/app?${queryString}` : '/channel/app'; - - return get>(url).json(); -} - -// 根据ID获取AI应用 -export function getAppById(id: string) { - return get(`/channel/app/${id}`).json(); -} - -// 创建AI应用 -export function createApp(data: AiAppCreateInput) { - return post('/channel/app', data).json(); -} - -// 更新AI应用 -export function updateApp(data: AiAppUpdateInput) { - return put('/channel/app', data).json(); -} - -// 删除AI应用 -export function deleteApp(id: string) { - return del(`/channel/app/${id}`).json(); -} - -// ==================== AI模型管理 ==================== - -// 获取AI模型列表 -export function getModelList(params?: AiModelGetListInput) { - const queryParams = new URLSearchParams(); - if (params?.searchKey) { - queryParams.append('SearchKey', params.searchKey); - } - if (params?.aiAppId) { - queryParams.append('AiAppId', params.aiAppId); - } - if (params?.isPremiumOnly !== undefined) { - queryParams.append('IsPremiumOnly', params.isPremiumOnly.toString()); - } - if (params?.skipCount !== undefined) { - queryParams.append('SkipCount', params.skipCount.toString()); - } - if (params?.maxResultCount !== undefined) { - queryParams.append('MaxResultCount', params.maxResultCount.toString()); - } - - const queryString = queryParams.toString(); - const url = queryString ? `/channel/model?${queryString}` : '/channel/model'; - - return get>(url).json(); -} - -// 根据ID获取AI模型 -export function getModelById(id: string) { - return get(`/channel/model/${id}`).json(); -} - -// 创建AI模型 -export function createModel(data: AiModelCreateInput) { - return post('/channel/model', data).json(); -} - -// 更新AI模型 -export function updateModel(data: AiModelUpdateInput) { - return put('/channel/model', data).json(); -} - -// 删除AI模型 -export function deleteModel(id: string) { - return del(`/channel/model/${id}`).json(); -} diff --git a/Yi.Ai.Vue3/src/api/channel/types.ts b/Yi.Ai.Vue3/src/api/channel/types.ts deleted file mode 100644 index 76a6306a..00000000 --- a/Yi.Ai.Vue3/src/api/channel/types.ts +++ /dev/null @@ -1,121 +0,0 @@ -// 模型类型枚举 -export enum ModelTypeEnum { - Chat = 0, - Image = 1, - Embedding = 2, - PremiumChat = 3, -} - -// 模型API类型枚举 -export enum ModelApiTypeEnum { - OpenAi = 0, - Claude = 1, -} - -// AI应用DTO -export interface AiAppDto { - id: string; - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; - creationTime: string; -} - -// 创建AI应用输入 -export interface AiAppCreateInput { - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; -} - -// 更新AI应用输入 -export interface AiAppUpdateInput { - id: string; - name: string; - endpoint: string; - extraUrl?: string; - apiKey: string; - orderNum: number; -} - -// 获取AI应用列表输入 -export interface AiAppGetListInput { - searchKey?: string; - skipCount?: number; - maxResultCount?: number; -} - -// AI模型DTO -export interface AiModelDto { - id: string; - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 创建AI模型输入 -export interface AiModelCreateInput { - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 更新AI模型输入 -export interface AiModelUpdateInput { - id: string; - handlerName: string; - modelId: string; - name: string; - description?: string; - orderNum: number; - aiAppId: string; - extraInfo?: string; - modelType: ModelTypeEnum; - modelApiType: ModelApiTypeEnum; - multiplier: number; - multiplierShow: number; - providerName?: string; - iconUrl?: string; - isPremium: boolean; -} - -// 获取AI模型列表输入 -export interface AiModelGetListInput { - searchKey?: string; - aiAppId?: string; - isPremiumOnly?: boolean; - skipCount?: number; - maxResultCount?: number; -} - -// 分页结果 -export interface PagedResultDto { - items: T[]; - totalCount: number; -} diff --git a/Yi.Ai.Vue3/src/pages/console/channel/index.vue b/Yi.Ai.Vue3/src/pages/console/channel/index.vue deleted file mode 100644 index c780fe70..00000000 --- a/Yi.Ai.Vue3/src/pages/console/channel/index.vue +++ /dev/null @@ -1,548 +0,0 @@ - - - - - diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 15ae21dd..44aece8f 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -19,7 +19,6 @@ const navItems = [ { name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' }, { name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' }, { name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' }, - { name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index 496bb214..b94431ec 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -207,14 +207,6 @@ export const layoutRouter: RouteRecordRaw[] = [ title: '激活码兑换', }, }, - { - path: 'channel', - name: 'consoleChannel', - component: () => import('@/pages/console/channel/index.vue'), - meta: { - title: '渠道商管理', - }, - }, ], }, ], diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 16d28441..9ef439fb 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,8 +25,6 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] - ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index 8f2a798b..c98d612e 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -7,6 +7,7 @@ interface ImportMetaEnv { readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; readonly VITE_FILE_UPLOAD_API: string; + readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From b8c0f9a212a00744b62d484c08cc26187a0def02 Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Wed, 31 Dec 2025 00:59:06 +0800 Subject: [PATCH 11/43] =?UTF-8?q?fix:=20=E5=89=8D=E7=AB=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=9E=B6=E6=9E=84=E9=87=8D=E6=9E=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layouts/components/ChatAside/index.vue | 138 +----------------- .../Header/components/Collapse.vue | 38 ++--- .../Header/components/CreateChat.vue | 2 + .../pages/chat/layouts/chatDefaul/index.vue | 54 ++++++- .../pages/chat/layouts/chatWithId/index.vue | 62 ++++++-- Yi.Ai.Vue3/src/stores/modules/session.ts | 2 +- 6 files changed, 125 insertions(+), 171 deletions(-) diff --git a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue index c0e8710d..90bc5731 100644 --- a/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/ChatAside/index.vue @@ -124,141 +124,7 @@ function handleMenuCommand(command: string, item: ConversationItem) { - event.preventDefault(); - // 在折叠状态下触发删除确认 - ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - confirmButtonClass: 'el-button--danger', - cancelButtonClass: 'el-button--info', - roundButton: true, - autofocus: false, - }) - .then(() => { - sessionStore.deleteSessions([item.id!]); - nextTick(() => { - if (item.id === active.value) { - sessionStore.createSessionBtn(); - } - }); - }) - .catch(() => { - // 取消删除 - }); -} - -// 折叠状态下点击更多按钮 -function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem) { - event.stopPropagation(); - - // 创建一个简单的菜单 - ElMessageBox({ - title: '对话操作', - message: ` -
- - -
- `, - showConfirmButton: false, - showCancelButton: false, - dangerouslyUseHTMLString: true, - customClass: 'collapsed-menu-dialog', - closeOnClickModal: true, - closeOnPressEscape: true, - }).then(() => { - // 对话框关闭 - }).catch(() => { - // 对话框关闭 - }); - - // 添加菜单项点击事件 - nextTick(() => { - const menuItems = document.querySelectorAll('.menu-item'); - menuItems.forEach((itemEl) => { - itemEl.addEventListener('click', (e) => { - const action = (e.currentTarget as HTMLElement).dataset.action; - if (action === 'delete') { - ElMessageBox.confirm('删除后,聊天记录将不可恢复。', '确定删除对话?', { - confirmButtonText: '确定', - cancelButtonText: '取消', - type: 'warning', - confirmButtonClass: 'el-button--danger', - cancelButtonClass: 'el-button--info', - roundButton: true, - autofocus: false, - }) - .then(() => { - sessionStore.deleteSessions([item.id!]); - nextTick(() => { - if (item.id === active.value) { - sessionStore.createSessionBtn(); - } - }); - }) - .catch(() => { - // 取消删除 - }); - } - else if (action === 'rename') { - ElMessageBox.prompt('', '编辑对话名称', { - confirmButtonText: '确定', - cancelButtonText: '取消', - inputErrorMessage: '请输入对话名称', - confirmButtonClass: 'el-button--primary', - cancelButtonClass: 'el-button--info', - roundButton: true, - inputValue: item.sessionTitle, - autofocus: false, - inputValidator: (value) => { - return !!value; - }, - }).then(({ value }) => { - sessionStore - .updateSession({ - id: item.id!, - sessionTitle: value, - sessionContent: item.sessionContent, - }) - .then(() => { - ElMessage({ - type: 'success', - message: '修改成功', - }); - nextTick(() => { - if (sessionStore.currentSession?.id === item.id) { - sessionStore.setCurrentSession({ - ...item, - sessionTitle: value, - }); - } - }); - }); - }); - } - - // 关闭菜单对话框 - document.querySelector('.collapsed-menu-dialog .el-message-box__headerbtn')?.dispatchEvent(new Event('click')); - }); - }); - }); + designStore.setIsCollapseConversationList(!designStore.isCollapseConversationList); } @@ -446,7 +312,7 @@ function handleCollapsedMenuClick(event: MouseEvent, item: ConversationItem diff --git a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue index 5ec82222..9553e1bf 100644 --- a/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue +++ b/Yi.Ai.Vue3/src/layouts/components0/Header/components/CreateChat.vue @@ -1,5 +1,7 @@ diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue index c459944a..c33f4549 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue @@ -1,8 +1,8 @@ @@ -86,15 +93,21 @@ function getProgressColor(task: DailyTaskItem): string {

每日任务

-

完成每日任务,领取额外尊享包 Token 奖励,可累加重复

+

+ 完成每日任务,领取额外尊享包 Token 奖励,可累加重复 +

-
🔥
+
+ 🔥 +
-
今日尊享包消耗
+
+ 今日尊享包消耗 +
{{ formatTokenDisplay(taskData.todayConsumedTokens) }} Tokens
@@ -109,7 +122,7 @@ function getProgressColor(task: DailyTaskItem): string { class="task-item" :class="{ 'task-completed': task.status === 2, - 'task-claimable': task.status === 1 + 'task-claimable': task.status === 1, }" >
@@ -187,7 +200,6 @@ function getProgressColor(task: DailyTaskItem): string { diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue index 0b98e38e..a28f0640 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -1,23 +1,19 @@ diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index 1db0ae27..78f16686 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -1,131 +1,3 @@ - - - + From ea403fcae061ea5159d3355c37769c75bcb74713 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 01:12:47 +0800 Subject: [PATCH 24/43] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BF=A1=E6=81=AF=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Chat/ImageTaskOutput.cs | 5 +++++ .../Services/Chat/AiImageService.cs | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs index 0f2e5a6f..8ed9083b 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs @@ -56,4 +56,9 @@ public class ImageTaskOutput /// 创建时间 ///
public DateTime CreationTime { get; set; } + + /// + /// 错误信息 + /// + public string? ErrorInfo { get; set; } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index 0297e1c0..f9ae39c0 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -36,6 +36,7 @@ public class AiImageService : ApplicationService private readonly IWebHostEnvironment _webHostEnvironment; private readonly TokenManager _tokenManager; private readonly ISqlSugarRepository _aiModelRepository; + public AiImageService( ISqlSugarRepository imageTaskRepository, IBackgroundJobManager backgroundJobManager, @@ -43,7 +44,8 @@ public class AiImageService : ApplicationService PremiumPackageManager premiumPackageManager, ModelManager modelManager, IGuidGenerator guidGenerator, - IWebHostEnvironment webHostEnvironment, TokenManager tokenManager, ISqlSugarRepository aiModelRepository) + IWebHostEnvironment webHostEnvironment, TokenManager tokenManager, + ISqlSugarRepository aiModelRepository) { _imageTaskRepository = imageTaskRepository; _backgroundJobManager = backgroundJobManager; @@ -66,7 +68,7 @@ public class AiImageService : ApplicationService public async Task GenerateAsync([FromBody] ImageGenerationInput input) { var userId = CurrentUser.GetId(); - + // 黑名单校验 await _aiBlacklistManager.VerifiyAiBlacklist(userId); @@ -76,7 +78,7 @@ public class AiImageService : ApplicationService await _tokenManager.ValidateTokenAsync(input.TokenId, input.ModelId); } - + // VIP校验 if (!CurrentUser.IsAiVip()) { @@ -174,6 +176,7 @@ public class AiImageService : ApplicationService { mimeType = header.Split(':')[1].Split(';')[0]; } + base64Content = parts[1]; } } @@ -253,7 +256,10 @@ public class AiImageService : ApplicationService // StoreBase64 = x.StoreBase64, StoreUrl = x.StoreUrl, TaskStatus = x.TaskStatus, - CreationTime = x.CreationTime + PublishStatus = x.PublishStatus, + Categories = x.Categories, + CreationTime = x.CreationTime, + ErrorInfo = x.ErrorInfo }) .ToListAsync(); @@ -322,7 +328,7 @@ public class AiImageService : ApplicationService await _imageTaskRepository.UpdateAsync(task); } - + /// /// 获取图片模型列表 /// @@ -368,4 +374,4 @@ public class PagedResult Total = total; Items = items; } -} +} \ No newline at end of file From 9d1dd725844dc9ff8b14d7dcc6ab4eac466cfd27 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 01:29:47 +0800 Subject: [PATCH 25/43] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E6=9D=A1=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Ai.Vue3/src/layouts/index.vue | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Yi.Ai.Vue3/src/layouts/index.vue b/Yi.Ai.Vue3/src/layouts/index.vue index 3943ed4a..c37bc699 100644 --- a/Yi.Ai.Vue3/src/layouts/index.vue +++ b/Yi.Ai.Vue3/src/layouts/index.vue @@ -68,7 +68,22 @@ onMounted(() => { From e39cbaf5e74f66a4b42520d1e37513667046254d Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 01:45:27 +0800 Subject: [PATCH 26/43] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E7=A9=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Chat/AiImageService.cs | 3 ++- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index f9ae39c0..4e4c44a9 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -334,11 +334,12 @@ public class AiImageService : ApplicationService /// /// [HttpPost("ai-image/model")] + [AllowAnonymous] public async Task> GetModelAsync() { var output = await _aiModelRepository._DbQueryable .Where(x => x.ModelType == ModelTypeEnum.Image) - .Where(x => x.ModelApiType == ModelApiTypeEnum.OpenAi) + .Where(x => x.ModelApiType == ModelApiTypeEnum.GenerateContent) .OrderByDescending(x => x.OrderNum) .Select(x => new ModelGetListOutput { diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 9f21193b..436d8c92 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -113,7 +113,7 @@ namespace Yi.Abp.Web //本地开发环境,可以禁用作业执行 if (host.IsDevelopment()) { - //Configure(options => { options.IsEnabled = false; }); + Configure(options => { options.IsEnabled = false; }); } //请求日志 From 28cdc293691ac78771fdfc2ea329214826a5ca8c Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 01:46:40 +0800 Subject: [PATCH 27/43] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=A8=A1=E5=9E=8B=E4=BC=9A=E5=91=98=E6=A0=87=E8=AF=86?= =?UTF-8?q?=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 IsPremiumPackage 的判断从使用 PremiumPackageConst.ModeIds 改为直接读取模型的 IsPremium 属性,避免因配置不一致导致会员标识错误。 --- .../Services/Chat/AiImageService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index 4e4c44a9..fc1487e9 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -348,7 +348,7 @@ public class AiImageService : ApplicationService ModelName = x.Name, ModelDescribe = x.Description, Remark = x.Description, - IsPremiumPackage = PremiumPackageConst.ModeIds.Contains(x.ModelId) + IsPremiumPackage = x.IsPremium }).ToListAsync(); return output; } From a50f877964c076a27e91aaa15ce468a877123950 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 01:54:22 +0800 Subject: [PATCH 28/43] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E5=8F=B0=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Ai.Vue3/src/pages/console/index.vue | 2 +- Yi.Ai.Vue3/types/components.d.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index 6d70249d..b7f7fa4d 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -224,7 +224,7 @@ window.addEventListener('resize', checkIsMobile); .content-main { flex: 1; - padding: 20px; + padding: 10px; overflow-y: auto; min-width: 0; transition: margin-left 0.3s ease; diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 9ef439fb..16d28441 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -25,6 +25,8 @@ declare module 'vue' { ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] From c4ab17608951706cf2456d69b1fecc7b801c7239 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 02:15:28 +0800 Subject: [PATCH 29/43] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=E6=95=B4?= =?UTF-8?q?=E4=BD=93title=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ChannelService.cs | 2 +- .../src/layouts/components/Header/index.vue | 7 ++- Yi.Ai.Vue3/src/pages/chat/agent/index.vue | 26 +++++++++++ Yi.Ai.Vue3/src/pages/chat/index.vue | 7 +-- Yi.Ai.Vue3/src/pages/modelLibrary/index.vue | 2 +- .../src/routers/modules/staticRouter.ts | 46 +++++++++++-------- 6 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 Yi.Ai.Vue3/src/pages/chat/agent/index.vue diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs index 66f1bc0e..750603d4 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/ChannelService.cs @@ -14,7 +14,7 @@ namespace Yi.Framework.AiHub.Application.Services; /// /// 渠道商管理服务实现 /// -[Authorize] +[Authorize(Roles = "admin")] public class ChannelService : ApplicationService, IChannelService { private readonly ISqlSugarRepository _appRepository; diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index 78f16686..54b4af58 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -76,10 +76,13 @@ function handleConsoleClick(e: MouseEvent) { AI对话 - 图片生成 + AI图片 - 视频生成 + AI视频 + + + AI智能体 diff --git a/Yi.Ai.Vue3/src/pages/chat/agent/index.vue b/Yi.Ai.Vue3/src/pages/chat/agent/index.vue new file mode 100644 index 00000000..a465c672 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/agent/index.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/index.vue b/Yi.Ai.Vue3/src/pages/chat/index.vue index e605ff25..086cfe43 100644 --- a/Yi.Ai.Vue3/src/pages/chat/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/index.vue @@ -11,9 +11,10 @@ const isCollapsed = ref(true); // 菜单项配置 const navItems = [ - { name: 'conversation', label: '对话', icon: 'ChatDotRound', path: '/chat/conversation' }, - { name: 'image', label: '图片生成', icon: 'Picture', path: '/chat/image' }, - { name: 'video', label: '视频生成', icon: 'VideoCamera', path: '/chat/video' }, + { name: 'conversation', label: 'AI对话', icon: 'ChatDotRound', path: '/chat/conversation' }, + { name: 'image', label: 'AI图片', icon: 'Picture', path: '/chat/image' }, + { name: 'video', label: 'AI视频', icon: 'VideoCamera', path: '/chat/video' }, + { name: 'monitor', label: 'AI智能体', icon: 'Monitor', path: '/chat/agent' }, ]; // 当前激活的菜单 diff --git a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue index 1ce311a7..a7b76079 100644 --- a/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue +++ b/Yi.Ai.Vue3/src/pages/modelLibrary/index.vue @@ -34,7 +34,7 @@ const modelTypeOptions = ref([]); const apiTypeOptions = ref([]); function goToMonitor() { - window.open('http://data.ccnetcore.com:91/', '_blank'); + window.open('http://data.ccnetcore.com:91/?period=24h', '_blank'); } async function fetchModelList() { diff --git a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts index 33d29cee..6c38b4e7 100644 --- a/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts +++ b/Yi.Ai.Vue3/src/routers/modules/staticRouter.ts @@ -32,7 +32,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'chatConversation', component: () => import('@/pages/chat/conversation/index.vue'), meta: { - title: 'AI对话', + title: '意心Ai-AI对话', isDefaultChat: true, }, }, @@ -41,7 +41,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'chatConversationWithId', component: () => import('@/pages/chat/conversation/index.vue'), meta: { - title: 'AI对话', + title: '意心Ai-AI对话', isDefaultChat: false, }, }, @@ -50,7 +50,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'chatImage', component: () => import('@/pages/chat/image/index.vue'), meta: { - title: '图片生成', + title: '意心Ai-AI图片', }, }, { @@ -58,7 +58,15 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'chatVideo', component: () => import('@/pages/chat/video/index.vue'), meta: { - title: '视频生成', + title: '意心Ai-AI视频', + }, + }, + { + path: 'agent', + name: 'monitor', + component: () => import('@/pages/chat/agent/index.vue'), + meta: { + title: '意心Ai-AI智能体', }, }, ], @@ -70,7 +78,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'products', component: () => import('@/pages/products/index.vue'), meta: { - title: '产品页面', + title: '意心Ai-产品页面', keepAlive: 0, isDefaultChat: false, layout: 'blankPage', @@ -83,7 +91,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'modelLibrary', component: () => import('@/pages/modelLibrary/index.vue'), meta: { - title: '模型库', + title: '意心Ai-模型库', keepAlive: 0, isDefaultChat: false, layout: 'default', @@ -96,7 +104,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'payResult', component: () => import('@/pages/payResult/index.vue'), meta: { - title: '支付结果', + title: '意心Ai-支付结果', keepAlive: 0, isDefaultChat: false, layout: 'blankPage', @@ -109,7 +117,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'activityDetail', component: () => import('@/pages/activity/detail.vue'), meta: { - title: '活动详情', + title: '意心Ai-活动详情', isDefaultChat: false, layout: 'blankPage', }, @@ -121,7 +129,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'announcementDetail', component: () => import('@/pages/announcement/detail.vue'), meta: { - title: '公告详情', + title: '意心Ai-公告详情', isDefaultChat: false, layout: 'blankPage', }, @@ -133,7 +141,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'console', component: () => import('@/pages/console/index.vue'), meta: { - title: '控制台', + title: '意心Ai-控制台', icon: 'Setting', layout: 'default', }, @@ -148,7 +156,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleUser', component: () => import('@/components/userPersonalCenter/components/UserManagement.vue'), meta: { - title: '用户信息', + title: '意心Ai-用户信息', }, }, { @@ -156,7 +164,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleApikey', component: () => import('@/components/userPersonalCenter/components/APIKeyManagement.vue'), meta: { - title: 'API密钥', + title: '意心Ai-API密钥', }, }, { @@ -164,7 +172,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleRechargeLog', component: () => import('@/components/userPersonalCenter/components/RechargeLog.vue'), meta: { - title: '充值记录', + title: '意心Ai-充值记录', }, }, { @@ -172,7 +180,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleUsage', component: () => import('@/components/userPersonalCenter/components/UsageStatistics.vue'), meta: { - title: '用量统计', + title: '意心Ai-用量统计', }, }, { @@ -180,7 +188,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consolePremium', component: () => import('@/components/userPersonalCenter/components/PremiumService.vue'), meta: { - title: '尊享服务', + title: '意心Ai-尊享服务', }, }, { @@ -188,7 +196,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleDailyTask', component: () => import('@/components/userPersonalCenter/components/DailyTask.vue'), meta: { - title: '每日任务', + title: '意心Ai-每日任务', }, }, { @@ -196,7 +204,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleInvite', component: () => import('@/components/userPersonalCenter/components/CardFlipActivity.vue'), meta: { - title: '每周邀请', + title: '意心Ai-每周邀请', }, }, { @@ -204,7 +212,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleActivation', component: () => import('@/components/userPersonalCenter/components/ActivationCode.vue'), meta: { - title: '激活码兑换', + title: '意心Ai-激活码兑换', }, }, { @@ -212,7 +220,7 @@ export const layoutRouter: RouteRecordRaw[] = [ name: 'consoleChannel', component: () => import('@/pages/console/channel/index.vue'), meta: { - title: '渠道商管理', + title: '意心Ai-渠道商管理', }, }, ], From a6e7a5e906a5193ffb4a1b40b9cdc4cafe043080 Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 02:20:52 +0800 Subject: [PATCH 30/43] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E5=95=86=E6=8B=A6=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 436d8c92..9f21193b 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -113,7 +113,7 @@ namespace Yi.Abp.Web //本地开发环境,可以禁用作业执行 if (host.IsDevelopment()) { - Configure(options => { options.IsEnabled = false; }); + //Configure(options => { options.IsEnabled = false; }); } //请求日志 From be5f57f65435ef5be630f9026d43edef89d712ac Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 02:58:21 +0800 Subject: [PATCH 31/43] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=B8=A0?= =?UTF-8?q?=E9=81=93=E5=95=86=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 1 + Yi.Ai.Vue3/src/App.vue | 8 +++++++- .../src/layouts/components/Header/components/Avatar.vue | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 9f21193b..06f01662 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -280,6 +280,7 @@ namespace Yi.Abp.Web { options.TokenValidationParameters = new TokenValidationParameters { + RoleClaimType = "Roles", ClockSkew = TimeSpan.Zero, ValidateIssuerSigningKey = true, ValidIssuer = jwtOptions.Issuer, diff --git a/Yi.Ai.Vue3/src/App.vue b/Yi.Ai.Vue3/src/App.vue index 3ea0046e..8a074912 100644 --- a/Yi.Ai.Vue3/src/App.vue +++ b/Yi.Ai.Vue3/src/App.vue @@ -4,5 +4,11 @@ + - + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue index a28f0640..f3c14952 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -216,7 +216,7 @@ function openVipGuide() { diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue new file mode 100644 index 00000000..6d632e2d --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue new file mode 100644 index 00000000..12b981b9 --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue @@ -0,0 +1,275 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/TaskCard.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/TaskCard.vue new file mode 100644 index 00000000..4eea66af --- /dev/null +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/TaskCard.vue @@ -0,0 +1,112 @@ + + + + + \ No newline at end of file diff --git a/Yi.Ai.Vue3/src/pages/chat/image/index.vue b/Yi.Ai.Vue3/src/pages/chat/image/index.vue index b319c725..6d6a0d45 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/index.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/index.vue @@ -1,26 +1,85 @@ - - - diff --git a/Yi.Ai.Vue3/src/pages/console/index.vue b/Yi.Ai.Vue3/src/pages/console/index.vue index b7f7fa4d..9d480616 100644 --- a/Yi.Ai.Vue3/src/pages/console/index.vue +++ b/Yi.Ai.Vue3/src/pages/console/index.vue @@ -6,6 +6,11 @@ import { useRoute, useRouter } from 'vue-router'; const route = useRoute(); const router = useRouter(); +// 从 URL 中提取邀请码参数 +const inviteCodeFromUrl = computed(() => { + return route.query.inviteCode as string | undefined; +}); + // 控制侧边栏折叠状态 const isCollapsed = ref(false); @@ -103,7 +108,7 @@ window.addEventListener('resize', checkIsMobile); - + diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 16d28441..9e9269b3 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -19,6 +19,7 @@ declare module 'vue' { ElBadge: typeof import('element-plus/es')['ElBadge'] ElButton: typeof import('element-plus/es')['ElButton'] ElCard: typeof import('element-plus/es')['ElCard'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckTag: typeof import('element-plus/es')['ElCheckTag'] ElCol: typeof import('element-plus/es')['ElCol'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] @@ -36,6 +37,7 @@ declare module 'vue' { ElHeader: typeof import('element-plus/es')['ElHeader'] ElIcon: typeof import('element-plus/es')['ElIcon'] ElImage: typeof import('element-plus/es')['ElImage'] + ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElMain: typeof import('element-plus/es')['ElMain'] @@ -59,6 +61,7 @@ declare module 'vue' { ElTimeline: typeof import('element-plus/es')['ElTimeline'] ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElUpload: typeof import('element-plus/es')['ElUpload'] FilesSelect: typeof import('./../src/components/FilesSelect/index.vue')['default'] IconSelect: typeof import('./../src/components/IconSelect/index.vue')['default'] Indexl: typeof import('./../src/components/SupportModelProducts/indexl.vue')['default'] @@ -87,6 +90,7 @@ declare module 'vue' { WelecomeText: typeof import('./../src/components/WelecomeText/index.vue')['default'] } export interface GlobalDirectives { + vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll'] vLoading: typeof import('element-plus/es')['ElLoadingDirective'] } } From 12878ba02243ad781cc010e09a5d82ef5075814d Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 16:00:18 +0800 Subject: [PATCH 37/43] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...skPageInput.cs => ImageMyTaskPageInput.cs} | 20 ++++--- .../Dtos/Chat/ImagePlazaPageInput.cs | 31 ++++++++++ .../Dtos/Chat/ImageTaskOutput.cs | 10 ++++ .../Jobs/ImageGenerationJob.cs | 8 +-- .../Services/Chat/AiImageService.cs | 59 ++++++++++--------- .../Chat/ImageStoreTaskAggregateRoot.cs | 11 +++- 6 files changed, 95 insertions(+), 44 deletions(-) rename Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/{ImageTaskPageInput.cs => ImageMyTaskPageInput.cs} (57%) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImagePlazaPageInput.cs diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskPageInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageMyTaskPageInput.cs similarity index 57% rename from Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskPageInput.cs rename to Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageMyTaskPageInput.cs index c42985d5..1f977cbf 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskPageInput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageMyTaskPageInput.cs @@ -1,24 +1,26 @@ +using Volo.Abp.Application.Dtos; using Yi.Framework.AiHub.Domain.Shared.Enums; +using Yi.Framework.Ddd.Application.Contracts; namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; /// /// 图片任务分页查询输入 /// -public class ImageTaskPageInput +public class ImageMyTaskPageInput: PagedAllResultRequestDto { /// - /// 页码(从1开始) + /// 提示词 /// - public int PageIndex { get; set; } = 1; - - /// - /// 每页数量 - /// - public int PageSize { get; set; } = 10; - + public string? Prompt { get; set; } + /// /// 任务状态筛选(可选) /// public TaskStatusEnum? TaskStatus { get; set; } + + /// + /// 发布状态 + /// + public PublishStatusEnum? PublishStatus { get; set; } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImagePlazaPageInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImagePlazaPageInput.cs new file mode 100644 index 00000000..184e75d7 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImagePlazaPageInput.cs @@ -0,0 +1,31 @@ +using Volo.Abp.Application.Dtos; +using Yi.Framework.AiHub.Domain.Shared.Enums; +using Yi.Framework.Ddd.Application.Contracts; + +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Chat; + +/// +/// 图片任务分页查询输入 +/// +public class ImagePlazaPageInput: PagedAllResultRequestDto +{ + /// + /// 分类 + /// + public string? Categories { get; set; } + + /// + /// 提示词 + /// + public string? Prompt { get; set; } + + /// + /// 任务状态筛选(可选) + /// + public TaskStatusEnum? TaskStatus { get; set; } + + /// + /// 用户名 + /// + public string? UserName{ get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs index 8ed9083b..ffd3fdbd 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs @@ -61,4 +61,14 @@ public class ImageTaskOutput /// 错误信息 /// public string? ErrorInfo { get; set; } + + /// + /// 用户名称 + /// + public string? UserName { get; set; } + + /// + /// 用户名称Id + /// + public Guid UserId { get; set; } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs index d0912fe2..67f9856d 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Jobs/ImageGenerationJob.cs @@ -43,7 +43,7 @@ public class ImageGenerationJob : AsyncBackgroundJob, IT // 构建 Gemini API 请求对象 var parts = new List { - new { role="user",text = task.Prompt } + new { text = task.Prompt } }; // 添加参考图(如果有) @@ -64,7 +64,7 @@ public class ImageGenerationJob : AsyncBackgroundJob, IT { contents = new[] { - new { parts } + new { role = "user", parts } } }; @@ -77,9 +77,9 @@ public class ImageGenerationJob : AsyncBackgroundJob, IT task.ModelId, request, task.UserId, - tokenId:task.TokenId); + tokenId: task.TokenId); + - _logger.LogInformation("图片生成任务完成,TaskId: {TaskId}", args.TaskId); } catch (Exception ex) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index bf0a1a9d..b4bb6155 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using SqlSugar; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.BackgroundJobs; @@ -104,6 +105,7 @@ public class AiImageService : ApplicationService ReferenceImagesUrl = new List(), TaskStatus = TaskStatusEnum.Processing, UserId = userId, + UserName = CurrentUser.UserName, TokenId = input.TokenId, ModelId = input.ModelId }; @@ -147,7 +149,6 @@ public class AiImageService : ApplicationService Categories = task.Categories, CreationTime = task.CreationTime, ErrorInfo = task.ErrorInfo, - }; } @@ -238,26 +239,23 @@ public class AiImageService : ApplicationService /// 分页查询我的任务列表 /// [HttpGet("ai-image/my-tasks")] - public async Task> GetMyTaskPageAsync([FromQuery] ImageTaskPageInput input) + public async Task> GetMyTaskPageAsync([FromQuery] ImageMyTaskPageInput input) { var userId = CurrentUser.GetId(); - var query = _imageTaskRepository._DbQueryable + RefAsync total = 0; + var output = await _imageTaskRepository._DbQueryable .Where(x => x.UserId == userId) - .WhereIF(input.TaskStatus.HasValue, x => x.TaskStatus == input.TaskStatus!.Value) - .OrderByDescending(x => x.CreationTime); - - var total = await query.CountAsync(); - var items = await query - .Skip((input.PageIndex - 1) * input.PageSize) - .Take(input.PageSize) + .WhereIF(input.TaskStatus is not null, x => x.TaskStatus == input.TaskStatus) + .WhereIF(!string.IsNullOrWhiteSpace(input.Prompt), x => x.Prompt.Contains(input.Prompt)) + .WhereIF(input.PublishStatus is not null, x => x.PublishStatus == input.PublishStatus) + .WhereIF(input.StartTime is not null && input.EndTime is not null, + x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .OrderByDescending(x => x.CreationTime) .Select(x => new ImageTaskOutput { Id = x.Id, Prompt = x.Prompt, - // ReferenceImagesBase64 = x.ReferenceImagesBase64, - // ReferenceImagesUrl = x.ReferenceImagesUrl, - // StoreBase64 = x.StoreBase64, StoreUrl = x.StoreUrl, TaskStatus = x.TaskStatus, PublishStatus = x.PublishStatus, @@ -265,9 +263,10 @@ public class AiImageService : ApplicationService CreationTime = x.CreationTime, ErrorInfo = x.ErrorInfo }) - .ToListAsync(); + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - return new PagedResult(total, items); + + return new PagedResult(total, output); } /// @@ -275,32 +274,36 @@ public class AiImageService : ApplicationService /// [HttpGet("ai-image/plaza")] [AllowAnonymous] - public async Task> GetPlazaPageAsync([FromQuery] ImageTaskPageInput input) + public async Task> GetPlazaPageAsync([FromQuery] ImagePlazaPageInput input) { - var query = _imageTaskRepository._DbQueryable + RefAsync total = 0; + var output = await _imageTaskRepository._DbQueryable .Where(x => x.PublishStatus == PublishStatusEnum.Published) .Where(x => x.TaskStatus == TaskStatusEnum.Success) - .OrderByDescending(x => x.CreationTime); - - var total = await query.CountAsync(); - var items = await query - .Skip((input.PageIndex - 1) * input.PageSize) - .Take(input.PageSize) + .WhereIF(input.TaskStatus is not null, x => x.TaskStatus == input.TaskStatus) + .WhereIF(!string.IsNullOrWhiteSpace(input.Prompt), x => x.Prompt.Contains(input.Prompt)) + .WhereIF(!string.IsNullOrWhiteSpace(input.Categories), x => SqlFunc.JsonLike(x.Categories, input.Categories)) + .WhereIF(!string.IsNullOrWhiteSpace(input.UserName),x=>x.UserName.Contains(input.UserName) ) + .WhereIF(input.StartTime is not null && input.EndTime is not null, + x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .OrderByDescending(x => x.CreationTime) .Select(x => new ImageTaskOutput { Id = x.Id, Prompt = x.Prompt, - // ReferenceImagesPrefixBase64 = x.ReferenceImagesPrefixBase64, - // ReferenceImagesUrl = x.ReferenceImagesUrl, StoreUrl = x.StoreUrl, TaskStatus = x.TaskStatus, PublishStatus = x.PublishStatus, Categories = x.Categories, - CreationTime = x.CreationTime + CreationTime = x.CreationTime, + UserName = x.UserName, + UserId = x.UserId, }) - .ToListAsync(); + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); + ; - return new PagedResult(total, items); + + return new PagedResult(total, output); } /// diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs index 37fe666b..76fdde14 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs @@ -16,16 +16,16 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot /// /// 参考图PrefixBase64(带前缀,如 data:image/png;base64,xxx) /// - [SugarColumn(IsJson = true,ColumnDataType = StaticConfig.CodeFirst_BigString)] + [SugarColumn(IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString)] public List ReferenceImagesPrefixBase64 { get; set; } - + /// /// 参考图url /// [SugarColumn(IsJson = true)] public List ReferenceImagesUrl { get; set; } - + /// /// 图片绝对路径 /// @@ -41,6 +41,11 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot /// public Guid UserId { get; set; } + /// + /// 用户名称 + /// + public string? UserName { get; set; } + /// /// 模型id /// From 3892ff1937c4f484e92baaf714ed5d8784479f3b Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Sat, 3 Jan 2026 16:17:57 +0800 Subject: [PATCH 38/43] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=8C=BF?= =?UTF-8?q?=E5=90=8D=E5=AD=97=E6=AE=B5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Chat/ImageTaskOutput.cs | 16 +++------------ .../Dtos/Chat/PublishImageInput.cs | 5 +++++ .../Services/Chat/AiImageService.cs | 20 ++++++++++++++----- .../Chat/ImageStoreTaskAggregateRoot.cs | 20 ++++++++++++++++++- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs index ffd3fdbd..4e06b189 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/ImageTaskOutput.cs @@ -18,19 +18,9 @@ public class ImageTaskOutput public string Prompt { get; set; } = string.Empty; /// - /// 参考图PrefixBase64列表(带前缀) + /// 是否匿名 /// - // public List? ReferenceImagesPrefixBase64 { get; set; } - - /// - /// 参考图URL列表 - /// - // public List? ReferenceImagesUrl { get; set; } - - /// - /// 生成图片PrefixBase64(包含前缀) - /// - // public string? StorePrefixBase64 { get; set; } + public bool IsAnonymous { get; set; } /// /// 生成图片URL @@ -70,5 +60,5 @@ public class ImageTaskOutput /// /// 用户名称Id /// - public Guid UserId { get; set; } + public Guid? UserId { get; set; } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/PublishImageInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/PublishImageInput.cs index 9300d7df..bb33ad01 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/PublishImageInput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Chat/PublishImageInput.cs @@ -5,6 +5,11 @@ /// public class PublishImageInput { + /// + /// 是否匿名 + /// + public bool IsAnonymous { get; set; } = false; + /// /// 任务ID /// diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs index b4bb6155..fad3ecc7 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/Chat/AiImageService.cs @@ -291,18 +291,29 @@ public class AiImageService : ApplicationService { Id = x.Id, Prompt = x.Prompt, + IsAnonymous = x.IsAnonymous, StoreUrl = x.StoreUrl, TaskStatus = x.TaskStatus, PublishStatus = x.PublishStatus, Categories = x.Categories, CreationTime = x.CreationTime, + ErrorInfo = null, UserName = x.UserName, UserId = x.UserId, + }) - .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); - ; + .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); ; + output.ForEach(x => + { + if (x.IsAnonymous) + { + x.UserName = null; + x.UserId = null; + } + }); + return new PagedResult(total, output); } @@ -330,9 +341,8 @@ public class AiImageService : ApplicationService throw new UserFriendlyException("该任务已发布"); } - task.PublishStatus = PublishStatusEnum.Published; - task.Categories = input.Categories ?? new List(); - + //设置发布 + task.SetPublish(input.IsAnonymous,input.Categories); await _imageTaskRepository.UpdateAsync(task); } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs index 76fdde14..c2593409 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/Chat/ImageStoreTaskAggregateRoot.cs @@ -68,7 +68,11 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot [SugarColumn(IsJson = true)] public List Categories { get; set; } = new(); - + /// + /// 是否匿名 + /// + public bool IsAnonymous { get; set; } = false; + /// /// 密钥id /// @@ -83,4 +87,18 @@ public class ImageStoreTaskAggregateRoot : FullAuditedAggregateRoot TaskStatus = TaskStatusEnum.Success; StoreUrl = storeUrl; } + + /// + /// 设置发布 + /// + /// + /// + public void SetPublish(bool isAnonymous,List categories) + { + this.PublishStatus = PublishStatusEnum.Published; + this.IsAnonymous = isAnonymous; + this.Categories = categories; + } + + } \ No newline at end of file From 42edd4c2305f8ae98329ea9cbd36bc2d0d3d2d8a Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Sat, 3 Jan 2026 17:03:42 +0800 Subject: [PATCH 39/43] =?UTF-8?q?feat:=20=E8=B7=AF=E7=94=B1=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E6=9D=83=E9=99=90=E6=8E=A7=E5=88=B6=E3=80=81=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E5=B9=BF=E5=9C=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/CardFlipActivity.vue | 230 +++++++++++++++++- Yi.Ai.Vue3/src/config/index.ts | 2 +- Yi.Ai.Vue3/src/config/permission.ts | 110 +++++++++ .../chat/image/components/ImageGenerator.vue | 145 ++++++----- .../chat/image/components/ImagePlaza.vue | 36 ++- .../pages/chat/image/components/MyImages.vue | 140 +++++++++-- Yi.Ai.Vue3/src/pages/chat/image/index.vue | 76 +++--- Yi.Ai.Vue3/src/pages/console/index.vue | 17 +- Yi.Ai.Vue3/src/pages/payResult/index.vue | 3 + Yi.Ai.Vue3/src/routers/index.ts | 12 + Yi.Ai.Vue3/src/styles/var.scss | 34 +-- 11 files changed, 660 insertions(+), 145 deletions(-) create mode 100644 Yi.Ai.Vue3/src/config/permission.ts diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue index 666f5900..14e47109 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue @@ -408,7 +408,10 @@ async function handleFlipCard(record: CardFlipRecord) { await new Promise(resolve => requestAnimationFrame(resolve)); // 4. 移动克隆卡片到屏幕中心并放大(考虑边界限制) - const scale = Math.min(1.8, window.innerWidth / rect.width * 0.6); // 动态计算缩放比例 + // 移动端使用更小的缩放比例 + const isMobile = window.innerWidth <= 768; + const maxScale = isMobile ? 1.5 : 1.8; + const scale = Math.min(maxScale, window.innerWidth / rect.width * (isMobile ? 0.5 : 0.6)); const scaledWidth = rect.width * scale; const scaledHeight = rect.height * scale; @@ -416,8 +419,8 @@ async function handleFlipCard(record: CardFlipRecord) { let centerX = window.innerWidth / 2; let centerY = window.innerHeight / 2; - // 边界检查:确保卡片完全在视口内(留20px边距) - const margin = 20; + // 边界检查:确保卡片完全在视口内(移动端留更多边距) + const margin = isMobile ? 30 : 20; const minX = scaledWidth / 2 + margin; const maxX = window.innerWidth - scaledWidth / 2 - margin; const minY = scaledHeight / 2 + margin; @@ -1253,6 +1256,11 @@ function getCardClass(record: CardFlipRecord): string[] { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; + @media (max-width: 768px) { + padding: 12px; + border-radius: 12px; + } + /* 自定义滚动条 */ &::-webkit-scrollbar { width: 6px; @@ -1277,15 +1285,36 @@ function getCardClass(record: CardFlipRecord): string[] { .lucky-float-ball { position: fixed; left: 50%; - /* left: 20px; */ - /* top: 20px; */ + transform: translateX(-50%); z-index: 999; bottom: 20px; - transition: all 0.3s - cubic-bezier(0.4, 0, 0.2, 1); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); cursor: pointer; + &:hover { - transform: scale(1.1); + transform: translateX(-50%) scale(1.1); + } + + @media (max-width: 768px) { + bottom: 15px; + + .lucky-circle { + width: 70px; + height: 70px; + } + + .lucky-content .lucky-icon { + font-size: 20px; + } + + .lucky-content .lucky-text { + font-size: 12px; + } + + .lucky-label { + font-size: 11px; + margin-top: 4px; + } } &.lucky-full { @@ -1408,6 +1437,12 @@ function getCardClass(record: CardFlipRecord): string[] { animation: slideIn 0.5s ease; flex-wrap: wrap; + @media (max-width: 768px) { + padding: 8px 10px; + gap: 8px; + margin-bottom: 10px; + } + .compact-stats { display: flex; align-items: center; @@ -1497,6 +1532,11 @@ function getCardClass(record: CardFlipRecord): string[] { border-radius: 12px; backdrop-filter: blur(10px); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + + @media (max-width: 768px) { + padding: 12px 20px; + border-radius: 8px; + } } .shuffle-text { @@ -1505,6 +1545,15 @@ function getCardClass(record: CardFlipRecord): string[] { color: #fff; text-shadow: 0 2px 8px rgba(255, 215, 0, 0.6); animation: textPulse 1.5s ease-in-out infinite; + white-space: nowrap; + + @media (max-width: 768px) { + font-size: 14px; + } + + @media (max-width: 480px) { + font-size: 12px; + } } } @@ -1515,8 +1564,13 @@ function getCardClass(record: CardFlipRecord): string[] { max-width: 100%; @media (max-width: 768px) { - grid-template-columns: repeat(5, 1fr); - gap: 6px; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + } + + @media (max-width: 480px) { + grid-template-columns: repeat(2, 1fr); + gap: 12px; } // 洗牌阶段样式 @@ -1683,6 +1737,11 @@ function getCardClass(record: CardFlipRecord): string[] { background: rgba(0, 0, 0, 0.2); padding: 2px 6px; border-radius: 4px; + + @media (max-width: 768px) { + font-size: 9px; + padding: 2px 5px; + } } .card-content { @@ -1691,6 +1750,10 @@ function getCardClass(record: CardFlipRecord): string[] { align-items: center; gap: 8px; z-index: 1; + + @media (max-width: 768px) { + gap: 6px; + } } // 系统logo样式(优化为居中圆形,更丰富的效果) @@ -1709,6 +1772,12 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 3; filter: brightness(1.1); + @media (max-width: 768px) { + width: 40px; + height: 40px; + border: 3px solid rgba(255, 255, 255, 1); + } + // 外层光晕效果 &::before { content: ''; @@ -1741,6 +1810,10 @@ function getCardClass(record: CardFlipRecord): string[] { font-weight: bold; color: #fff; text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + + @media (max-width: 768px) { + font-size: 24px; + } } .card-type-badge { @@ -1753,6 +1826,12 @@ function getCardClass(record: CardFlipRecord): string[] { border-radius: 10px; backdrop-filter: blur(4px); z-index: 2; + + @media (max-width: 768px) { + font-size: 9px; + padding: 2px 6px; + bottom: 4px; + } } .card-shine { @@ -1827,6 +1906,10 @@ function getCardClass(record: CardFlipRecord): string[] { position: relative; z-index: 1; + @media (max-width: 768px) { + padding: 8px; + } + // logo水印样式 .result-watermark { position: absolute; @@ -1869,6 +1952,10 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 1; filter: drop-shadow(0 4px 8px rgba(255, 215, 0, 0.5)); margin-bottom: 4px; + + @media (max-width: 768px) { + font-size: 36px; + } } .result-text { @@ -1883,6 +1970,11 @@ function getCardClass(record: CardFlipRecord): string[] { letter-spacing: 2px; position: relative; + @media (max-width: 768px) { + font-size: 16px; + letter-spacing: 1px; + } + // 文字外发光 &::after { content: attr(data-text); @@ -1907,6 +1999,11 @@ function getCardClass(record: CardFlipRecord): string[] { position: relative; filter: drop-shadow(0 3px 10px rgba(255, 215, 0, 0.6)); + @media (max-width: 768px) { + font-size: 32px; + margin: 8px 0; + } + // 金色光效边框 &::before { content: attr(data-amount); @@ -1932,6 +2029,11 @@ function getCardClass(record: CardFlipRecord): string[] { letter-spacing: 3px; text-transform: uppercase; margin-top: 4px; + + @media (max-width: 768px) { + font-size: 14px; + letter-spacing: 2px; + } } } @@ -1953,6 +2055,10 @@ function getCardClass(record: CardFlipRecord): string[] { margin-bottom: 6px; filter: drop-shadow(0 2px 6px rgba(147, 112, 219, 0.3)); animation: gentleBounce 2s ease-in-out infinite; + + @media (max-width: 768px) { + font-size: 36px; + } } .result-text { @@ -1964,6 +2070,10 @@ function getCardClass(record: CardFlipRecord): string[] { margin: 8px 0; z-index: 1; letter-spacing: 1px; + + @media (max-width: 768px) { + font-size: 15px; + } } .result-tip { @@ -1972,6 +2082,10 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 1; margin-top: 6px; font-weight: 500; + + @media (max-width: 768px) { + font-size: 12px; + } } } @@ -1994,6 +2108,11 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 1; filter: drop-shadow(0 4px 12px rgba(255, 215, 0, 0.8)); margin: 10px 0; + + @media (max-width: 768px) { + font-size: 42px; + margin: 8px 0; + } } .mystery-text { @@ -2004,6 +2123,11 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 1; letter-spacing: 4px; margin: 8px 0; + + @media (max-width: 768px) { + font-size: 18px; + letter-spacing: 2px; + } } .mystery-hint { @@ -2012,6 +2136,11 @@ function getCardClass(record: CardFlipRecord): string[] { z-index: 1; letter-spacing: 2px; margin-top: 6px; + + @media (max-width: 768px) { + font-size: 12px; + letter-spacing: 1px; + } } .mystery-stars { @@ -2060,6 +2189,11 @@ function getCardClass(record: CardFlipRecord): string[] { margin-bottom: 16px; text-align: center; line-height: 1.6; + + @media (max-width: 768px) { + font-size: 13px; + margin-bottom: 12px; + } } .code-input { @@ -2078,10 +2212,19 @@ function getCardClass(record: CardFlipRecord): string[] { border: 1px solid #bae6fd; border-radius: 12px; + @media (max-width: 768px) { + padding: 20px 12px; + } + .filled-icon { font-size: 48px; display: block; margin-bottom: 12px; + + @media (max-width: 768px) { + font-size: 40px; + margin-bottom: 10px; + } } .filled-text { @@ -2090,6 +2233,10 @@ function getCardClass(record: CardFlipRecord): string[] { line-height: 1.6; margin: 0; font-weight: 500; + + @media (max-width: 768px) { + font-size: 14px; + } } } } @@ -2128,12 +2275,22 @@ function getCardClass(record: CardFlipRecord): string[] { border-radius: 12px; margin-bottom: 16px; + @media (max-width: 768px) { + padding: 14px; + margin-bottom: 12px; + } + .code-text { font-size: 28px; font-weight: bold; color: #fff; letter-spacing: 6px; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + + @media (max-width: 768px) { + font-size: 24px; + letter-spacing: 4px; + } } } @@ -2142,8 +2299,17 @@ function getCardClass(record: CardFlipRecord): string[] { gap: 8px; margin-bottom: 12px; + @media (max-width: 768px) { + flex-direction: column; + gap: 10px; + } + .el-button { flex: 1; + + @media (max-width: 768px) { + width: 100%; + } } } @@ -2153,6 +2319,10 @@ function getCardClass(record: CardFlipRecord): string[] { line-height: 1.5; margin: 0 0 12px 0; + @media (max-width: 768px) { + font-size: 12px; + } + strong { color: #f56c6c; font-weight: 600; @@ -2199,12 +2369,20 @@ function getCardClass(record: CardFlipRecord): string[] { padding: 12px; text-align: left; + @media (max-width: 768px) { + padding: 10px; + } + .share-preview-title { font-size: 14px; font-weight: bold; color: #303133; margin-bottom: 8px; text-align: center; + + @media (max-width: 768px) { + font-size: 13px; + } } .share-preview-content { @@ -2218,6 +2396,12 @@ function getCardClass(record: CardFlipRecord): string[] { max-height: 200px; overflow-y: auto; + @media (max-width: 768px) { + font-size: 12px; + padding: 8px; + max-height: 150px; + } + /* 自定义滚动条 */ &::-webkit-scrollbar { width: 4px; @@ -2276,9 +2460,17 @@ function getCardClass(record: CardFlipRecord): string[] { display: inline-block; margin-bottom: 20px; + @media (max-width: 768px) { + margin-bottom: 16px; + } + .double-icon { font-size: 64px; animation: bounce 1s infinite; + + @media (max-width: 768px) { + font-size: 52px; + } } .double-sparkle { @@ -2287,6 +2479,10 @@ function getCardClass(record: CardFlipRecord): string[] { right: -10px; font-size: 32px; animation: spin 2s linear infinite; + + @media (max-width: 768px) { + font-size: 26px; + } } } @@ -2295,6 +2491,11 @@ function getCardClass(record: CardFlipRecord): string[] { font-weight: bold; color: #303133; margin-bottom: 16px; + + @media (max-width: 768px) { + font-size: 18px; + margin-bottom: 12px; + } } .double-text { @@ -2303,10 +2504,19 @@ function getCardClass(record: CardFlipRecord): string[] { color: #606266; margin-bottom: 24px; + @media (max-width: 768px) { + font-size: 14px; + margin-bottom: 20px; + } + .highlight { color: #f56c6c; font-weight: bold; font-size: 16px; + + @media (max-width: 768px) { + font-size: 15px; + } } } diff --git a/Yi.Ai.Vue3/src/config/index.ts b/Yi.Ai.Vue3/src/config/index.ts index 5f054135..7d60d8fc 100644 --- a/Yi.Ai.Vue3/src/config/index.ts +++ b/Yi.Ai.Vue3/src/config/index.ts @@ -12,4 +12,4 @@ export const COLLAPSE_THRESHOLD: number = 600; export const SIDE_BAR_WIDTH: number = 280; // 路由白名单地址[本地存在的路由 staticRouter.ts 中] -export const ROUTER_WHITE_LIST: string[] = ['/chat', '/chat/not_login', '/products', '/model-library', '/403', '/404']; +export const ROUTER_WHITE_LIST: string[] = ['/chat', '/chat/conversation', '/chat/image', '/chat/video', '/model-library', '/403', '/404']; diff --git a/Yi.Ai.Vue3/src/config/permission.ts b/Yi.Ai.Vue3/src/config/permission.ts new file mode 100644 index 00000000..c1c3c64a --- /dev/null +++ b/Yi.Ai.Vue3/src/config/permission.ts @@ -0,0 +1,110 @@ +/** + * 权限配置文件 + * 用于配置特定页面的访问权限 + */ + +/** + * 权限配置接口 + */ +export interface PermissionConfig { + /** 路由路径 */ + path: string; + /** 允许访问的用户名列表 */ + allowedUsers: string[]; + /** 权限描述 */ + description?: string; +} + +/** + * 页面权限配置列表 + * 在这里配置需要特殊权限控制的页面 + */ +export const PAGE_PERMISSIONS: PermissionConfig[] = [ + { + path: '/console/channel', + allowedUsers: ['cc', 'Guo'], + description: '渠道商管理页面 - 仅限cc和Guo用户访问', + }, + // 可以在这里继续添加其他需要权限控制的页面 + // { + // path: '/console/admin', + // allowedUsers: ['admin', 'superadmin'], + // description: '管理员页面', + // }, +]; + +/** + * 检查用户是否有权限访问指定路径 + * @param path 路由路径 + * @param userName 用户名 + * @returns 是否有权限 + */ +export function checkPagePermission(path: string, userName: string | undefined): boolean { + // 如果没有用户名,返回false + if (!userName) { + return false; + } + + // 查找该路径的权限配置 + const permissionConfig = PAGE_PERMISSIONS.find(config => config.path === path); + + // 如果没有配置权限,说明该页面不需要特殊权限,返回true + if (!permissionConfig) { + return true; + } + + // 检查用户名是否在允许列表中(不区分大小写) + return permissionConfig.allowedUsers.some( + allowedUser => allowedUser.toLowerCase() === userName.toLowerCase(), + ); +} + +/** + * 获取用户无权访问的路由列表 + * @param userName 用户名 + * @returns 无权访问的路由路径数组 + */ +export function getRestrictedRoutes(userName: string | undefined): string[] { + if (!userName) { + return PAGE_PERMISSIONS.map(config => config.path); + } + + return PAGE_PERMISSIONS.filter( + config => !config.allowedUsers.some( + allowedUser => allowedUser.toLowerCase() === userName.toLowerCase(), + ), + ).map(config => config.path); +} + +/** + * 检查路由是否需要权限控制 + * @param path 路由路径 + * @returns 是否需要权限控制 + */ +export function isRestrictedRoute(path: string): boolean { + return PAGE_PERMISSIONS.some(config => config.path === path); +} + +/** + * 过滤菜单路由,移除用户无权访问的菜单项 + * @param routes 路由配置数组 + * @param userName 用户名 + * @returns 过滤后的路由配置数组 + */ +export function filterMenuRoutes(routes: any[], userName: string | undefined): any[] { + return routes.filter((route) => { + // 检查当前路由是否有权限 + const hasPermission = checkPagePermission(route.path, userName); + + if (!hasPermission) { + return false; + } + + // 如果有子路由,递归过滤 + if (route.children && route.children.length > 0) { + route.children = filterMenuRoutes(route.children, userName); + } + + return true; + }); +} diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue index 3f50855d..2145c693 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue @@ -12,10 +12,17 @@ import { ZoomIn, } from '@element-plus/icons-vue'; import { ElMessage } from 'element-plus'; -import { computed, onMounted, onUnmounted, ref } from 'vue'; +import { computed, onMounted, onUnmounted, ref, watch } from 'vue'; import { getSelectableTokenInfo } from '@/api'; import { generateImage, getImageModels, getTaskStatus } from '@/api/aiImage'; +const props = defineProps({ + isActive: { + type: Boolean, + default: true, + }, +}); + const emit = defineEmits(['task-created']); // State @@ -41,6 +48,19 @@ const canGenerate = computed(() => { return selectedModelId.value && prompt.value && !generating.value; }); +// Watch isActive to manage polling +watch(() => props.isActive, (active) => { + if (active) { + // Resume polling if we have a processing task + if (currentTaskId.value && currentTask.value?.taskStatus === 'Processing') { + startPolling(currentTaskId.value); + } + } + else { + stopPolling(); + } +}); + // Methods async function fetchTokens() { tokenLoading.value = true; @@ -197,6 +217,12 @@ function startPolling(taskId: string) { } async function pollStatus(taskId: string) { + // Double check active status before polling (though timer should be cleared) + if (!props.isActive) { + stopPolling(); + return; + } + try { const res = await getTaskStatus(taskId); // Handle response structure if needed @@ -257,7 +283,8 @@ async function downloadImage() { link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); - } catch (e) { + } + catch (e) { console.error('Download failed', e); // Fallback window.open(currentTask.value.storeUrl, '_blank'); @@ -324,14 +351,15 @@ onUnmounted(() => { 配置 - -
- + + + { - + + { -
- -
-
- - - - - 清空 - -
- -
- - -
- -
- -
- - - - 点击上传 + + + + + + + + +
+ +
+ + + + 点击上传 +
+
+
+ 最多2张,< 5MB (支持 JPG/PNG/WEBP) +
- -
- 最多2张,< 5MB (支持 JPG/PNG/WEBP) -
-
-
+ +
@@ -450,7 +481,7 @@ onUnmounted(() => {
- +
{ :deep(.hide-upload-btn .el-upload--picture-card) { display: none; } +/* 隐藏默认的标签 */ +:deep(.prompt-form-item .el-form-item__label){ + display: flex; +} diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue index 6d632e2d..da28d0bb 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue @@ -55,18 +55,22 @@ class="w-full h-full" :preview-src-list="[currentTask.storeUrl]" /> + +
+ +
-
-
-

+
+
+

提示词

-
+
{{ currentTask.prompt }}
-
+
创建时间 {{ formatTime(currentTask.creationTime) }} @@ -118,7 +122,7 @@ import TaskCard from './TaskCard.vue'; import { format } from 'date-fns'; import { ElMessage } from 'element-plus'; import { useClipboard } from '@vueuse/core'; -import { Picture, Loading, MagicStick, CopyDocument } from '@element-plus/icons-vue'; +import { Picture, Loading, MagicStick, CopyDocument, Download } from '@element-plus/icons-vue'; const emit = defineEmits(['use-prompt', 'use-reference']); @@ -197,6 +201,24 @@ const copyPrompt = async (text: string) => { await copy(text); ElMessage.success('提示词已复制'); }; + +const downloadImage = async (url: string) => { + try { + const response = await fetch(url); + const blob = await response.blob(); + const blobUrl = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = blobUrl; + link.download = `image-${Date.now()}.png`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(blobUrl); + } catch (e) { + console.error('Download failed', e); + window.open(url, '_blank'); + } +}; diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue index da28d0bb..73474a9b 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/ImagePlaza.vue @@ -1,128 +1,12 @@ - - - + diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue index 1225d4aa..ed11d16e 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/MyImages.vue @@ -1,49 +1,405 @@ + + - - diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue index 9df9fe82..22dbe36a 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/AnnouncementBtn.vue @@ -17,24 +17,6 @@ function openAnnouncement() { > 公告 - - -
- - - - -
@@ -151,26 +133,4 @@ function openAnnouncement() { box-shadow: 0 1px 2px rgba(245, 108, 108, 0.2); } } - -/* 移动端显示图标,隐藏文字 */ -@media (max-width: 768px) { - .pc-text { - display: none; - } - - .mobile-icon { - display: block; - } - - .announcement-btn { - padding: 8px; - } -} - -/* 响应式调整 */ -@media (min-width: 769px) { - .mobile-icon { - display: none; - } -} diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue index 54b4af58..78290e2b 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue @@ -1,5 +1,5 @@ @@ -166,7 +269,12 @@ function handleConsoleClick(e: MouseEvent) { height: var(--header-container-default-height, 64px); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08); user-select: none; + position: relative; +} +// 移动端头部(默认隐藏) +.mobile-header { + display: none; } .header-menu { @@ -294,6 +402,112 @@ function handleConsoleClick(e: MouseEvent) { margin-left: 4px; } +// 移动端头部样式 +.mobile-brand { + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + padding: 8px; + border-radius: 6px; + transition: background-color 0.2s; + + &:hover { + background-color: var(--menu-hover-bg); + } +} + +.mobile-logo { + width: 32px; + height: 32px; + flex-shrink: 0; +} + +.mobile-brand-text { + font-size: 18px; + font-weight: 600; + color: var(--brand-color, #000000); +} + +.mobile-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.mobile-avatar { + display: flex; + align-items: center; +} + +.menu-toggle { + padding: 8px; + color: var(--el-text-color-primary); + + &:hover { + color: var(--el-color-primary); + } +} + +// 移动端抽屉样式 +.drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0; +} + +.drawer-title { + font-size: 18px; + font-weight: 600; + color: var(--el-text-color-primary); +} + +.mobile-menu { + border: none; + + :deep(.el-sub-menu__title), + :deep(.el-menu-item) { + height: 48px; + line-height: 48px; + padding: 0 20px !important; + margin: 4px 0; + border-radius: 8px; + transition: all 0.2s; + + &:hover { + background-color: var(--el-color-primary-light-9); + } + + &.is-active { + color: var(--el-color-primary); + background-color: var(--el-color-primary-light-9); + font-weight: 500; + } + } + + :deep(.el-icon) { + margin-right: 12px; + font-size: 18px; + } +} + +.mobile-menu-actions { + margin-top: 20px; + padding: 16px 0; + border-top: 1px solid var(--el-border-color-light); + + .action-item { + padding: 8px 20px; + margin: 4px 0; + + &:hover { + background-color: var(--el-color-primary-light-9); + border-radius: 8px; + } + } +} + // 响应式设计 @media (max-width: 1280px) { .brand-text { @@ -340,52 +554,37 @@ function handleConsoleClick(e: MouseEvent) { } @media (max-width: 768px) { - .brand-text { - display: none; + // 隐藏桌面端菜单 + .desktop-menu { + display: none !important; } - .logo-img { - width: 32px; - height: 32px; - } - - .menu-left { - margin-left: 12px; - } - - .menu-right { - margin-right: 8px; - - // 隐藏按钮文字 - :deep(.button-text) { - display: none; - } - - .menu-title { - display: none; - } - - // 显示图标 - :deep(.el-icon) { - font-size: 18px; - } - } - - :deep(.el-menu-item), - :deep(.el-sub-menu__title) { - padding: 0 8px !important; - min-width: auto !important; + // 显示移动端头部 + .mobile-header { + display: flex; + align-items: center; + justify-content: space-between; + height: 100%; + padding: 0 12px; } } @media (max-width: 480px) { - .menu-right { - gap: 0; + .mobile-header { + padding: 0 8px; + } - :deep(.el-menu-item), - :deep(.el-sub-menu__title) { - padding: 0 6px !important; - } + .mobile-brand-text { + font-size: 16px; + } + + .mobile-logo { + width: 28px; + height: 28px; + } + + .mobile-actions { + gap: 4px; } } diff --git a/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue b/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue index 067daa8f..03a82d35 100644 --- a/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue +++ b/Yi.Ai.Vue3/src/pages/chat/image/components/ImageGenerator.vue @@ -351,11 +351,11 @@ onUnmounted(() => {