mirror of
https://github.com/linux-do/new-api.git
synced 2025-09-17 07:56:38 +08:00
merge upstream
Signed-off-by: wozulong <>
This commit is contained in:
commit
c74e43b8fd
2
Makefile
2
Makefile
@ -7,7 +7,7 @@ all: build-frontend start-backend
|
|||||||
|
|
||||||
build-frontend:
|
build-frontend:
|
||||||
@echo "Building frontend..."
|
@echo "Building frontend..."
|
||||||
@cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat VERSION) yarn build
|
@cd $(FRONTEND_DIR) && yarn install --network-timeout 1000000 && DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat VERSION) yarn build
|
||||||
|
|
||||||
start-backend:
|
start-backend:
|
||||||
@echo "Starting backend dev server..."
|
@echo "Starting backend dev server..."
|
||||||
|
@ -18,16 +18,28 @@ type Adaptor struct {
|
|||||||
func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) {
|
func (a *Adaptor) Init(info *relaycommon.RelayInfo, request dto.GeneralOpenAIRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定义一个映射,存储模型名称和对应的版本
|
||||||
|
var modelVersionMap = map[string]string{
|
||||||
|
"gemini-1.5-pro-latest": "v1beta",
|
||||||
|
"gemini-ultra": "v1beta",
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
||||||
version := "v1"
|
// 从映射中获取模型名称对应的版本,如果找不到就使用 info.ApiVersion 或默认的版本 "v1"
|
||||||
if info.ApiVersion != "" {
|
version, beta := modelVersionMap[info.UpstreamModelName]
|
||||||
version = info.ApiVersion
|
if !beta {
|
||||||
}
|
if info.ApiVersion != "" {
|
||||||
action := "generateContent"
|
version = info.ApiVersion
|
||||||
if info.IsStream {
|
} else {
|
||||||
action = "streamGenerateContent"
|
version = "v1"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
|
}
|
||||||
|
|
||||||
|
action := "generateContent"
|
||||||
|
if info.IsStream {
|
||||||
|
action = "streamGenerateContent"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s/models/%s:%s", info.BaseUrl, version, info.UpstreamModelName, action), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error {
|
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error {
|
||||||
|
@ -5,8 +5,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
|
"gemini-1.0-pro-latest", "gemini-1.0-pro-001", "gemini-1.5-pro-latest", "gemini-ultra",
|
||||||
"gemini-pro-vision", "gemini-1.0-pro-vision-001",
|
"gemini-1.0-pro-vision-latest", "gemini-1.0-pro-vision-001",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ChannelName = "google gemini"
|
var ChannelName = "google gemini"
|
||||||
|
@ -179,7 +179,13 @@ func xunfeiHandler(c *gin.Context, textRequest dto.GeneralOpenAIRequest, appId s
|
|||||||
case stop = <-stopChan:
|
case stop = <-stopChan:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||||
|
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
||||||
|
{
|
||||||
|
Content: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
||||||
|
|
||||||
response := responseXunfei2OpenAI(&xunfeiResponse)
|
response := responseXunfei2OpenAI(&xunfeiResponse)
|
||||||
|
@ -254,6 +254,19 @@ const ChannelsTable = () => {
|
|||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
|
<Popconfirm
|
||||||
|
title='确定是否要复制此渠道?'
|
||||||
|
content='复制渠道的所有信息'
|
||||||
|
okType={'danger'}
|
||||||
|
position={'left'}
|
||||||
|
onConfirm={async () => {
|
||||||
|
copySelectedChannel(record.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button theme='light' type='primary' style={{ marginRight: 1 }}>
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -340,6 +353,33 @@ const ChannelsTable = () => {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const copySelectedChannel = async (id) => {
|
||||||
|
const channelToCopy = channels.find(
|
||||||
|
(channel) => String(channel.id) === String(id),
|
||||||
|
);
|
||||||
|
console.log(channelToCopy);
|
||||||
|
channelToCopy.name += '_复制';
|
||||||
|
channelToCopy.created_time = null;
|
||||||
|
channelToCopy.balance = 0;
|
||||||
|
channelToCopy.used_quota = 0;
|
||||||
|
if (!channelToCopy) {
|
||||||
|
showError('渠道未找到,请刷新页面后重试。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const newChannel = { ...channelToCopy, id: undefined };
|
||||||
|
const response = await API.post('/api/channel/', newChannel);
|
||||||
|
if (response.data.success) {
|
||||||
|
showSuccess('渠道复制成功');
|
||||||
|
await refresh();
|
||||||
|
} else {
|
||||||
|
showError(response.data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError('渠道复制失败: ' + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
await loadChannels(activePage - 1, pageSize, idSort);
|
await loadChannels(activePage - 1, pageSize, idSort);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
import { UserContext } from '../context/User';
|
import { UserContext } from '../context/User';
|
||||||
|
import { useSetTheme, useTheme } from '../context/Theme';
|
||||||
|
|
||||||
import { API, getLogo, getSystemName, showSuccess } from '../helpers';
|
import { API, getLogo, getSystemName, showSuccess } from '../helpers';
|
||||||
import '../index.css';
|
import '../index.css';
|
||||||
@ -34,10 +35,8 @@ const HeaderBar = () => {
|
|||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
const [showSidebar, setShowSidebar] = useState(false);
|
const [showSidebar, setShowSidebar] = useState(false);
|
||||||
const [dark, setDark] = useState(false);
|
|
||||||
const systemName = getSystemName();
|
const systemName = getSystemName();
|
||||||
const logo = getLogo();
|
const logo = getLogo();
|
||||||
var themeMode = localStorage.getItem('theme-mode');
|
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
// enable fireworks on new year(1.1 and 2.9-2.24)
|
// enable fireworks on new year(1.1 and 2.9-2.24)
|
||||||
const isNewYear =
|
const isNewYear =
|
||||||
@ -66,26 +65,19 @@ const HeaderBar = () => {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const setTheme = useSetTheme();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themeMode === 'dark') {
|
if (theme === 'dark') {
|
||||||
switchMode(true);
|
document.body.setAttribute('theme-mode', 'dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNewYear) {
|
if (isNewYear) {
|
||||||
console.log('Happy New Year!');
|
console.log('Happy New Year!');
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const switchMode = (model) => {
|
|
||||||
const body = document.body;
|
|
||||||
if (!model) {
|
|
||||||
body.removeAttribute('theme-mode');
|
|
||||||
localStorage.setItem('theme-mode', 'light');
|
|
||||||
} else {
|
|
||||||
body.setAttribute('theme-mode', 'dark');
|
|
||||||
localStorage.setItem('theme-mode', 'dark');
|
|
||||||
}
|
|
||||||
setDark(model);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Layout>
|
<Layout>
|
||||||
@ -132,9 +124,11 @@ const HeaderBar = () => {
|
|||||||
<Switch
|
<Switch
|
||||||
checkedText='🌞'
|
checkedText='🌞'
|
||||||
size={'large'}
|
size={'large'}
|
||||||
checked={dark}
|
checked={theme === 'dark'}
|
||||||
uncheckedText='🌙'
|
uncheckedText='🌙'
|
||||||
onChange={switchMode}
|
onChange={(checked) => {
|
||||||
|
setTheme(checked);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{userState.user ? (
|
{userState.user ? (
|
||||||
<>
|
<>
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
verifyJSON,
|
verifyJSON,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
|
|
||||||
|
import { useTheme } from '../context/Theme';
|
||||||
|
|
||||||
const OperationSetting = () => {
|
const OperationSetting = () => {
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
let [inputs, setInputs] = useState({
|
let [inputs, setInputs] = useState({
|
||||||
@ -77,6 +79,9 @@ const OperationSetting = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getOptions().then();
|
getOptions().then();
|
||||||
}, []);
|
}, []);
|
||||||
@ -219,8 +224,10 @@ const OperationSetting = () => {
|
|||||||
return (
|
return (
|
||||||
<Grid columns={1}>
|
<Grid columns={1}>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<Form loading={loading}>
|
<Form loading={loading} inverted={isDark}>
|
||||||
<Header as='h3'>通用设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
通用设置
|
||||||
|
</Header>
|
||||||
<Form.Group widths={4}>
|
<Form.Group widths={4}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='充值链接'
|
label='充值链接'
|
||||||
@ -299,7 +306,9 @@ const OperationSetting = () => {
|
|||||||
保存通用设置
|
保存通用设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>绘图设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
绘图设置
|
||||||
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.DrawingEnabled === 'true'}
|
checked={inputs.DrawingEnabled === 'true'}
|
||||||
@ -321,7 +330,9 @@ const OperationSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>屏蔽词过滤设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
屏蔽词过滤设置
|
||||||
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.CheckSensitiveEnabled === 'true'}
|
checked={inputs.CheckSensitiveEnabled === 'true'}
|
||||||
@ -381,7 +392,9 @@ const OperationSetting = () => {
|
|||||||
保存屏蔽词设置
|
保存屏蔽词设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>日志设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
日志设置
|
||||||
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.LogConsumeEnabled === 'true'}
|
checked={inputs.LogConsumeEnabled === 'true'}
|
||||||
@ -409,7 +422,9 @@ const OperationSetting = () => {
|
|||||||
清理历史日志
|
清理历史日志
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>数据看板</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
数据看板
|
||||||
|
</Header>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.DataExportEnabled === 'true'}
|
checked={inputs.DataExportEnabled === 'true'}
|
||||||
label='启用数据看板(实验性)'
|
label='启用数据看板(实验性)'
|
||||||
@ -439,7 +454,9 @@ const OperationSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>监控设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
监控设置
|
||||||
|
</Header>
|
||||||
<Form.Group widths={3}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='最长响应时间'
|
label='最长响应时间'
|
||||||
@ -484,7 +501,9 @@ const OperationSetting = () => {
|
|||||||
保存监控设置
|
保存监控设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>额度设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
额度设置
|
||||||
|
</Header>
|
||||||
<Form.Group widths={4}>
|
<Form.Group widths={4}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='新用户初始额度'
|
label='新用户初始额度'
|
||||||
@ -535,7 +554,9 @@ const OperationSetting = () => {
|
|||||||
保存额度设置
|
保存额度设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>倍率设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
倍率设置
|
||||||
|
</Header>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
|
label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
|
||||||
|
@ -10,6 +10,8 @@ import {
|
|||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
|
import { API, removeTrailingSlash, showError, verifyJSON } from '../helpers';
|
||||||
|
|
||||||
|
import { useTheme } from '../context/Theme';
|
||||||
|
|
||||||
const SystemSetting = () => {
|
const SystemSetting = () => {
|
||||||
let [inputs, setInputs] = useState({
|
let [inputs, setInputs] = useState({
|
||||||
PasswordLoginEnabled: '',
|
PasswordLoginEnabled: '',
|
||||||
@ -62,6 +64,9 @@ const SystemSetting = () => {
|
|||||||
const [showPasswordWarningModal, setShowPasswordWarningModal] =
|
const [showPasswordWarningModal, setShowPasswordWarningModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const isDark = theme === 'dark';
|
||||||
|
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
const res = await API.get('/api/option/');
|
const res = await API.get('/api/option/');
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
@ -346,8 +351,10 @@ const SystemSetting = () => {
|
|||||||
return (
|
return (
|
||||||
<Grid columns={1}>
|
<Grid columns={1}>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<Form loading={loading}>
|
<Form loading={loading} inverted={isDark}>
|
||||||
<Header as='h3'>通用设置</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
通用设置
|
||||||
|
</Header>
|
||||||
<Form.Group widths='equal'>
|
<Form.Group widths='equal'>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='服务器地址'
|
label='服务器地址'
|
||||||
@ -361,7 +368,7 @@ const SystemSetting = () => {
|
|||||||
更新服务器地址
|
更新服务器地址
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
支付设置(当前仅支持Stripe Checkout)
|
支付设置(当前仅支持Stripe Checkout)
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
密钥、Webhook 等设置请
|
密钥、Webhook 等设置请
|
||||||
@ -455,7 +462,9 @@ const SystemSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>配置登录注册</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
配置登录注册
|
||||||
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.PasswordLoginEnabled === 'true'}
|
checked={inputs.PasswordLoginEnabled === 'true'}
|
||||||
@ -550,7 +559,7 @@ const SystemSetting = () => {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
配置邮箱域名白名单
|
配置邮箱域名白名单
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以防止恶意用户利用临时邮箱批量注册
|
用以防止恶意用户利用临时邮箱批量注册
|
||||||
@ -615,7 +624,7 @@ const SystemSetting = () => {
|
|||||||
保存邮箱域名白名单设置
|
保存邮箱域名白名单设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
配置 SMTP
|
配置 SMTP
|
||||||
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
|
<Header.Subheader>用以支持系统的邮件发送</Header.Subheader>
|
||||||
</Header>
|
</Header>
|
||||||
@ -674,7 +683,7 @@ const SystemSetting = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
|
<Form.Button onClick={submitSMTP}>保存 SMTP 设置</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
配置 GitHub OAuth App
|
配置 GitHub OAuth App
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以支持通过 GitHub 进行登录注册,
|
用以支持通过 GitHub 进行登录注册,
|
||||||
@ -768,7 +777,7 @@ const SystemSetting = () => {
|
|||||||
保存 LINUX DO OAuth 设置
|
保存 LINUX DO OAuth 设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
配置 WeChat Server
|
配置 WeChat Server
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以支持通过微信进行登录注册,
|
用以支持通过微信进行登录注册,
|
||||||
@ -813,7 +822,9 @@ const SystemSetting = () => {
|
|||||||
保存 WeChat Server 设置
|
保存 WeChat Server 设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>配置 Telegram 登录</Header>
|
<Header as='h3' inverted={isDark}>
|
||||||
|
配置 Telegram 登录
|
||||||
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='Telegram Bot Token'
|
label='Telegram Bot Token'
|
||||||
@ -834,7 +845,7 @@ const SystemSetting = () => {
|
|||||||
保存 Telegram 登录设置
|
保存 Telegram 登录设置
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Header as='h3'>
|
<Header as='h3' inverted={isDark}>
|
||||||
配置 Turnstile
|
配置 Turnstile
|
||||||
<Header.Subheader>
|
<Header.Subheader>
|
||||||
用以支持用户校验,
|
用以支持用户校验,
|
||||||
|
36
web/src/context/Theme/index.js
Normal file
36
web/src/context/Theme/index.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { createContext, useCallback, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const ThemeContext = createContext(null);
|
||||||
|
export const useTheme = () => useContext(ThemeContext);
|
||||||
|
|
||||||
|
const SetThemeContext = createContext(null);
|
||||||
|
export const useSetTheme = () => useContext(SetThemeContext);
|
||||||
|
|
||||||
|
export const ThemeProvider = ({ children }) => {
|
||||||
|
const [theme, _setTheme] = useState(() => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem('theme-mode') || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const setTheme = useCallback((input) => {
|
||||||
|
_setTheme(input ? 'dark' : 'light');
|
||||||
|
|
||||||
|
const body = document.body;
|
||||||
|
if (!input) {
|
||||||
|
body.removeAttribute('theme-mode');
|
||||||
|
localStorage.setItem('theme-mode', 'light');
|
||||||
|
} else {
|
||||||
|
body.setAttribute('theme-mode', 'dark');
|
||||||
|
localStorage.setItem('theme-mode', 'dark');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SetThemeContext.Provider value={setTheme}>
|
||||||
|
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
|
||||||
|
</SetThemeContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -12,6 +12,7 @@ import 'react-toastify/dist/ReactToastify.css';
|
|||||||
import { StatusProvider } from './context/Status';
|
import { StatusProvider } from './context/Status';
|
||||||
import { Layout } from '@douyinfe/semi-ui';
|
import { Layout } from '@douyinfe/semi-ui';
|
||||||
import SiderBar from './components/SiderBar';
|
import SiderBar from './components/SiderBar';
|
||||||
|
import { ThemeProvider } from './context/Theme';
|
||||||
|
|
||||||
// initialization
|
// initialization
|
||||||
|
|
||||||
@ -22,27 +23,29 @@ root.render(
|
|||||||
<StatusProvider>
|
<StatusProvider>
|
||||||
<UserProvider>
|
<UserProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Layout>
|
<ThemeProvider>
|
||||||
<Sider>
|
|
||||||
<SiderBar />
|
|
||||||
</Sider>
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Header>
|
<Sider>
|
||||||
<HeaderBar />
|
<SiderBar />
|
||||||
</Header>
|
</Sider>
|
||||||
<Content
|
<Layout>
|
||||||
style={{
|
<Header>
|
||||||
padding: '24px',
|
<HeaderBar />
|
||||||
}}
|
</Header>
|
||||||
>
|
<Content
|
||||||
<App />
|
style={{
|
||||||
</Content>
|
padding: '24px',
|
||||||
<Layout.Footer>
|
}}
|
||||||
<Footer></Footer>
|
>
|
||||||
</Layout.Footer>
|
<App />
|
||||||
|
</Content>
|
||||||
|
<Layout.Footer>
|
||||||
|
<Footer></Footer>
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
<ToastContainer />
|
||||||
</Layout>
|
</Layout>
|
||||||
<ToastContainer />
|
</ThemeProvider>
|
||||||
</Layout>
|
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
</StatusProvider>
|
</StatusProvider>
|
||||||
|
Loading…
Reference in New Issue
Block a user