Commit eec9046c by CaIon

更换前端组件库

parent f439c4d5
......@@ -54,7 +54,7 @@ func RelayMidjourneyImage(c *gin.Context) {
return
}
c.Header("Content-Type", "image/jpeg")
//c.Header("Content-Length", string(rune(len(data))))
//c.HeaderBar("Content-Length", string(rune(len(data))))
c.Data(http.StatusOK, "image/jpeg", data)
}
......@@ -275,13 +275,13 @@ func relayMidjourneySubmit(c *gin.Context, relayMode int) *MidjourneyResponse {
Description: "create_request_failed",
}
}
//req.Header.Set("Authorization", c.Request.Header.Get("Authorization"))
//req.HeaderBar.Set("Authorization", c.Request.HeaderBar.Get("Authorization"))
req.Header.Set("Content-Type", c.Request.Header.Get("Content-Type"))
req.Header.Set("Accept", c.Request.Header.Get("Accept"))
//mjToken := ""
//if c.Request.Header.Get("Authorization") != "" {
// mjToken = strings.Split(c.Request.Header.Get("Authorization"), " ")[1]
//if c.Request.HeaderBar.Get("Authorization") != "" {
// mjToken = strings.Split(c.Request.HeaderBar.Get("Authorization"), " ")[1]
//}
req.Header.Set("mj-api-secret", strings.Split(c.Request.Header.Get("Authorization"), " ")[1])
// print request header
......
......@@ -381,7 +381,7 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
if isStream && c.Request.Header.Get("Accept") == "" {
req.Header.Set("Accept", "text/event-stream")
}
//req.Header.Set("Connection", c.Request.Header.Get("Connection"))
//req.HeaderBar.Set("Connection", c.Request.HeaderBar.Get("Connection"))
resp, err = httpClient.Do(req)
if err != nil {
return errorWrapper(err, "do_request_failed", http.StatusInternalServerError)
......
......@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@douyinfe/semi-ui": "^2.45.2",
"axios": "^0.27.2",
"history": "^5.3.0",
"marked": "^4.1.1",
......@@ -14,7 +15,8 @@
"react-toastify": "^9.0.8",
"react-turnstile": "^1.0.5",
"semantic-ui-css": "^2.5.0",
"semantic-ui-react": "^2.1.3"
"semantic-ui-react": "^2.1.3",
"usehooks-ts": "^2.9.1"
},
"scripts": {
"start": "react-scripts start",
......@@ -47,5 +49,5 @@
"singleQuote": true,
"jsxSingleQuote": true
},
"proxy": "http://localhost:3000"
"proxy": "https://nekoapi.com"
}
......@@ -24,6 +24,7 @@ import EditRedemption from './pages/Redemption/EditRedemption';
import TopUp from './pages/TopUp';
import Log from './pages/Log';
import Chat from './pages/Chat';
import {Layout} from "@douyinfe/semi-ui";
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
......@@ -86,6 +87,7 @@ function App() {
}, []);
return (
<Layout>
<Routes>
<Route
path='/'
......@@ -287,6 +289,7 @@ function App() {
<NotFound />
} />
</Routes>
</Layout>
);
}
......
......@@ -44,8 +44,8 @@ const Footer = () => {
{systemName} {process.env.REACT_APP_VERSION}{' '}
</a>
{' '}
<a href='https://github.com/songquanpeng' target='_blank'>
JustSong
<a href='https://github.com/Calcium-Ion' target='_blank'>
Calcium-Ion
</a>{' '}
构建,源代码遵循{' '}
<a href='https://opensource.org/licenses/mit-license.php'>
......
import React, { useContext, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { UserContext } from '../context/User';
import React, {useContext, useState} from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {UserContext} from '../context/User';
import { Button, Container, Dropdown, Icon, Menu, Segment } from 'semantic-ui-react';
import { API, getLogo, getSystemName, isAdmin, isMobile, showSuccess } from '../helpers';
import {Button, Container, Icon, Menu, Segment} from 'semantic-ui-react';
import {API, getLogo, getSystemName, isAdmin, isMobile, showSuccess} from '../helpers';
import '../index.css';
// Header Buttons
import {
IconAt,
IconHistogram,
IconGift,
IconKey,
IconUser,
IconLayers,
IconHelpCircle,
IconCreditCard,
IconSemiLogo,
IconHome,
IconImage
} from '@douyinfe/semi-icons';
import {Nav, Avatar, Dropdown, Layout, Switch} from '@douyinfe/semi-ui';
import {stringToColor} from "../helpers/render";
// HeaderBar Buttons
let headerButtons = [
{
name: '首页',
to: '/',
icon: 'home'
},
{
name: '渠道',
to: '/channel',
icon: 'sitemap',
admin: true
},
{
name: '令牌',
to: '/token',
icon: 'key'
},
{
name: '兑换',
to: '/redemption',
icon: 'dollar sign',
admin: true
},
{
name: '充值',
to: '/topup',
icon: 'cart'
},
{
name: '用户',
to: '/user',
icon: 'user',
admin: true
},
{
name: '日志',
to: '/log',
icon: 'book'
},
{
name: 'Midjourney',
to: '/midjourney',
icon: 'images outline'
},
{
name: '设置',
to: '/setting',
icon: 'setting'
},
{
name: '关于',
text: '关于',
itemKey: 'about',
to: '/about',
icon: 'info circle'
}
icon: <IconHelpCircle/>
},
];
if (localStorage.getItem('chat_link')) {
......@@ -71,7 +40,7 @@ if (localStorage.getItem('chat_link')) {
});
}
const Header = () => {
const HeaderBar = () => {
const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate();
......@@ -83,7 +52,7 @@ const Header = () => {
setShowSidebar(false);
await API.get('/api/user/logout');
showSuccess('注销成功!');
userDispatch({ type: 'logout' });
userDispatch({type: 'logout'});
localStorage.removeItem('user');
navigate('/login');
}
......@@ -109,7 +78,7 @@ const Header = () => {
}
return (
<Menu.Item key={button.name} as={Link} to={button.to}>
<Icon name={button.icon} />
<Icon name={button.icon}/>
{button.name}
</Menu.Item>
);
......@@ -130,7 +99,7 @@ const Header = () => {
borderTop: 'none',
height: '51px'
}
: { borderTop: 'none', height: '52px' }
: {borderTop: 'none', height: '52px'}
}
>
<Container>
......@@ -138,21 +107,22 @@ const Header = () => {
<img
src={logo}
alt='logo'
style={{ marginRight: '0.75em' }}
style={{marginRight: '0.75em'}}
/>
<div style={{ fontSize: '20px' }}>
<div style={{fontSize: '20px'}}>
<b>{systemName}</b>
</div>
</Menu.Item>
<Menu.Menu position='right'>
<Menu.Item onClick={toggleSidebar}>
<Icon name={showSidebar ? 'close' : 'sidebar'} />
<Icon name={showSidebar ? 'close' : 'sidebar'}/>
</Menu.Item>
</Menu.Menu>
</Container>
</Menu>
{showSidebar ? (
<Segment style={{ marginTop: 0, borderTop: '0' }}>
<Segment style={{marginTop: 0, borderTop: '0'}}>
<Menu secondary vertical style={{ width: '100%', margin: 0 }}>
{renderButtons(true)}
<Menu.Item>
......@@ -180,49 +150,81 @@ const Header = () => {
)}
</Menu.Item>
</Menu>
</Segment>
) : (
<></>
)}
</>
);
}
const switchMode = (model) => {
const body = document.body;
if (!model) {
body.removeAttribute('theme-mode');
} else {
body.setAttribute('theme-mode', 'dark');
}
};
return (
<>
<Menu borderless style={{ borderTop: 'none' }}>
<Container>
<Menu.Item as={Link} to='/' className={'hide-on-mobile'}>
<img src={logo} alt='logo' style={{ marginRight: '0.75em' }} />
<div style={{ fontSize: '20px' }}>
<b>{systemName}</b>
</div>
</Menu.Item>
{renderButtons(false)}
<Menu.Menu position='right'>
{userState.user ? (
<Dropdown
text={userState.user.username}
pointing
className='link item'
<Layout>
<div style={{width: '100%'}}>
<Nav
mode={'horizontal'}
// bodyStyle={{ height: 100 }}
renderWrapper={({itemElement, isSubNav, isInSubNav, props}) => {
const routerMap = {
about: "/about",
};
return (
<Link
style={{textDecoration: "none"}}
to={routerMap[props.itemKey]}
>
{itemElement}
</Link>
);
}}
selectedKeys={[]}
// items={headerButtons}
onSelect={key => console.log(key)}
footer={
<>
<Nav.Item itemKey={'about'} icon={<IconHelpCircle />} />
<Switch checkedText="🌞" size={'large'} uncheckedText="🌙" onChange={switchMode} />
{userState.user ?
<>
<Dropdown
position="bottomRight"
render={
<Dropdown.Menu>
<Dropdown.Item onClick={logout}>注销</Dropdown.Item>
<Dropdown.Item>退出</Dropdown.Item>
</Dropdown.Menu>
}
>
<Avatar size="small" color={stringToColor(userState.user.username)} style={{ margin: 4 }}>
{userState.user.username[0]}
</Avatar>
<span>{userState.user.username}</span>
</Dropdown>
) : (
<Menu.Item
name='登录'
as={Link}
to='/login'
className='btn btn-link'
/>
)}
</Menu.Menu>
</Container>
</Menu>
</>
:
<>
<Nav.Item itemKey={'login'} text={'登录'} icon={<IconKey />} />
<Nav.Item itemKey={'register'} text={'注册'} icon={<IconUser />} />
</>
}
</>
}
>
</Nav>
</div>
</Layout>
</>
);
};
export default Header;
export default HeaderBar;
import React, {useContext, useState} from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {UserContext} from '../context/User';
import {Button, Container, Icon, Menu, Segment} from 'semantic-ui-react';
import {API, getLogo, getSystemName, isAdmin, isMobile, showSuccess} from '../helpers';
import '../index.css';
import {
IconAt,
IconHistogram,
IconGift,
IconKey,
IconUser,
IconLayers,
IconSetting,
IconCreditCard,
IconSemiLogo,
IconHome,
IconImage
} from '@douyinfe/semi-icons';
import {Nav, Avatar, Dropdown, Layout} from '@douyinfe/semi-ui';
// HeaderBar Buttons
let headerButtons = [
{
text: '首页',
itemKey: 'home',
to: '/',
icon: <IconHome/>
},
{
text: '渠道',
itemKey: 'channel',
to: '/channel',
icon: <IconLayers/>,
admin: true
},
{
text: '令牌',
itemKey: 'token',
to: '/token',
icon: <IconKey/>
},
{
text: '兑换码',
itemKey: 'redemption',
to: '/redemption',
icon: <IconGift/>,
admin: true
},
{
text: '钱包',
itemKey: 'topup',
to: '/topup',
icon: <IconCreditCard/>
},
{
text: '用户管理',
itemKey: 'user',
to: '/user',
icon: <IconUser/>,
admin: true
},
{
text: '日志',
itemKey: 'log',
to: '/log',
icon: <IconHistogram/>
},
{
text: '绘图',
itemKey: 'midjourney',
to: '/midjourney',
icon: <IconImage/>
},
{
text: '设置',
itemKey: 'setting',
to: '/setting',
icon: <IconSetting/>
},
// {
// text: '关于',
// itemKey: 'about',
// to: '/about',
// icon: <IconAt/>
// }
];
if (localStorage.getItem('chat_link')) {
headerButtons.splice(1, 0, {
name: '聊天',
to: '/chat',
icon: 'comments'
});
}
const HeaderBar = () => {
const [userState, userDispatch] = useContext(UserContext);
let navigate = useNavigate();
const [selectedKeys, setSelectedKeys] = useState(['home']);
const [showSidebar, setShowSidebar] = useState(false);
const systemName = getSystemName();
const logo = getLogo();
async function logout() {
setShowSidebar(false);
await API.get('/api/user/logout');
showSuccess('注销成功!');
userDispatch({type: 'logout'});
localStorage.removeItem('user');
navigate('/login');
}
const toggleSidebar = () => {
setShowSidebar(!showSidebar);
};
const renderButtons = (isMobile) => {
return headerButtons.map((button) => {
if (button.admin && !isAdmin()) return <></>;
if (isMobile) {
return (
<Menu.Item
onClick={() => {
navigate(button.to);
setShowSidebar(false);
}}
>
{button.name}
</Menu.Item>
);
}
return (
<Menu.Item key={button.name} as={Link} to={button.to}>
<Icon name={button.icon}/>
{button.name}
</Menu.Item>
);
});
};
if (isMobile()) {
return (
<>
<Menu
borderless
size='large'
style={
showSidebar
? {
borderBottom: 'none',
marginBottom: '0',
borderTop: 'none',
height: '51px'
}
: {borderTop: 'none', height: '52px'}
}
>
<Container>
<Menu.Item as={Link} to='/'>
<img
src={logo}
alt='logo'
style={{marginRight: '0.75em'}}
/>
<div style={{fontSize: '20px'}}>
<b>{systemName}</b>
</div>
</Menu.Item>
<Menu.Menu position='right'>
<Menu.Item onClick={toggleSidebar}>
<Icon name={showSidebar ? 'close' : 'sidebar'}/>
</Menu.Item>
</Menu.Menu>
</Container>
</Menu>
{showSidebar ? (
<Segment style={{marginTop: 0, borderTop: '0'}}>
{/*<Menu secondary vertical style={{ width: '100%', margin: 0 }}>*/}
{/* {renderButtons(true)}*/}
{/* <Menu.Item>*/}
{/* {userState.user ? (*/}
{/* <Button onClick={logout}>注销</Button>*/}
{/* ) : (*/}
{/* <>*/}
{/* <Button*/}
{/* onClick={() => {*/}
{/* setShowSidebar(false);*/}
{/* navigate('/login');*/}
{/* }}*/}
{/* >*/}
{/* 登录*/}
{/* </Button>*/}
{/* <Button*/}
{/* onClick={() => {*/}
{/* setShowSidebar(false);*/}
{/* navigate('/register');*/}
{/* }}*/}
{/* >*/}
{/* 注册*/}
{/* </Button>*/}
{/* </>*/}
{/* )}*/}
{/* </Menu.Item>*/}
{/*</Menu>*/}
</Segment>
) : (
<></>
)}
</>
);
}
return (
<>
<Layout>
<div style={{height: '100%'}}>
<Nav
// mode={'horizontal'}
// bodyStyle={{ height: 100 }}
selectedKeys={selectedKeys}
renderWrapper={({itemElement, isSubNav, isInSubNav, props}) => {
const routerMap = {
home: "/",
channel: "/channel",
token: "/token",
redemption: "/redemption",
topup: "/topup",
user: "/user",
log: "/log",
midjourney: "/midjourney",
setting: "/setting",
about: "/about",
};
return (
<Link
style={{textDecoration: "none"}}
to={routerMap[props.itemKey]}
>
{itemElement}
</Link>
);
}}
items={headerButtons}
onSelect={key => {
console.log(key);
setSelectedKeys([key.itemKey]);
}}
header={{
logo: <IconSemiLogo style={{height: '36px', fontSize: 36}}/>,
text: 'NekoAPI'
}}
// footer={{
// text: '© 2021 NekoAPI',
// }}
>
<Nav.Footer collapseButton={true}>
</Nav.Footer>
</Nav>
</div>
</Layout>
</>
);
};
export default HeaderBar;
......@@ -56,3 +56,20 @@ export function renderQuotaWithPrompt(quota, digits) {
}
return '';
}
const colors = ['amber', 'blue', 'cyan', 'green', 'grey', 'indigo',
'light-blue', 'lime', 'orange', 'pink',
'purple', 'red', 'teal', 'violet', 'yellow'
]
export function stringToColor(str) {
let sum = 0;
// 对字符串中的每个字符进行操作
for (let i = 0; i < str.length; i++) {
// 将字符的ASCII值加到sum中
sum += str.charCodeAt(i);
}
// 使用模运算得到个位数
let i = sum % colors.length;
return colors[i];
}
\ No newline at end of file
......@@ -22,7 +22,7 @@ export function isRoot() {
export function getSystemName() {
let system_name = localStorage.getItem('system_name');
if (!system_name) return 'One API Midjourney';
if (!system_name) return 'Neko API';
return system_name;
}
......
......@@ -6,6 +6,8 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
scrollbar-width: none;
color: var(--semi-color-text-0) !important;
background-color: var( --semi-color-bg-0) !important;
}
body::-webkit-scrollbar {
......@@ -16,8 +18,27 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
.semi-navigation-vertical {
/*display: flex;*/
/*flex-direction: column;*/
}
.semi-navigation-item {
margin-bottom: 0;
}
.semi-navigation-vertical {
/*flex: 0 0 auto;*/
/*display: flex;*/
/*flex-direction: column;*/
/*width: 100%;*/
height: 100vh;
overflow: hidden;
}
.main-content {
padding: 4px;
height: 100%;
}
.small-icon .icon {
......
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { Container } from 'semantic-ui-react';
import {BrowserRouter} from 'react-router-dom';
import {Container} from 'semantic-ui-react';
import App from './App';
import Header from './components/Header';
import HeaderBar from './components/HeaderBar';
import Footer from './components/Footer';
import 'semantic-ui-css/semantic.min.css';
import './index.css';
import { UserProvider } from './context/User';
import { ToastContainer } from 'react-toastify';
import {UserProvider} from './context/User';
import {ToastContainer} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { StatusProvider } from './context/Status';
import {StatusProvider} from './context/Status';
import {Layout} from "@douyinfe/semi-ui";
import SiderBar from "./components/SiderBar";
const root = ReactDOM.createRoot(document.getElementById('root'));
const {Sider, Content, Header} = Layout;
root.render(
<React.StrictMode>
<StatusProvider>
<UserProvider>
<BrowserRouter>
<Header />
<Container className={'main-content'}>
<App />
</Container>
<ToastContainer />
<Footer />
<Layout>
<Sider>
<SiderBar/>
</Sider>
<Layout>
<Header>
<HeaderBar/>
</Header>
<Content
style={{
padding: '24px',
}}
>
<App/>
</Content>
<Footer></Footer>
</Layout>
</Layout>
<ToastContainer/>
{/*<Footer />*/}
</BrowserRouter>
</UserProvider>
</StatusProvider>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment