From 3cae477f3e4bcfa4f208bdc8fcefe94fc6c45da4 Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Sat, 30 Aug 2025 22:28:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=89=AB=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yi.Ai.FileAssistant/ui/.gitignore | 25 -- Yi.Ai.FileAssistant/ui/index.html | 21 -- Yi.Ai.FileAssistant/ui/main.js | 26 -- Yi.Ai.FileAssistant/ui/package.json | 15 - Yi.Ai.Vue3/package.json | 1 + Yi.Ai.Vue3/pnpm-lock.yaml | 11 + Yi.Ai.Vue3/src/api/user/index.ts | 4 +- .../components/QrCodeLogin/index.vue | 210 ++++++----- .../src/components/LoginDialog/index.vue | 3 +- .../components/UserManagement.vue | 346 +++++++++++++++++- .../components/Header/components/Avatar.vue | 3 +- Yi.Ai.Vue3/src/utils/user.ts | 7 + Yi.Ai.Vue3/types/components.d.ts | 2 + 13 files changed, 468 insertions(+), 206 deletions(-) delete mode 100644 Yi.Ai.FileAssistant/ui/.gitignore delete mode 100644 Yi.Ai.FileAssistant/ui/index.html delete mode 100644 Yi.Ai.FileAssistant/ui/main.js delete mode 100644 Yi.Ai.FileAssistant/ui/package.json diff --git a/Yi.Ai.FileAssistant/ui/.gitignore b/Yi.Ai.FileAssistant/ui/.gitignore deleted file mode 100644 index 49ef0bd1..00000000 --- a/Yi.Ai.FileAssistant/ui/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -!.vscode/settings.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/Yi.Ai.FileAssistant/ui/index.html b/Yi.Ai.FileAssistant/ui/index.html deleted file mode 100644 index db2a2716..00000000 --- a/Yi.Ai.FileAssistant/ui/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Hello from Electron renderer! - - -

Hello from Electron renderer!

-

👋

-

