mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-03 23:33:37 +08:00
@@ -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}`;
|
||||
};
|
||||
|
||||
|
||||
110
ui/src/api/UserDashboard.ts
Normal file
110
ui/src/api/UserDashboard.ts
Normal file
@@ -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,
|
||||
});
|
||||
72
ui/src/api/UserManage.ts
Normal file
72
ui/src/api/UserManage.ts
Normal file
@@ -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,
|
||||
});
|
||||
148
ui/src/api/UserRecord.ts
Normal file
148
ui/src/api/UserRecord.ts
Normal file
@@ -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,
|
||||
});
|
||||
@@ -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}`;
|
||||
};
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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 }) => ({
|
||||
|
||||
@@ -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 }) => ({
|
||||
|
||||
@@ -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 || []);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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 || '';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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<DomainUser | null>(null);
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>('24h');
|
||||
|
||||
const { data: userData = {} } = useRequest(getUserProfile);
|
||||
|
||||
return (
|
||||
<Stack gap={2} sx={{ height: '100%' }}>
|
||||
<Stack direction='row' justifyContent='space-between' alignItems='center'>
|
||||
@@ -32,7 +26,7 @@ const Dashboard = () => {
|
||||
</Select>
|
||||
</Stack>
|
||||
|
||||
<MemberStatistic memberData={memberData} timeRange={timeRange} />
|
||||
<MemberStatistic memberData={userData as any} timeRange={timeRange} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user