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;
};