From 4c3864f66503f31bf3618b1cbeaa2694587639d9 Mon Sep 17 00:00:00 2001 From: Gavan <994259213@qq.com> Date: Mon, 21 Jul 2025 18:06:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/api-templates/http-client.ejs | 4 +- ui/src/api/UserDashboard.ts | 110 +++++++++++++ ui/src/api/UserManage.ts | 72 +++++++++ ui/src/api/UserRecord.ts | 148 ++++++++++++++++++ ui/src/api/httpClient.ts | 4 +- ui/src/api/index.ts | 3 + ui/src/api/types.ts | 110 ++++++++++++- ui/src/assets/fonts/iconfont.js | 2 +- ui/src/components/sidebar/index.tsx | 7 + ui/src/pages/auth/index.tsx | 2 +- ui/src/pages/user/chat/chatDetailModal.tsx | 4 +- ui/src/pages/user/chat/index.tsx | 4 +- .../user/completion/completionDetailModal.tsx | 4 +- ui/src/pages/user/completion/index.tsx | 19 +-- .../dashboard/components/memberStatistic.tsx | 35 ++--- ui/src/pages/user/dashboard/index.tsx | 16 +- ui/src/pages/user/login/index.tsx | 53 +++---- ui/src/utils/index.ts | 11 +- 18 files changed, 508 insertions(+), 100 deletions(-) create mode 100644 ui/src/api/UserDashboard.ts create mode 100644 ui/src/api/UserManage.ts create mode 100644 ui/src/api/UserRecord.ts diff --git a/ui/api-templates/http-client.ejs b/ui/api-templates/http-client.ejs index 7164e22..b7046a9 100644 --- a/ui/api-templates/http-client.ejs +++ b/ui/api-templates/http-client.ejs @@ -40,7 +40,9 @@ export enum ContentType { const redirectToLogin = () => { const redirectAfterLogin = encodeURIComponent(location.href); const search = `redirect=${redirectAfterLogin}`; - const pathname = '/login'; + const pathname = location.pathname.startsWith('/user') + ? '/user/login' + : '/login'; window.location.href = `${pathname}?${search}`; }; diff --git a/ui/src/api/UserDashboard.ts b/ui/src/api/UserDashboard.ts new file mode 100644 index 0000000..a79d338 --- /dev/null +++ b/ui/src/api/UserDashboard.ts @@ -0,0 +1,110 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import request, { ContentType, RequestParams } from "./httpClient"; +import { + DomainUserEvent, + DomainUserHeatmapResp, + DomainUserStat, + GetUserDashboardEventsParams, + GetUserDashboardStatParams, + WebResp, +} from "./types"; + +/** + * @description 获取用户事件 + * + * @tags User Dashboard + * @name GetUserDashboardEvents + * @summary 获取用户事件 + * @request GET:/api/v1/user/dashboard/events + * @response `200` `(WebResp & { + data?: (DomainUserEvent)[], + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserDashboardEvents = ( + query: GetUserDashboardEventsParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainUserEvent[]; + } + >({ + path: `/api/v1/user/dashboard/events`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 用户热力图 + * + * @tags User Dashboard + * @name GetUserDashboardHeatmap + * @summary 用户热力图 + * @request GET:/api/v1/user/dashboard/heatmap + * @response `200` `(WebResp & { + data?: DomainUserHeatmapResp, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserDashboardHeatmap = (params: RequestParams = {}) => + request< + WebResp & { + data?: DomainUserHeatmapResp; + } + >({ + path: `/api/v1/user/dashboard/heatmap`, + method: "GET", + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 获取用户统计信息 + * + * @tags User Dashboard + * @name GetUserDashboardStat + * @summary 获取用户统计信息 + * @request GET:/api/v1/user/dashboard/stat + * @response `200` `(WebResp & { + data?: DomainUserStat, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserDashboardStat = ( + query: GetUserDashboardStatParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainUserStat; + } + >({ + path: `/api/v1/user/dashboard/stat`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); diff --git a/ui/src/api/UserManage.ts b/ui/src/api/UserManage.ts new file mode 100644 index 0000000..1e9473d --- /dev/null +++ b/ui/src/api/UserManage.ts @@ -0,0 +1,72 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import request, { ContentType, RequestParams } from "./httpClient"; +import { DomainProfileUpdateReq, DomainUser, WebResp } from "./types"; + +/** + * @description 获取用户信息 + * + * @tags User Manage + * @name GetUserProfile + * @summary 获取用户信息 + * @request GET:/api/v1/user/profile + * @response `200` `(WebResp & { + data?: DomainUser, + +})` OK + * @response `401` `WebResp` Unauthorized + */ + +export const getUserProfile = (params: RequestParams = {}) => + request< + WebResp & { + data?: DomainUser; + } + >({ + path: `/api/v1/user/profile`, + method: "GET", + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 更新用户信息 + * + * @tags User Manage + * @name PutUserUpdateProfile + * @summary 更新用户信息 + * @request PUT:/api/v1/user/profile + * @response `200` `(WebResp & { + data?: DomainUser, + +})` OK + * @response `401` `WebResp` Unauthorized + */ + +export const putUserUpdateProfile = ( + req: DomainProfileUpdateReq, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainUser; + } + >({ + path: `/api/v1/user/profile`, + method: "PUT", + body: req, + type: ContentType.Json, + format: "json", + ...params, + }); diff --git a/ui/src/api/UserRecord.ts b/ui/src/api/UserRecord.ts new file mode 100644 index 0000000..cf1e678 --- /dev/null +++ b/ui/src/api/UserRecord.ts @@ -0,0 +1,148 @@ +/* eslint-disable */ +/* tslint:disable */ +// @ts-nocheck +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import request, { ContentType, RequestParams } from "./httpClient"; +import { + DomainChatInfo, + DomainCompletionInfo, + DomainListChatRecordResp, + DomainListCompletionRecordResp, + GetUserChatInfoParams, + GetUserCompletionInfoParams, + GetUserListChatRecordParams, + GetUserListCompletionRecordParams, + WebResp, +} from "./types"; + +/** + * @description 获取对话内容 + * + * @tags User Record + * @name GetUserChatInfo + * @summary 获取对话内容 + * @request GET:/api/v1/user/chat/info + * @response `200` `(WebResp & { + data?: DomainChatInfo, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserChatInfo = ( + query: GetUserChatInfoParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainChatInfo; + } + >({ + path: `/api/v1/user/chat/info`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 获取用户对话记录 + * + * @tags User Record + * @name GetUserListChatRecord + * @summary 获取用户对话记录 + * @request GET:/api/v1/user/chat/record + * @response `200` `(WebResp & { + data?: DomainListChatRecordResp, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserListChatRecord = ( + query: GetUserListChatRecordParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainListChatRecordResp; + } + >({ + path: `/api/v1/user/chat/record`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 获取补全内容 + * + * @tags User Record + * @name GetUserCompletionInfo + * @summary 获取补全内容 + * @request GET:/api/v1/user/completion/info + * @response `200` `(WebResp & { + data?: DomainCompletionInfo, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserCompletionInfo = ( + query: GetUserCompletionInfoParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainCompletionInfo; + } + >({ + path: `/api/v1/user/completion/info`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); + +/** + * @description 获取补全记录 + * + * @tags User Record + * @name GetUserListCompletionRecord + * @summary 获取补全记录 + * @request GET:/api/v1/user/completion/record + * @response `200` `(WebResp & { + data?: DomainListCompletionRecordResp, + +})` OK + * @response `401` `string` Unauthorized + */ + +export const getUserListCompletionRecord = ( + query: GetUserListCompletionRecordParams, + params: RequestParams = {}, +) => + request< + WebResp & { + data?: DomainListCompletionRecordResp; + } + >({ + path: `/api/v1/user/completion/record`, + method: "GET", + query: query, + type: ContentType.Json, + format: "json", + ...params, + }); diff --git a/ui/src/api/httpClient.ts b/ui/src/api/httpClient.ts index 227c591..8042895 100644 --- a/ui/src/api/httpClient.ts +++ b/ui/src/api/httpClient.ts @@ -61,7 +61,9 @@ export enum ContentType { const redirectToLogin = () => { const redirectAfterLogin = encodeURIComponent(location.href); const search = `redirect=${redirectAfterLogin}`; - const pathname = "/login"; + const pathname = location.pathname.startsWith("/user") + ? "/user/login" + : "/login"; window.location.href = `${pathname}?${search}`; }; diff --git a/ui/src/api/index.ts b/ui/src/api/index.ts index c9ce7b6..424477f 100644 --- a/ui/src/api/index.ts +++ b/ui/src/api/index.ts @@ -4,5 +4,8 @@ export * from './Dashboard' export * from './Model' export * from './OpenAiv1' export * from './User' +export * from './UserDashboard' +export * from './UserManage' +export * from './UserRecord' export * from './types' diff --git a/ui/src/api/types.ts b/ui/src/api/types.ts index d4e769e..20df103 100644 --- a/ui/src/api/types.ts +++ b/ui/src/api/types.ts @@ -54,6 +54,11 @@ export enum ConstsModelProvider { ModelProviderVolcengine = "Volcengine", } +export enum ConstsLoginSource { + LoginSourcePlugin = "plugin", + LoginSourceBrowser = "browser", +} + export enum ConstsChatRole { ChatRoleUser = "user", ChatRoleAssistant = "assistant", @@ -360,8 +365,10 @@ export interface DomainListUserResp { export interface DomainLoginReq { /** 密码 */ password?: string; - /** 会话Id */ + /** 会话Id插件登录时必填 */ session_id?: string; + /** 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin */ + source?: ConstsLoginSource; /** 用户名 */ username?: string; } @@ -369,6 +376,8 @@ export interface DomainLoginReq { export interface DomainLoginResp { /** 重定向URL */ redirect_url?: string; + /** 用户信息 */ + user?: DomainUser; } export interface DomainModel { @@ -465,6 +474,17 @@ export interface DomainOAuthURLResp { url?: string; } +export interface DomainProfileUpdateReq { + /** 头像 */ + avatar?: string; + /** 旧密码 */ + old_password?: string; + /** 密码 */ + password?: string; + /** 用户名 */ + username?: string; +} + export interface DomainProviderModel { /** 模型列表 */ models?: DomainModelBasic[]; @@ -727,13 +747,13 @@ export interface DomainUserStat { }[]; /** 编程语言占比 */ program_language?: DomainCategoryPoint[]; - /** 近90天总接受率 */ + /** 总接受率 */ total_accepted_per?: number; - /** 近90天总对话任务数 */ + /** 总对话任务数 */ total_chats?: number; - /** 近90天总补全任务数 */ + /** 总补全任务数 */ total_completions?: number; - /** 近90天总代码行数 */ + /** 总代码行数 */ total_lines_of_code?: number; /** 工作模式占比 */ work_mode?: DomainCategoryPoint[]; @@ -935,6 +955,84 @@ export interface GetGetTokenUsageParams { model_type: "llm" | "coder" | "embedding" | "audio" | "reranker"; } +export interface GetUserChatInfoParams { + /** 对话记录ID */ + id: string; +} + +export interface GetUserListChatRecordParams { + /** 作者 */ + author?: string; + /** 是否接受筛选 */ + is_accept?: boolean; + /** 语言 */ + language?: string; + /** 下一页标识 */ + next_token?: string; + /** 分页 */ + page?: number; + /** 每页多少条记录 */ + size?: number; + /** 工作模式 */ + work_mode?: string; +} + +export interface GetUserCompletionInfoParams { + /** 补全记录ID */ + id: string; +} + +export interface GetUserListCompletionRecordParams { + /** 作者 */ + author?: string; + /** 是否接受筛选 */ + is_accept?: boolean; + /** 语言 */ + language?: string; + /** 下一页标识 */ + next_token?: string; + /** 分页 */ + page?: number; + /** 每页多少条记录 */ + size?: number; + /** 工作模式 */ + work_mode?: string; +} + +export interface GetUserDashboardEventsParams { + /** + * 持续时间 (小时或天数)` + * @min 24 + * @max 90 + * @default 90 + */ + duration?: number; + /** + * 精度: "hour", "day" + * @default "day" + */ + precision: "hour" | "day"; + /** 用户ID,可选参数 */ + user_id?: string; +} + +export interface GetUserDashboardStatParams { + /** + * 持续时间 (小时或天数)` + * @min 24 + * @max 90 + * @default 90 + */ + duration?: number; + /** + * 精度: "hour", "day" + * @default "day" + */ + precision: "hour" | "day"; + /** 用户ID,可选参数 */ + user_id?: string; +} + export interface DeleteDeleteUserParams { /** 用户ID */ id: string; @@ -972,4 +1070,6 @@ export interface GetUserOauthSignupOrInParams { redirect_url?: string; /** 会话ID */ session_id?: string; + /** 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin */ + source?: "plugin" | "browser"; } diff --git a/ui/src/assets/fonts/iconfont.js b/ui/src/assets/fonts/iconfont.js index abc174c..8c442a5 100644 --- a/ui/src/assets/fonts/iconfont.js +++ b/ui/src/assets/fonts/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,e,m=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?m(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=l.document,e=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){e||(e=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}d()}})(window); \ No newline at end of file +window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,m,e=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?e(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=l.document,m=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){m||(m=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}d()}})(window); \ No newline at end of file diff --git a/ui/src/components/sidebar/index.tsx b/ui/src/components/sidebar/index.tsx index 98ccd9d..afe9148 100644 --- a/ui/src/components/sidebar/index.tsx +++ b/ui/src/components/sidebar/index.tsx @@ -81,6 +81,13 @@ const USER_MENUS = [ icon: 'icon-buquanjilu', show: true, }, + // { + // label: '设置', + // value: '/user/setting', + // pathname: '/user/setting', + // icon: 'icon-setting', + // show: true, + // }, ]; const SidebarButton = styled(Button)(({ theme }) => ({ diff --git a/ui/src/pages/auth/index.tsx b/ui/src/pages/auth/index.tsx index e611418..ab1ab7b 100644 --- a/ui/src/pages/auth/index.tsx +++ b/ui/src/pages/auth/index.tsx @@ -27,7 +27,7 @@ import { getGetSetting } from '@/api/Admin'; import { useForm, Controller } from 'react-hook-form'; import { styled } from '@mui/material/styles'; import { useRequest } from 'ahooks'; -import { DomainSetting } from '@/api/types'; +import { DomainSetting, ConstsLoginSource } from '@/api/types'; // 样式化组件 const StyledContainer = styled(Container)(({ theme }) => ({ diff --git a/ui/src/pages/user/chat/chatDetailModal.tsx b/ui/src/pages/user/chat/chatDetailModal.tsx index 6e61428..8b2bb2d 100644 --- a/ui/src/pages/user/chat/chatDetailModal.tsx +++ b/ui/src/pages/user/chat/chatDetailModal.tsx @@ -1,6 +1,6 @@ import Avatar from '@/components/avatar'; import Card from '@/components/card'; -import { getChatInfo } from '@/api/Billing'; +import { getUserChatInfo } from '@/api/UserRecord'; import MarkDown from '@/components/markDown'; import { Ellipsis, Modal } from '@c-x/ui'; import { styled } from '@mui/material'; @@ -78,7 +78,7 @@ const ChatDetailModal = ({ const getChatDetailModal = () => { if (!data) return; - getChatInfo({ id: data.id! }).then((res) => { + getUserChatInfo({ id: data.id! }).then((res) => { setContent(res.contents || []); }); }; diff --git a/ui/src/pages/user/chat/index.tsx b/ui/src/pages/user/chat/index.tsx index 2df2649..7b1899f 100644 --- a/ui/src/pages/user/chat/index.tsx +++ b/ui/src/pages/user/chat/index.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { Table } from '@c-x/ui'; -import { getListChatRecord } from '@/api/Billing'; +import { getUserListChatRecord } from '@/api/UserRecord'; import dayjs from 'dayjs'; import Card from '@/components/card'; @@ -24,7 +24,7 @@ const Chat = () => { >(); const fetchData = async () => { setLoading(true); - const res = await getListChatRecord({ + const res = await getUserListChatRecord({ page: page, size: size, }); diff --git a/ui/src/pages/user/completion/completionDetailModal.tsx b/ui/src/pages/user/completion/completionDetailModal.tsx index a1fa4f8..72026d4 100644 --- a/ui/src/pages/user/completion/completionDetailModal.tsx +++ b/ui/src/pages/user/completion/completionDetailModal.tsx @@ -1,5 +1,5 @@ import Card from '@/components/card'; -import { getCompletionInfo } from '@/api/Billing'; +import { getUserCompletionInfo } from '@/api/UserRecord'; import { Modal } from '@c-x/ui'; import MonacoEditor from '@monaco-editor/react'; @@ -32,7 +32,7 @@ const ChatDetailModal = ({ const getChatDetailModal = () => { if (!data) return; - getCompletionInfo({ id: data.id! }).then((res) => { + getUserCompletionInfo({ id: data.id! }).then((res) => { // 先去除 <|im_start|> 和 <|im_end|> 及其间内容 const rawPrompt = removeImBlocks(res.prompt || ''); const content = res.content || ''; diff --git a/ui/src/pages/user/completion/index.tsx b/ui/src/pages/user/completion/index.tsx index 79a4882..f9fc8ca 100644 --- a/ui/src/pages/user/completion/index.tsx +++ b/ui/src/pages/user/completion/index.tsx @@ -1,7 +1,6 @@ import { useState, useEffect } from 'react'; import { DomainCompletionRecord, DomainUser } from '@/api/types'; -import { getListCompletionRecord } from '@/api/Billing'; -import { useRequest } from 'ahooks'; +import { getUserListCompletionRecord } from '@/api/UserRecord'; import { Table } from '@c-x/ui'; import Card from '@/components/card'; import { @@ -14,7 +13,6 @@ import { Autocomplete, TextField, } from '@mui/material'; -import { getListUser } from '@/api/User'; import dayjs from 'dayjs'; import { useDebounceFn } from 'ahooks'; import { ColumnsType } from '@c-x/ui/dist/Table'; @@ -34,29 +32,19 @@ const Completion = () => { DomainCompletionRecord | undefined >(); - // 新增筛选项 state - const [filterUser, setFilterUser] = useState(''); const [filterLang, setFilterLang] = useState(''); const [filterAccept, setFilterAccept] = useState< 'accepted' | 'unaccepted' | '' >('accepted'); - const { data: userOptions = { users: [] } } = useRequest(() => - getListUser({ - page: 1, - size: 9999, - }) - ); - useEffect(() => { setPage(1); // 筛选变化时重置页码 fetchData({ page: 1, language: filterLang, - author: filterUser, is_accept: filterAccept, }); - }, [filterUser, filterLang, filterAccept]); + }, [filterLang, filterAccept]); const fetchData = async (params: { page?: number; @@ -67,11 +55,10 @@ const Completion = () => { }) => { setLoading(true); const isAccept = params.is_accept || filterAccept; - const res = await getListCompletionRecord({ + const res = await getUserListCompletionRecord({ page: params.page || page, size: params.size || size, language: params.language || filterLang, - author: params.author || filterUser, is_accept: isAccept === 'accepted' ? true diff --git a/ui/src/pages/user/dashboard/components/memberStatistic.tsx b/ui/src/pages/user/dashboard/components/memberStatistic.tsx index 087c285..7b8a06c 100644 --- a/ui/src/pages/user/dashboard/components/memberStatistic.tsx +++ b/ui/src/pages/user/dashboard/components/memberStatistic.tsx @@ -1,16 +1,15 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Grid2 as Grid } from '@mui/material'; -import { useParams } from 'react-router-dom'; import MemberInfo from '@/pages/dashboard/components/memberInfo'; import PieCharts from '@/pages/dashboard/components/pieCharts'; import LineCharts from '@/pages/dashboard/components/lineCharts'; import { RecentActivityCard } from '@/pages/dashboard/components/statisticCard'; import { useRequest } from 'ahooks'; import { - getUserEventsDashboard, - getUserStatDashboard, - getUserHeatmapDashboard, -} from '@/api/Dashboard'; + getUserDashboardEvents, + getUserDashboardHeatmap, + getUserDashboardStat, +} from '@/api/UserDashboard'; import { StyledHighlight } from '@/pages/dashboard/components/globalStatistic'; import { getRecent90DaysData, getRecent24HoursData } from '@/utils'; import { DomainUser } from '@/api/types'; @@ -33,42 +32,28 @@ const MemberStatistic = ({ precision: timeRange === '90d' ? 'day' : 'hour', }); - const { id } = useParams(); const { data: userEvents } = useRequest( () => - getUserEventsDashboard({ - user_id: id || '', + getUserDashboardEvents({ precision: timeDuration.precision, }), { - refreshDeps: [id], manual: false, - ready: !!id, } ); const { data: userStat } = useRequest( () => - getUserStatDashboard({ - user_id: id || '', + getUserDashboardStat({ ...timeDuration, }), { - refreshDeps: [id, timeDuration], + refreshDeps: [timeDuration], manual: false, - ready: !!id, - } - ); - const { data: userHeatmap } = useRequest( - () => - getUserHeatmapDashboard({ - user_id: id || '', - }), - { - refreshDeps: [id], - manual: false, - ready: !!id, } ); + const { data: userHeatmap } = useRequest(getUserDashboardHeatmap, { + manual: false, + }); useEffect(() => { setTimeDuration({ diff --git a/ui/src/pages/user/dashboard/index.tsx b/ui/src/pages/user/dashboard/index.tsx index 6c154bd..3d5171f 100644 --- a/ui/src/pages/user/dashboard/index.tsx +++ b/ui/src/pages/user/dashboard/index.tsx @@ -1,22 +1,16 @@ -import { useEffect, useMemo, useState } from 'react'; -import { getListUser } from '@/api/User'; +import { useState } from 'react'; import { Stack, MenuItem, Select } from '@mui/material'; - +import { getUserProfile } from '@/api/UserManage'; import { useRequest } from 'ahooks'; import MemberStatistic from './components/memberStatistic'; -import { useParams } from 'react-router-dom'; -import { useNavigate } from 'react-router-dom'; -import { DomainUser } from '@/api/types'; export type TimeRange = '90d' | '24h'; const Dashboard = () => { - const navigate = useNavigate(); - const { tab, id } = useParams(); - const [tabValue, setTabValue] = useState(tab || 'global'); - const [memberData, setMemberData] = useState(null); const [timeRange, setTimeRange] = useState('24h'); + const { data: userData = {} } = useRequest(getUserProfile); + return ( @@ -32,7 +26,7 @@ const Dashboard = () => { - + ); }; diff --git a/ui/src/pages/user/login/index.tsx b/ui/src/pages/user/login/index.tsx index c47141d..88aba19 100644 --- a/ui/src/pages/user/login/index.tsx +++ b/ui/src/pages/user/login/index.tsx @@ -28,7 +28,7 @@ import { getGetSetting } from '@/api/Admin'; import { useForm, Controller } from 'react-hook-form'; import { styled } from '@mui/material/styles'; import { useRequest } from 'ahooks'; -import { DomainSetting } from '@/api/types'; +import { DomainSetting, ConstsLoginSource } from '@/api/types'; // 样式化组件 const StyledContainer = styled(Container)(({ theme }) => ({ @@ -132,36 +132,28 @@ const UserLogin = () => { }, []); // 处理登录表单提交 - const onSubmit = useCallback( - async (data: LoginFormData) => { - setLoading(true); - setError(null); + const onSubmit = useCallback(async (data: LoginFormData) => { + setLoading(true); + setError(null); - try { - const sessionId = searchParams.get('session_id'); - if (!sessionId) { - message.error('缺少会话ID参数'); - return; - } + try { + // 用户登录 + const loginResult = await postLogin({ + ...data, + source: ConstsLoginSource.LoginSourceBrowser, + }); - // 用户登录 - const loginResult = await postLogin({ - ...data, - session_id: sessionId, - }); - - window.location.href = loginResult.redirect_url!; - } catch (err) { - const errorMessage = - err instanceof Error ? err.message : '登录失败,请重试'; - setError(errorMessage); - console.error('登录失败:', err); - } finally { - setLoading(false); - } - }, - [searchParams] - ); + const redirectUrl = getRedirectUrl('user'); + window.location.href = redirectUrl.href; + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : '登录失败,请重试'; + setError(errorMessage); + console.error('登录失败:', err); + } finally { + setLoading(false); + } + }, []); // 初始化背景动画 useEffect(() => { @@ -266,10 +258,11 @@ const UserLogin = () => { ); const onOauthLogin = (platform: 'dingtalk' | 'custom') => { - const redirectUrl = getRedirectUrl(); + const redirectUrl = getRedirectUrl('user'); getUserOauthSignupOrIn({ platform, redirect_url: redirectUrl.href, + source: ConstsLoginSource.LoginSourceBrowser, }).then((res) => { if (res.url) { window.location.href = res.url; diff --git a/ui/src/utils/index.ts b/ui/src/utils/index.ts index 21d1e58..7dd2c10 100644 --- a/ui/src/utils/index.ts +++ b/ui/src/utils/index.ts @@ -131,9 +131,11 @@ export const isValidUrl = (url: string) => { return regex.test(url); }; -export const getRedirectUrl = () => { +export const getRedirectUrl = (source: 'user' | 'admin' = 'admin') => { const searchParams = new URLSearchParams(location.search); - const redirect = searchParams.get('redirect') || '/dashboard'; + const redirect = + searchParams.get('redirect') || + `${source === 'admin' ? '' : '/user'}/dashboard`; let redirectUrl: URL | null = null; try { redirectUrl = redirect ? new URL(decodeURIComponent(redirect)) : null; @@ -145,7 +147,10 @@ export const getRedirectUrl = () => { redirectUrl = isValidUrl(redirectUrl?.href || '') ? redirectUrl - : new URL('/dashboard', location.origin); + : new URL( + `${source === 'admin' ? '' : '/user'}/dashboard`, + location.origin + ); return redirectUrl as URL; };