mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-02 14:53:55 +08:00
feat: 添加钉钉登录
This commit is contained in:
@@ -1,64 +1,23 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const Axios = require('axios')
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
async function downloadFile(url) {
|
||||
try {
|
||||
const iconPath = path.resolve(__dirname, '../src/assets/fonts/iconfont.js')
|
||||
const iconDir = path.dirname(iconPath)
|
||||
|
||||
// 检查目录是否存在,如果不存在则创建
|
||||
if (!fs.existsSync(iconDir)) {
|
||||
console.log(`目录 ${iconDir} 不存在,正在创建...`)
|
||||
fs.mkdirSync(iconDir, { recursive: true })
|
||||
console.log('目录创建成功')
|
||||
}
|
||||
|
||||
console.log(`开始下载图标文件到: ${iconPath}`)
|
||||
|
||||
const writer = fs.createWriteStream(iconPath)
|
||||
const response = await Axios({
|
||||
url: `https:${url}`,
|
||||
method: 'GET',
|
||||
responseType: 'stream',
|
||||
timeout: 30000, // 30秒超时
|
||||
})
|
||||
|
||||
response.data.pipe(writer)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on('finish', () => {
|
||||
console.log('图标文件下载成功!')
|
||||
resolve()
|
||||
})
|
||||
writer.on('error', (err) => {
|
||||
console.error('写入文件时出错:', err.message)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('下载过程中出错:', error.message)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
const iconPath = path.resolve(__dirname, "../src/assets/fonts/iconfont.js");
|
||||
const iconDir = path.dirname(iconPath);
|
||||
|
||||
async function main() {
|
||||
const argument = process.argv.splice(2)
|
||||
|
||||
if (!argument[0]) {
|
||||
console.error('错误: 请提供下载URL作为参数')
|
||||
console.log('使用方法: node downLoadIcon.cjs <url>')
|
||||
process.exit(1)
|
||||
// 检查目录是否存在,不存在则创建
|
||||
if (!fs.existsSync(iconDir)) {
|
||||
fs.mkdirSync(iconDir, { recursive: true });
|
||||
console.log(`目录 ${iconDir} 已创建`);
|
||||
}
|
||||
|
||||
try {
|
||||
await downloadFile(argument[0])
|
||||
console.log('所有操作完成!')
|
||||
} catch (error) {
|
||||
console.error('脚本执行失败:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
const response = await fetch(`https:${url}`, {
|
||||
method: "GET",
|
||||
// responseType: "stream", // fetch 不支持此参数
|
||||
}).then((res) => res.text());
|
||||
fs.writeFileSync(iconPath, response);
|
||||
console.log("Download Icon Success");
|
||||
}
|
||||
let argument = process.argv.splice(2);
|
||||
downloadFile(argument[0]);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
DomainListUserResp,
|
||||
DomainLoginReq,
|
||||
DomainLoginResp,
|
||||
DomainOAuthURLResp,
|
||||
DomainRegisterReq,
|
||||
DomainSetting,
|
||||
DomainUpdateSettingReq,
|
||||
@@ -32,6 +33,8 @@ import {
|
||||
GetListAdminUserParams,
|
||||
GetListUserParams,
|
||||
GetLoginHistoryParams,
|
||||
GetUserOauthCallbackParams,
|
||||
GetUserOauthSignupOrInParams,
|
||||
WebResp,
|
||||
} from "./types";
|
||||
|
||||
@@ -401,6 +404,66 @@ export const getLoginHistory = (
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 用户 OAuth 回调
|
||||
*
|
||||
* @tags User
|
||||
* @name GetUserOauthCallback
|
||||
* @summary 用户 OAuth 回调
|
||||
* @request GET:/api/v1/user/oauth/callback
|
||||
* @response `200` `(WebResp & {
|
||||
data?: string,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getUserOauthCallback = (
|
||||
query: GetUserOauthCallbackParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: string;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/user/oauth/callback`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 用户 OAuth 登录或注册
|
||||
*
|
||||
* @tags User
|
||||
* @name GetUserOauthSignupOrIn
|
||||
* @summary 用户 OAuth 登录或注册
|
||||
* @request GET:/api/v1/user/oauth/signup-or-in
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainOAuthURLResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getUserOauthSignupOrIn = (
|
||||
query: GetUserOauthSignupOrInParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainOAuthURLResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/user/oauth/signup-or-in`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 注册用户
|
||||
*
|
||||
|
||||
@@ -16,6 +16,11 @@ export enum ConstsUserStatus {
|
||||
UserStatusLocked = "locked",
|
||||
}
|
||||
|
||||
export enum ConstsUserPlatform {
|
||||
UserPlatformEmail = "email",
|
||||
UserPlatformDingTalk = "dingtalk",
|
||||
}
|
||||
|
||||
export enum ConstsModelType {
|
||||
ModelTypeLLM = "llm",
|
||||
ModelTypeCoder = "coder",
|
||||
@@ -312,6 +317,10 @@ export interface DomainModelTokenUsageResp {
|
||||
total_output?: number;
|
||||
}
|
||||
|
||||
export interface DomainOAuthURLResp {
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface DomainProviderModel {
|
||||
/** 模型列表 */
|
||||
models?: DomainModelBasic[];
|
||||
@@ -333,6 +342,8 @@ export interface DomainSetting {
|
||||
created_at?: number;
|
||||
/** 是否禁用密码登录 */
|
||||
disable_password_login?: boolean;
|
||||
/** 是否开启钉钉OAuth */
|
||||
enable_dingtalk_oauth?: boolean;
|
||||
/** 是否开启SSO */
|
||||
enable_sso?: boolean;
|
||||
/** 是否强制两步验证 */
|
||||
@@ -419,8 +430,14 @@ export interface DomainUpdateModelReq {
|
||||
}
|
||||
|
||||
export interface DomainUpdateSettingReq {
|
||||
/** 钉钉客户端ID */
|
||||
dingtalk_client_id?: string;
|
||||
/** 钉钉客户端密钥 */
|
||||
dingtalk_client_secret?: string;
|
||||
/** 是否禁用密码登录 */
|
||||
disable_password_login?: boolean;
|
||||
/** 是否开启钉钉OAuth */
|
||||
enable_dingtalk_oauth?: boolean;
|
||||
/** 是否开启SSO */
|
||||
enable_sso?: boolean;
|
||||
/** 是否强制两步验证 */
|
||||
@@ -637,3 +654,17 @@ export interface GetLoginHistoryParams {
|
||||
/** 每页多少条记录 */
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface GetUserOauthCallbackParams {
|
||||
code: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
export interface GetUserOauthSignupOrInParams {
|
||||
/** 第三方平台 dingtalk */
|
||||
platform: "email" | "dingtalk";
|
||||
/** 登录成功后跳转的 URL */
|
||||
redirect_url?: string;
|
||||
/** 会话ID */
|
||||
session_id?: string;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,11 +1,10 @@
|
||||
'use client';
|
||||
import { styled, FormLabel } from '@mui/material';
|
||||
|
||||
export const StyledFormLabel = styled(FormLabel)(({ theme }) => ({
|
||||
display: 'block',
|
||||
color: theme.vars.palette.text.primary,
|
||||
fontSize: 16,
|
||||
fontWeight: 500,
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
marginBottom: theme.spacing(1),
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
fontSize: 14,
|
||||
|
||||
@@ -97,6 +97,10 @@ const MarkDown = ({
|
||||
|
||||
const answer = processContent(content);
|
||||
|
||||
console.log(answer);
|
||||
|
||||
console.log(content);
|
||||
|
||||
if (content.length === 0) return null;
|
||||
|
||||
return (
|
||||
@@ -446,12 +450,13 @@ const MarkDown = ({
|
||||
...rest
|
||||
}: React.HTMLAttributes<HTMLElement>) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
console.log(children, rest);
|
||||
return match ? (
|
||||
<SyntaxHighlighter
|
||||
showLineNumbers
|
||||
{...rest}
|
||||
language={match[1] || 'bash'}
|
||||
style={github}
|
||||
style={anOldHope}
|
||||
onClick={() => {
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(
|
||||
|
||||
@@ -12,17 +12,20 @@ import {
|
||||
Grid2 as Grid,
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
Divider,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import { Icon } from '@c-x/ui';
|
||||
import { Icon, message } from '@c-x/ui';
|
||||
|
||||
// @ts-ignore
|
||||
import { AestheticFluidBg } from '@/assets/jsm/AestheticFluidBg.module.js';
|
||||
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { postLogin } from '@/api/User';
|
||||
import { postLogin, getUserOauthSignupOrIn, getGetSetting } from '@/api/User';
|
||||
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { useRequest } from 'ahooks';
|
||||
|
||||
// 样式化组件
|
||||
const StyledContainer = styled(Container)(({ theme }) => ({
|
||||
@@ -111,6 +114,7 @@ const AuthPage = () => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const { data: loginSetting = {} } = useRequest(getGetSetting);
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -132,7 +136,8 @@ const AuthPage = () => {
|
||||
try {
|
||||
const sessionId = searchParams.get('session_id');
|
||||
if (!sessionId) {
|
||||
throw new Error('缺少会话ID参数');
|
||||
message.error('缺少会话ID参数');
|
||||
return;
|
||||
}
|
||||
|
||||
// 用户登录
|
||||
@@ -245,17 +250,6 @@ const AuthPage = () => {
|
||||
/>
|
||||
);
|
||||
|
||||
// 渲染错误提示
|
||||
const renderErrorAlert = () => {
|
||||
if (!error) return null;
|
||||
|
||||
return (
|
||||
<Grid size={12}>
|
||||
<Alert severity='error'>{error}</Alert>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染登录按钮
|
||||
const renderLoginButton = () => (
|
||||
<Grid size={12}>
|
||||
@@ -271,6 +265,32 @@ const AuthPage = () => {
|
||||
</Grid>
|
||||
);
|
||||
|
||||
const onDingdingLogin = () => {
|
||||
getUserOauthSignupOrIn({
|
||||
platform: 'dingtalk',
|
||||
redirect_url: window.location.origin + window.location.pathname,
|
||||
// @ts-ignore
|
||||
session_id: searchParams.get('session_id') || null,
|
||||
}).then((res) => {
|
||||
if (res.url) {
|
||||
window.location.href = res.url;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const dingdingLogin = () => {
|
||||
return (
|
||||
<Stack justifyContent='center'>
|
||||
<Divider sx={{ my: 3, fontSize: 12, borderColor: 'divider' }}>
|
||||
使用其他方式登录
|
||||
</Divider>
|
||||
<IconButton sx={{ alignSelf: 'center' }} onClick={onDingdingLogin}>
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 30 }} />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染登录表单
|
||||
const renderLoginForm = () => (
|
||||
<>
|
||||
@@ -284,19 +304,27 @@ const AuthPage = () => {
|
||||
<Box component='form' onSubmit={handleSubmit(onSubmit)}>
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={12}>{renderUsernameField()}</Grid>
|
||||
|
||||
<Grid size={12}>{renderPasswordField()}</Grid>
|
||||
|
||||
{renderErrorAlert()}
|
||||
{renderLoginButton()}
|
||||
</Grid>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const redirect_url = searchParams.get('redirect_url');
|
||||
if (redirect_url) {
|
||||
window.location.href = redirect_url;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<StyledContainer id='box'>
|
||||
<StyledPaper elevation={3}>{renderLoginForm()}</StyledPaper>
|
||||
<StyledPaper elevation={3}>
|
||||
{!loginSetting.disable_password_login && renderLoginForm()}
|
||||
{loginSetting.enable_dingtalk_oauth && dingdingLogin()}
|
||||
</StyledPaper>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ const ChatDetailModal = ({
|
||||
width: 700,
|
||||
}}
|
||||
>
|
||||
{data?.question?.replace(/^<task>|<\/task>$/g, '') || '-'}
|
||||
对话记录-{data?.user?.username}
|
||||
</Ellipsis>
|
||||
}
|
||||
width={800}
|
||||
|
||||
@@ -56,7 +56,7 @@ const Chat = () => {
|
||||
dataIndex: 'question',
|
||||
title: '任务',
|
||||
render(value: string, record) {
|
||||
const cleanValue = value?.replace(/^<task>|<\/task>$/g, '') || value;
|
||||
const cleanValue = value?.replace(/<\/?task>/g, '') || value;
|
||||
return (
|
||||
<Box
|
||||
onClick={() => setChatDetailModal(record)}
|
||||
|
||||
@@ -33,9 +33,9 @@ const ChatDetailModal = ({
|
||||
if (!data) return;
|
||||
getChatInfo({ id: data.id! }).then((res) => {
|
||||
setContent(
|
||||
`<code class="language-${data.program_language}">${
|
||||
res.content || ''
|
||||
}</code>`
|
||||
data.program_language
|
||||
? `\`\`\`${data.program_language}\n${res.content || ''}\n\`\`\``
|
||||
: res.content || ''
|
||||
);
|
||||
});
|
||||
// getConversationChatDetailModal({ id }).then((res) => {
|
||||
|
||||
@@ -19,9 +19,14 @@ import {
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
CircularProgress,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { postRegister } from '@/api/User';
|
||||
import {
|
||||
postRegister,
|
||||
getUserOauthSignupOrIn,
|
||||
getGetSetting,
|
||||
} from '@/api/User';
|
||||
import { Icon } from '@c-x/ui';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
@@ -63,9 +68,9 @@ const StepCard = styled(Box)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
const Invite = () => {
|
||||
const { id } = useParams();
|
||||
const { id, step } = useParams();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const { data: loginSetting = {} } = useRequest(getGetSetting);
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
@@ -80,7 +85,7 @@ const Invite = () => {
|
||||
const { runAsync: register, loading } = useRequest(postRegister, {
|
||||
manual: true,
|
||||
});
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [activeStep, setActiveStep] = useState(step ? parseInt(step) : 1);
|
||||
|
||||
const onNext = () => {
|
||||
setActiveStep(activeStep + 1);
|
||||
@@ -107,10 +112,19 @@ const Invite = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onDingdingLogin = () => {
|
||||
getUserOauthSignupOrIn({
|
||||
platform: 'dingtalk',
|
||||
redirect_url: `${window.location.origin}/invite/${id}/2`,
|
||||
}).then((res) => {
|
||||
window.location.href = res.url!;
|
||||
});
|
||||
};
|
||||
|
||||
const renderStepContent = () => {
|
||||
switch (activeStep) {
|
||||
case 0:
|
||||
return (
|
||||
case 1:
|
||||
return !loginSetting.enable_dingtalk_oauth ? (
|
||||
<Box component='form' onSubmit={onRegister}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid size={12}>
|
||||
@@ -229,9 +243,21 @@ const Invite = () => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
) : (
|
||||
<Stack>
|
||||
<Button
|
||||
size='large'
|
||||
variant='contained'
|
||||
sx={{ alignSelf: 'center' }}
|
||||
onClick={onDingdingLogin}
|
||||
>
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 20, mr: 1 }} />
|
||||
使用钉钉登录
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
return (
|
||||
<StepCard>
|
||||
<DownloadIcon sx={{ fontSize: 60, color: 'primary.main', mb: 2 }} />
|
||||
@@ -262,7 +288,7 @@ const Invite = () => {
|
||||
</StepCard>
|
||||
);
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
return (
|
||||
<StepCard>
|
||||
<MenuBookIcon sx={{ fontSize: 60, color: 'primary.main', mb: 2 }} />
|
||||
|
||||
131
ui/src/pages/user/dingdingLoginSettingModal.tsx
Normal file
131
ui/src/pages/user/dingdingLoginSettingModal.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, message, Loading } from '@c-x/ui';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { StyledFormLabel } from '@/components/form';
|
||||
import { putUpdateSetting } from '@/api/User';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
IconButton,
|
||||
Paper,
|
||||
TextField,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
|
||||
const DingingLoginSettingModal = ({
|
||||
open,
|
||||
onClose,
|
||||
onOk,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onOk: () => void;
|
||||
}) => {
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
dingtalk_client_id: '',
|
||||
dingtalk_client_secret: '',
|
||||
// title: '',
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
reset();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
putUpdateSetting({ ...data, enable_dingtalk_oauth: true }).then(() => {
|
||||
message.success('设置成功');
|
||||
onClose();
|
||||
onOk();
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title='钉钉登录设置'
|
||||
width={800}
|
||||
open={open}
|
||||
onOk={onSubmit}
|
||||
onCancel={onClose}
|
||||
>
|
||||
<Stack gap={2}>
|
||||
<Box>
|
||||
<StyledFormLabel required>Client ID</StyledFormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name='dingtalk_client_id'
|
||||
rules={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Client Id 不能为空',
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.dingtalk_client_id}
|
||||
helperText={errors.dingtalk_client_id?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<StyledFormLabel required>Client Secret</StyledFormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name='dingtalk_client_secret'
|
||||
rules={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Client Secret 不能为空',
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.dingtalk_client_secret}
|
||||
helperText={errors.dingtalk_client_secret?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
{/* <Box>
|
||||
<StyledFormLabel>标题名称,默认为 身份认证-钉钉登录</StyledFormLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name='title'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='请输入'
|
||||
error={!!errors.title}
|
||||
helperText={errors.title?.message}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box> */}
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DingingLoginSettingModal;
|
||||
@@ -1,16 +1,30 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Card from '@/components/card';
|
||||
import { Grid2 as Grid, Stack, styled, Switch } from '@mui/material';
|
||||
import {
|
||||
Grid2 as Grid,
|
||||
Stack,
|
||||
styled,
|
||||
Switch,
|
||||
Button,
|
||||
Box,
|
||||
Select,
|
||||
MenuItem,
|
||||
Radio,
|
||||
} from '@mui/material';
|
||||
import { Icon, Modal } from '@c-x/ui';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { getGetSetting, putUpdateSetting } from '@/api/User';
|
||||
import MemberManage from './memberManage';
|
||||
import LoginHistory from './loginHistory';
|
||||
import { message } from '@c-x/ui';
|
||||
import DingingLoginSettingModal from './dingdingLoginSettingModal';
|
||||
|
||||
const StyledCard = styled(Card)({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
boxShadow:
|
||||
'0px 0px 10px 0px rgba(68, 80, 91, 0.1), 0px 0px 2px 0px rgba(68, 80, 91, 0.1)',
|
||||
});
|
||||
|
||||
const StyledLabel = styled('div')(({ theme }) => ({
|
||||
@@ -20,60 +34,143 @@ const StyledLabel = styled('div')(({ theme }) => ({
|
||||
}));
|
||||
|
||||
const User = () => {
|
||||
const [dingdingLoginSettingModalOpen, setDingdingLoginSettingModalOpen] =
|
||||
useState(false);
|
||||
const [dingdingCheck, setDingdingCheck] = useState(false);
|
||||
const {
|
||||
data = {
|
||||
enable_sso: false,
|
||||
force_two_factor_auth: false,
|
||||
disable_password_login: false,
|
||||
enable_dingtalk_oauth: false,
|
||||
},
|
||||
refresh,
|
||||
} = useRequest(() => getGetSetting());
|
||||
const { run: updateSetting } = useRequest(putUpdateSetting, {
|
||||
} = useRequest(getGetSetting, {
|
||||
onSuccess: (data) => {
|
||||
setDingdingCheck(data.enable_dingtalk_oauth!);
|
||||
},
|
||||
});
|
||||
|
||||
const { runAsync: updateSetting } = useRequest(putUpdateSetting, {
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
refresh();
|
||||
message.success('设置更新成功');
|
||||
},
|
||||
});
|
||||
|
||||
const onDisabledDingdingLogin = () => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要关闭钉钉登录吗?',
|
||||
onOk: () => {
|
||||
updateSetting({ enable_dingtalk_oauth: false }).then(() => {
|
||||
refresh();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid size={4}>
|
||||
<StyledCard>
|
||||
<StyledLabel>单点登录</StyledLabel>
|
||||
<Switch
|
||||
checked={data?.enable_sso}
|
||||
onChange={(e) => updateSetting({ enable_sso: e.target.checked })}
|
||||
/>
|
||||
</StyledCard>
|
||||
<Grid size={6} container>
|
||||
<Grid size={12}>
|
||||
<StyledCard>
|
||||
<StyledLabel>强制成员启用两步认证</StyledLabel>
|
||||
<Switch
|
||||
checked={data?.force_two_factor_auth}
|
||||
onChange={(e) => {
|
||||
updateSetting({ force_two_factor_auth: e.target.checked });
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
</Grid>
|
||||
<Grid size={12}>
|
||||
<StyledCard>
|
||||
<StyledLabel>禁止成员使用密码登录</StyledLabel>
|
||||
<Switch
|
||||
checked={data?.disable_password_login}
|
||||
onChange={(e) =>
|
||||
updateSetting({ disable_password_login: e.target.checked })
|
||||
}
|
||||
/>
|
||||
</StyledCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid size={4}>
|
||||
<StyledCard>
|
||||
<StyledLabel>强制成员启用两步认证</StyledLabel>
|
||||
<Switch
|
||||
checked={data?.force_two_factor_auth}
|
||||
onChange={(e) => {
|
||||
console.log(e.target.checked);
|
||||
updateSetting({ force_two_factor_auth: e.target.checked });
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
</Grid>
|
||||
<Grid size={4}>
|
||||
<StyledCard>
|
||||
<StyledLabel>禁止成员使用密码登录</StyledLabel>
|
||||
<Switch
|
||||
checked={data?.disable_password_login}
|
||||
onChange={(e) =>
|
||||
updateSetting({ disable_password_login: e.target.checked })
|
||||
}
|
||||
/>
|
||||
</StyledCard>
|
||||
<Grid size={6} container>
|
||||
<Grid size={12}>
|
||||
<Card sx={{ height: '100%' }}>
|
||||
<StyledLabel>第三方登录</StyledLabel>
|
||||
<Stack
|
||||
direction='row'
|
||||
alignItems='center'
|
||||
spacing={2}
|
||||
sx={{ mt: 2, height: 'calc(100% - 40px)' }}
|
||||
>
|
||||
<Button
|
||||
variant='outlined'
|
||||
color='primary'
|
||||
sx={{ gap: 3 }}
|
||||
onClick={() => {
|
||||
if (dingdingCheck) {
|
||||
onDisabledDingdingLogin();
|
||||
} else {
|
||||
setDingdingLoginSettingModalOpen(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Radio size='small' sx={{ p: 0.5 }} checked={dingdingCheck} />
|
||||
<Stack direction='row' alignItems='center' gap={2}>
|
||||
<Stack direction='row' alignItems='center' gap={1}>
|
||||
<Icon type='icon-dingding' sx={{ fontSize: 18 }}></Icon>
|
||||
钉钉
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Button>
|
||||
<Button
|
||||
variant='outlined'
|
||||
color='primary'
|
||||
sx={{ gap: 3 }}
|
||||
disabled
|
||||
>
|
||||
<Radio size='small' sx={{ p: 0.5 }} disabled />
|
||||
<Stack direction='row' alignItems='center' gap={2}>
|
||||
<Stack direction='row' alignItems='center' gap={1}>
|
||||
<Icon type='icon-weixin' sx={{ fontSize: 18 }}></Icon>
|
||||
微信
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Button>
|
||||
<Button
|
||||
variant='outlined'
|
||||
color='primary'
|
||||
sx={{ gap: 3 }}
|
||||
disabled
|
||||
>
|
||||
<Radio size='small' sx={{ p: 0.5 }} disabled />
|
||||
<Stack direction='row' alignItems='center' gap={2}>
|
||||
<Stack direction='row' alignItems='center' gap={1}>
|
||||
<Icon type='icon-github' sx={{ fontSize: 18 }}></Icon>
|
||||
GitHub
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Button>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<MemberManage />
|
||||
<LoginHistory />
|
||||
<DingingLoginSettingModal
|
||||
open={dingdingLoginSettingModalOpen}
|
||||
onClose={() => setDingdingLoginSettingModalOpen(false)}
|
||||
onOk={() => {
|
||||
refresh();
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ const InviteUserModal = ({
|
||||
const { loading, refresh } = useRequest(getInvite, {
|
||||
manual: true,
|
||||
onSuccess: (data) => {
|
||||
setInviteUrl(location.origin + '/invite/' + data?.code);
|
||||
setInviteUrl(location.origin + '/invite/' + data?.code + '/1');
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ const routerConfig = [
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/invite/:id',
|
||||
path: '/invite/:id/:step?',
|
||||
element: <Invite />,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ const lightTheme = createTheme(
|
||||
auxiliary: 'rgba(33,34,45, 0.5)',
|
||||
disabled: 'rgba(33,34,45, 0.2)',
|
||||
},
|
||||
divider: '#ECEEF1',
|
||||
// divider: '#ECEEF1',
|
||||
},
|
||||
shadows: [
|
||||
...defaultTheme.shadows.slice(0, 8),
|
||||
|
||||
Reference in New Issue
Block a user