- - - diff --git a/Yi.Ai.FileAssistant/ui/main.js b/Yi.Ai.FileAssistant/ui/main.js deleted file mode 100644 index ad5d6735..00000000 --- a/Yi.Ai.FileAssistant/ui/main.js +++ /dev/null @@ -1,26 +0,0 @@ -const { app, BrowserWindow } = require('electron/main') - -const createWindow = () => { - const win = new BrowserWindow({ - width: 800, - height: 600 - }) - - win.loadFile('index.html') -} - -app.whenReady().then(() => { - createWindow() - - app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) { - createWindow() - } - }) -}) - -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit() - } -}) diff --git a/Yi.Ai.FileAssistant/ui/package.json b/Yi.Ai.FileAssistant/ui/package.json deleted file mode 100644 index fefeb291..00000000 --- a/Yi.Ai.FileAssistant/ui/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "file-app", - "version": "1.0.0", - "main": "main.js", - "scripts": { - "start": "electron .", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "description": "", - "devDependencies": { - "electron": "^23.1.3" - } -} diff --git a/Yi.Ai.Vue3/package.json b/Yi.Ai.Vue3/package.json index 8591f58a..1e2ddcdc 100644 --- a/Yi.Ai.Vue3/package.json +++ b/Yi.Ai.Vue3/package.json @@ -37,6 +37,7 @@ "@jsonlee_12138/enum": "^1.0.4", "@vueuse/core": "^13.5.0", "@vueuse/integrations": "^13.5.0", + "date-fns": "^2.30.0", "driver.js": "^1.3.6", "echarts": "^6.0.0", "element-plus": "^2.10.4", diff --git a/Yi.Ai.Vue3/pnpm-lock.yaml b/Yi.Ai.Vue3/pnpm-lock.yaml index 8d3ac5a4..3ae52c43 100644 --- a/Yi.Ai.Vue3/pnpm-lock.yaml +++ b/Yi.Ai.Vue3/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@vueuse/integrations': specifier: ^13.5.0 version: 13.5.0(async-validator@4.2.5)(nprogress@0.2.0)(qrcode@1.5.4)(vue@3.5.17(typescript@5.8.3)) + date-fns: + specifier: ^2.30.0 + version: 2.30.0 driver.js: specifier: ^1.3.6 version: 1.3.6 @@ -1774,6 +1777,10 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} @@ -6659,6 +6666,10 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.27.6 + dayjs@1.11.13: {} de-indent@1.0.2: {} diff --git a/Yi.Ai.Vue3/src/api/user/index.ts b/Yi.Ai.Vue3/src/api/user/index.ts index 9024ef06..a29a3931 100644 --- a/Yi.Ai.Vue3/src/api/user/index.ts +++ b/Yi.Ai.Vue3/src/api/user/index.ts @@ -2,12 +2,12 @@ import { get, post } from '@/utils/request'; // 获取用户信息 export function getUserInfo() { - return get('/ai-chat/account').json(); + return get('/account/ai').json(); } // 获取二维码 LoginOrRegister 登录注册, Bind 绑定 export function getQrCode(data: any) { - return post('/fuwuhao/qrcode', data).json(); + return post(`/fuwuhao/qrcode?sceneType=${data.sceneType}`, data).json(); } // 扫码轮询 diff --git a/Yi.Ai.Vue3/src/components/LoginDialog/components/QrCodeLogin/index.vue b/Yi.Ai.Vue3/src/components/LoginDialog/components/QrCodeLogin/index.vue index dcec87ca..9144d832 100644 --- a/Yi.Ai.Vue3/src/components/LoginDialog/components/QrCodeLogin/index.vue +++ b/Yi.Ai.Vue3/src/components/LoginDialog/components/QrCodeLogin/index.vue @@ -2,16 +2,32 @@ import { Check, Picture as IconPicture, Refresh } from '@element-plus/icons-vue'; import { useCountdown } from '@vueuse/core'; import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'; -import { getQrCode, getQrCodeResult, getWechatAuth } from '@/api'; +import { useRouter } from 'vue-router'; +import { getQrCode, getQrCodeResult, getUserInfo } from '@/api'; +import { useUserStore } from '@/stores'; +import { useSessionStore } from '@/stores/modules/session.ts'; +import { WECHAT_QRCODE_TYPE } from '@/utils/user.ts'; +const props = defineProps({ + type: { + type: String, + default: WECHAT_QRCODE_TYPE.LoginOrRegister, + }, +}); +const emit = defineEmits(['bind-wechat']); +const QrCodeType = props.type || WECHAT_QRCODE_TYPE.LoginOrRegister; // 响应式状态 // const urlText = shallowRef(''); const qrCodeUrl = ref(''); const isExpired = ref(false); const isScanned = ref(false); const isConfirming = ref(false); +const isAuthorization = ref(false); const confirmCountdownSeconds = shallowRef(180); const sceneStr = ref(''); // 场景值,用于标识二维码 +const userStore = useUserStore(); +const router = useRouter(); +const sessionStore = useSessionStore(); // 二维码倒计时实例 const { start: qrStart, stop: qrStop } = useCountdown(shallowRef(600), { @@ -39,18 +55,9 @@ let statusPolling: number | null = null; async function fetchQRCodeInfo() { try { const param = { - sceneType: 'LoginOrRegister', + sceneType: QrCodeType, }; const response = await getQrCode(param); - console.log('response---', response); - // { - // "code": 200, - // "data": { - // "qrCodeUrl": "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQG17zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAySnR6NzBJcGxhTC0xQmR4TXhFY1UAAgT1XrBoAwRYAgAA", - // "scene": "8baf623f79dc452880c6832415deb26a" - // }, - // "msg": "success" - // } if (response && response.data.qrCodeUrl && response.data.scene) { qrCodeUrl.value = response.data.qrCodeUrl; sceneStr.value = response.data.scene; @@ -68,30 +75,27 @@ async function fetchQRCodeInfo() { async function checkQRCodeStatus() { if (!sceneStr.value) return; - try { - console.log('startStatusPolling---'); const param = { scene: sceneStr.value, }; const response = await getQrCodeResult(param); - console.log('response.data.sceneResult---', response.data); - console.log('response.data.sceneResult---', response.data.sceneResult); + switch (response.data.sceneResult) { case 'Wait': // Wait // 继续等待 break; case 'Login': // Login // 登录成功 - handleLoginSuccess(response.data.token, response.refreshToken); + await handleLoginSuccess(response.data.token, response.data.refreshToken); break; case 'Register': // Register // 需要注册 handleRegister(); break; case 'Bind': // Bind - // 需要绑定 - handleBind(response.data.token); + // 绑定成功 + handleBind(); break; case 'Expired': // Expired // 二维码过期 @@ -111,65 +115,35 @@ async function checkQRCodeStatus() { } // 处理登录成功 -function handleLoginSuccess(token: string, refreshToken: string) { +async function handleLoginSuccess(token: string, refreshToken: string) { // 停止轮询 stopPolling(); - - // 存储token - localStorage.setItem('access_token', token); - localStorage.setItem('refresh_token', refreshToken || ''); - + userStore.setToken(token, refreshToken); + const resUserInfo = await getUserInfo(); + userStore.setUserInfo(resUserInfo.data); // 提示用户 ElMessage.success('登录成功'); - // 刷新页面或跳转到首页 - setTimeout(() => { - window.location.reload(); - }, 1000); + await router.replace('/'); + await sessionStore.requestSessionList(1, true); + userStore.closeLoginDialog(); } // 处理注册授权 function handleRegister() { - console.log('需要注册授权'); ElMessage.info('请在微信授权'); - // const appId = 'wx373eb3ecd65bdac4'; - // const redirectUri = encodeURIComponent('http://localhost:17001/wechat/callback'); - // const state = 'register'; // 用于防止CSRF,可根据业务生成随机字符串 - - // scope 可以选 snsapi_base(静默,只拿 openid) 或 snsapi_userinfo(需要用户确认,可拿昵称头像) - // const scope = 'snsapi_userinfo'; - - // 拼接授权链接 - // const wechatAuthUrl - // = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}` - // + `&redirect_uri=${redirectUri}` - // + `&response_type=code&scope=${scope}&state=${state}#wechat_redirect`; - // console.log('wechatAuthUrl---', wechatAuthUrl); - // 打开授权页(推荐用 location.href 替换,直接跳转) - // window.location.href = wechatAuthUrl; } // 处理绑定 -function handleBind(token: string) { +async function handleBind() { + // 停止轮询 + stopPolling(); // 处理账号绑定逻辑 - console.log('需要绑定,临时token:', token); - ElMessage.info('请绑定您的账号'); -} - -// 完成注册 -async function completeRegistration() { - try { - - // 调用后端API完成注册 - // const response = await completeRegister({ token }); - // ElMessage.success('注册成功,正在登录...'); - // 模拟登录成功 - // handleLoginSuccess(); - } - catch (error) { - console.error('注册失败:', error); - ElMessage.error('注册失败,请重试'); - } + ElMessage.success('微信绑定成功'); + const resUserInfo = await getUserInfo(); + userStore.setUserInfo(resUserInfo.data); + // 调用父组件方法 + emit('bind-wechat'); } // 更新UI状态 @@ -180,11 +154,14 @@ function updateUIStatus(status: string) { isConfirming.value = false; break; case 'Login': // Login - 已扫码并确认 - case 'Register': // Register - 已扫码并确认 case 'Bind': // Bind - 已扫码并确认 isScanned.value = true; isConfirming.value = false; break; + case 'Register': // Register - 已扫码并确认 + isScanned.value = true; + isAuthorization.value = true; + break; case 'Expired': // Expired isExpired.value = true; isScanned.value = false; @@ -197,29 +174,6 @@ function updateUIStatus(status: string) { } } -// 在微信授权页面获取到微信code后发给服务端 -async function handleWechatAuth(code: string) { - try { - const param = { - code, - }; - const response = await getWechatAuth(param); - - if (response.success) { - // 授权成功,可以获取用户信息或完成登录 - console.log('微信授权成功', response); - } - else { - console.error('微信授权失败:', response.message); - ElMessage.error(`微信授权失败: ${response.message}`); - } - } - catch (error) { - console.error('处理微信授权失败:', error); - ElMessage.error('处理微信授权时出错'); - } -} - /** 停止所有轮询 */ function stopPolling() { if (statusPolling) { @@ -247,7 +201,6 @@ async function handleRefresh() { /** 启动状态轮询 */ function startStatusPolling() { - console.log('1111----'); stopPolling(); // 先停止之前的轮询 statusPolling = setInterval(async () => { @@ -260,7 +213,6 @@ function startStatusPolling() { /** 组件初始化 */ onMounted(async () => { const success = await fetchQRCodeInfo(); - console.log('qrCodeUrl---', success); if (success) { qrStart(); startStatusPolling(); @@ -277,23 +229,15 @@ onBeforeUnmount(() => { stopPolling(); }); -// 监听URL参数中的微信code(适用于微信授权回调) onMounted(() => { - const urlParams = new URLSearchParams(window.location.search); - const code = urlParams.get('code'); - const state = urlParams.get('state'); - if (code) { - // 处理微信授权回调 - handleWechatAuth(code); - } }); 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 a36f3258..2240d577 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -58,9 +58,10 @@ const popoverList = ref([ const dialogVisible = ref(false); const navItems = [ - // { name: 'user', label: '用户管理', icon: 'User' }, + { name: 'user', label: '用户信息', icon: 'User' }, // { name: 'role', label: '角色管理', icon: 'Avatar' }, // { name: 'permission', label: '权限管理', icon: 'Key' }, + // { name: 'userInfo', label: '用户信息', icon: 'User' }, { name: 'apiKey', label: 'API密钥', icon: 'Key' }, { name: 'rechargeLog', label: '充值记录', icon: 'Document' }, { name: 'usageStatistics', label: '用量统计', icon: 'Histogram' }, diff --git a/Yi.Ai.Vue3/src/utils/user.ts b/Yi.Ai.Vue3/src/utils/user.ts index 27f82016..1fd5f257 100644 --- a/Yi.Ai.Vue3/src/utils/user.ts +++ b/Yi.Ai.Vue3/src/utils/user.ts @@ -18,3 +18,10 @@ export function getUserProfilePicture(): string { // 系统头像(可以常量) export const systemProfilePicture = `/images/logo.png`; + +// 获取微信二维码类型 +// 获取二维码 LoginOrRegister 登录注册, Bind 绑定 +export const WECHAT_QRCODE_TYPE = { + LoginOrRegister: 'LoginOrRegister', + Bind: 'Bind', +}; diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 3b898408..ef586459 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -19,6 +19,8 @@ declare module 'vue' { ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElContainer: typeof import('element-plus/es')['ElContainer'] + 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'] ElEmpty: typeof import('element-plus/es')['ElEmpty']