feat: able to customize system name & logo now

This commit is contained in:
JustSong 2023-05-14 19:29:02 +08:00
parent 2cdc718fde
commit 926951ee03
10 changed files with 87 additions and 29 deletions

View File

@ -11,6 +11,7 @@ var Version = "v0.0.0" // this hard coding will be replaced automatic
var SystemName = "One API" var SystemName = "One API"
var ServerAddress = "http://localhost:3000" var ServerAddress = "http://localhost:3000"
var Footer = "" var Footer = ""
var Logo = ""
var TopUpLink = "" var TopUpLink = ""
var UsingSQLite = false var UsingSQLite = false

View File

@ -20,6 +20,7 @@ func GetStatus(c *gin.Context) {
"github_oauth": common.GitHubOAuthEnabled, "github_oauth": common.GitHubOAuthEnabled,
"github_client_id": common.GitHubClientId, "github_client_id": common.GitHubClientId,
"system_name": common.SystemName, "system_name": common.SystemName,
"logo": common.Logo,
"footer_html": common.Footer, "footer_html": common.Footer,
"wechat_qrcode": common.WeChatAccountQRCodeImageURL, "wechat_qrcode": common.WeChatAccountQRCodeImageURL,
"wechat_login": common.WeChatAuthEnabled, "wechat_login": common.WeChatAuthEnabled,

View File

@ -41,6 +41,8 @@ func InitOptionMap() {
common.OptionMap["About"] = "" common.OptionMap["About"] = ""
common.OptionMap["HomePageContent"] = "" common.OptionMap["HomePageContent"] = ""
common.OptionMap["Footer"] = common.Footer common.OptionMap["Footer"] = common.Footer
common.OptionMap["SystemName"] = common.SystemName
common.OptionMap["Logo"] = common.Logo
common.OptionMap["ServerAddress"] = "" common.OptionMap["ServerAddress"] = ""
common.OptionMap["GitHubClientId"] = "" common.OptionMap["GitHubClientId"] = ""
common.OptionMap["GitHubClientSecret"] = "" common.OptionMap["GitHubClientSecret"] = ""
@ -134,6 +136,10 @@ func updateOptionMap(key string, value string) (err error) {
common.GitHubClientSecret = value common.GitHubClientSecret = value
case "Footer": case "Footer":
common.Footer = value common.Footer = value
case "SystemName":
common.SystemName = value
case "Logo":
common.Logo = value
case "WeChatServerAddress": case "WeChatServerAddress":
common.WeChatServerAddress = value common.WeChatServerAddress = value
case "WeChatServerToken": case "WeChatServerToken":

View File

@ -42,6 +42,8 @@ function App() {
if (success) { if (success) {
localStorage.setItem('status', JSON.stringify(data)); localStorage.setItem('status', JSON.stringify(data));
statusDispatch({ type: 'set', payload: data }); statusDispatch({ type: 'set', payload: data });
localStorage.setItem('system_name', data.system_name);
localStorage.setItem('logo', data.logo);
localStorage.setItem('footer_html', data.footer_html); localStorage.setItem('footer_html', data.footer_html);
if ( if (
data.version !== process.env.REACT_APP_VERSION && data.version !== process.env.REACT_APP_VERSION &&

View File

@ -1,40 +1,37 @@
import React, { useEffect, useState } from 'react'; import React from 'react';
import { Container, Segment } from 'semantic-ui-react'; import { Container, Segment } from 'semantic-ui-react';
import { getFooterHTML, getSystemName } from '../helpers';
const Footer = () => { const Footer = () => {
const [Footer, setFooter] = useState(''); const systemName = getSystemName();
useEffect(() => { const footer = getFooterHTML();
let savedFooter = localStorage.getItem('footer_html');
if (!savedFooter) savedFooter = '';
setFooter(savedFooter);
});
return ( return (
<Segment vertical> <Segment vertical>
<Container textAlign="center"> <Container textAlign='center'>
{Footer === '' ? ( {footer ? (
<div className="custom-footer"> <div
className='custom-footer'
dangerouslySetInnerHTML={{ __html: footer }}
></div>
) : (
<div className='custom-footer'>
<a <a
href="https://github.com/songquanpeng/one-api" href='https://github.com/songquanpeng/one-api'
target="_blank" target='_blank'
> >
One API {process.env.REACT_APP_VERSION}{' '} {systemName} {process.env.REACT_APP_VERSION}{' '}
</a> </a>
{' '} {' '}
<a href="https://github.com/songquanpeng" target="_blank"> <a href='https://github.com/songquanpeng' target='_blank'>
JustSong JustSong
</a>{' '} </a>{' '}
构建源代码遵循{' '} 构建源代码遵循{' '}
<a href="https://opensource.org/licenses/mit-license.php"> <a href='https://opensource.org/licenses/mit-license.php'>
MIT 协议 MIT 协议
</a> </a>
</div> </div>
) : (
<div
className="custom-footer"
dangerouslySetInnerHTML={{ __html: Footer }}
></div>
)} )}
</Container> </Container>
</Segment> </Segment>

View File

@ -3,7 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { UserContext } from '../context/User'; import { UserContext } from '../context/User';
import { Button, Container, Dropdown, Icon, Menu, Segment } from 'semantic-ui-react'; import { Button, Container, Dropdown, Icon, Menu, Segment } from 'semantic-ui-react';
import { API, isAdmin, isMobile, showSuccess } from '../helpers'; import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../helpers';
import '../index.css'; import '../index.css';
// Header Buttons // Header Buttons
@ -53,6 +53,8 @@ const Header = () => {
let navigate = useNavigate(); let navigate = useNavigate();
const [showSidebar, setShowSidebar] = useState(false); const [showSidebar, setShowSidebar] = useState(false);
const systemName = getSystemName();
const logo = getLogo();
async function logout() { async function logout() {
setShowSidebar(false); setShowSidebar(false);
@ -111,12 +113,12 @@ const Header = () => {
<Container> <Container>
<Menu.Item as={Link} to='/'> <Menu.Item as={Link} to='/'>
<img <img
src='/logo.png' src={logo}
alt='logo' alt='logo'
style={{ marginRight: '0.75em' }} style={{ marginRight: '0.75em' }}
/> />
<div style={{ fontSize: '20px' }}> <div style={{ fontSize: '20px' }}>
<b>One API</b> <b>{systemName}</b>
</div> </div>
</Menu.Item> </Menu.Item>
<Menu.Menu position='right'> <Menu.Menu position='right'>
@ -168,9 +170,9 @@ const Header = () => {
<Menu borderless style={{ borderTop: 'none' }}> <Menu borderless style={{ borderTop: 'none' }}>
<Container> <Container>
<Menu.Item as={Link} to='/' className={'hide-on-mobile'}> <Menu.Item as={Link} to='/' className={'hide-on-mobile'}>
<img src='/logo.png' alt='logo' style={{ marginRight: '0.75em' }} /> <img src={logo} alt='logo' style={{ marginRight: '0.75em' }} />
<div style={{ fontSize: '20px' }}> <div style={{ fontSize: '20px' }}>
<b>One API</b> <b>{systemName}</b>
</div> </div>
</Menu.Item> </Menu.Item>
{renderButtons(false)} {renderButtons(false)}

View File

@ -12,7 +12,7 @@ import {
} from 'semantic-ui-react'; } from 'semantic-ui-react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { UserContext } from '../context/User'; import { UserContext } from '../context/User';
import { API, showError, showSuccess } from '../helpers'; import { API, getLogo, showError, showSuccess } from '../helpers';
const LoginForm = () => { const LoginForm = () => {
const [inputs, setInputs] = useState({ const [inputs, setInputs] = useState({
@ -27,6 +27,7 @@ const LoginForm = () => {
let navigate = useNavigate(); let navigate = useNavigate();
const [status, setStatus] = useState({}); const [status, setStatus] = useState({});
const logo = getLogo();
useEffect(() => { useEffect(() => {
if (searchParams.get("expired")) { if (searchParams.get("expired")) {
@ -95,7 +96,7 @@ const LoginForm = () => {
<Grid textAlign="center" style={{ marginTop: '48px' }}> <Grid textAlign="center" style={{ marginTop: '48px' }}>
<Grid.Column style={{ maxWidth: 450 }}> <Grid.Column style={{ maxWidth: 450 }}>
<Header as="h2" color="" textAlign="center"> <Header as="h2" color="" textAlign="center">
<Image src="/logo.png" /> 用户登录 <Image src={logo} /> 用户登录
</Header> </Header>
<Form size="large"> <Form size="large">
<Segment> <Segment>

View File

@ -8,6 +8,8 @@ const OtherSetting = () => {
Footer: '', Footer: '',
Notice: '', Notice: '',
About: '', About: '',
SystemName: '',
Logo: '',
HomePageContent: '', HomePageContent: '',
}); });
let originInputs = {}; let originInputs = {};
@ -66,6 +68,14 @@ const OtherSetting = () => {
await updateOption('Footer', inputs.Footer); await updateOption('Footer', inputs.Footer);
}; };
const submitSystemName = async () => {
await updateOption('SystemName', inputs.SystemName);
};
const submitLogo = async () => {
await updateOption('Logo', inputs.Logo);
};
const submitAbout = async () => { const submitAbout = async () => {
await updateOption('About', inputs.About); await updateOption('About', inputs.About);
}; };
@ -114,6 +124,27 @@ const OtherSetting = () => {
<Form.Button onClick={submitNotice}>保存公告</Form.Button> <Form.Button onClick={submitNotice}>保存公告</Form.Button>
<Divider /> <Divider />
<Header as='h3'>个性化设置</Header> <Header as='h3'>个性化设置</Header>
<Form.Group widths='equal'>
<Form.Input
label='系统名称'
placeholder='在此输入系统名称'
value={inputs.SystemName}
name='SystemName'
onChange={handleInputChange}
/>
</Form.Group>
<Form.Button onClick={submitSystemName}>设置系统名称</Form.Button>
<Form.Group widths='equal'>
<Form.Input
label='Logo 图片地址'
placeholder='在此输入 Logo 图片地址'
value={inputs.Logo}
name='Logo'
type='url'
onChange={handleInputChange}
/>
</Form.Group>
<Form.Button onClick={submitLogo}>设置 Logo</Form.Button>
<Form.Group widths='equal'> <Form.Group widths='equal'>
<Form.TextArea <Form.TextArea
label='首页内容' label='首页内容'

View File

@ -9,7 +9,7 @@ import {
Segment, Segment,
} from 'semantic-ui-react'; } from 'semantic-ui-react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { API, showError, showInfo, showSuccess } from '../helpers'; import { API, getLogo, showError, showInfo, showSuccess } from '../helpers';
import Turnstile from 'react-turnstile'; import Turnstile from 'react-turnstile';
const RegisterForm = () => { const RegisterForm = () => {
@ -26,6 +26,7 @@ const RegisterForm = () => {
const [turnstileSiteKey, setTurnstileSiteKey] = useState(''); const [turnstileSiteKey, setTurnstileSiteKey] = useState('');
const [turnstileToken, setTurnstileToken] = useState(''); const [turnstileToken, setTurnstileToken] = useState('');
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const logo = getLogo();
useEffect(() => { useEffect(() => {
let status = localStorage.getItem('status'); let status = localStorage.getItem('status');
@ -100,7 +101,7 @@ const RegisterForm = () => {
<Grid textAlign='center' style={{ marginTop: '48px' }}> <Grid textAlign='center' style={{ marginTop: '48px' }}>
<Grid.Column style={{ maxWidth: 450 }}> <Grid.Column style={{ maxWidth: 450 }}>
<Header as='h2' color='' textAlign='center'> <Header as='h2' color='' textAlign='center'>
<Image src='/logo.png' /> 新用户注册 <Image src={logo} /> 新用户注册
</Header> </Header>
<Form size='large'> <Form size='large'>
<Segment> <Segment>

View File

@ -15,6 +15,22 @@ export function isRoot() {
return user.role >= 100; return user.role >= 100;
} }
export function getSystemName() {
let system_name = localStorage.getItem('system_name');
if (!system_name) return 'One API';
return system_name;
}
export function getLogo() {
let logo = localStorage.getItem('logo');
if (!logo) return '/logo.png';
return logo
}
export function getFooterHTML() {
return localStorage.getItem('footer_html');
}
export async function copy(text) { export async function copy(text) {
let okay = true; let okay = true;
try { try {