feat: main content

main
fadeaway 6 months ago
parent a761ea3d0d
commit 72c4fa2c77

24001
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -19,6 +19,7 @@
"@arco-themes/react-arco-pro": "^0.0.7", "@arco-themes/react-arco-pro": "^0.0.7",
"@loadable/component": "^5.13.2", "@loadable/component": "^5.13.2",
"@turf/turf": "^6.5.0", "@turf/turf": "^6.5.0",
"ahooks": "^3.8.0",
"arco-design-pro": "^2.8.1", "arco-design-pro": "^2.8.1",
"axios": "^0.24.0", "axios": "^0.24.0",
"bizcharts": "^4.1.11", "bizcharts": "^4.1.11",

@ -6,6 +6,8 @@
padding: 28px 28px 33px; padding: 28px 28px 33px;
background: #101011; background: #101011;
color: #ffffff99; color: #ffffff99;
width: 100%;
box-sizing: border-box;
.nav { .nav {
.center(); .center();

@ -2,16 +2,22 @@
@import '@/style/common.less'; @import '@/style/common.less';
.header { .header {
position: fixed;
left: 0;
top: 0;
.center(); .center();
padding-left: 20px; padding-left: 20px;
padding-right: 20px; padding-right: 20px;
box-sizing: border-box; box-sizing: border-box;
width: 100%;
z-index: 99;
&.light { &.light {
background-color: #fff; background-color: #fff;
box-shadow: 0px -1px 0px 0px #00000014 inset;
} }
&.dark { &.dark {
// background-color: #000; background-color: #000;
} }
.cont { .cont {

@ -41,7 +41,7 @@ function LoginModal(props: LoginModalProps, ref: React.Ref<any>) {
setSubmiting(true); setSubmiting(true);
const { userName, password } = values; const { userName, password } = values;
request request
.post(`/user/user/login/${userName}/${password}`, values) .post(`/user/user/login/username`, values)
.then((res: any) => { .then((res: any) => {
if (res?.code === 200 && res?.data?.token) { if (res?.code === 200 && res?.data?.token) {
Message.success('登录成功'); Message.success('登录成功');

@ -0,0 +1,76 @@
import React from 'react';
import {
Typography,
Card,
Table,
Form,
Grid,
Input,
Select,
Button,
Space,
DatePicker,
} from '@arco-design/web-react';
import type { TableProps } from '@arco-design/web-react';
import styles from './style/index.module.less';
export interface SearchTableProps extends TableProps {
searchFields: any[];
}
const { Item: FormItem, useForm } = Form;
const { Row, Col } = Grid;
const { RangePicker } = DatePicker;
function SearchTable(props?: SearchTableProps) {
const { columns, data, searchFields } = props || {};
const [form] = useForm();
const handleSearch = () => {
//
};
const handleReset = () => {
form.resetFields();
};
return (
<>
{searchFields && searchFields.length > 0 && (
<Form form={form} className={styles.mb24}>
<Row gutter={12}>
{searchFields.map((item) => {
return (
<Col flex={item.width || '200px'} key={item.field}>
<FormItem field={item.field} noStyle>
{item.ele}
</FormItem>
</Col>
);
})}
<Col flex={1} className={styles.alignRight}>
<Space size={12}>
<Button type="primary" onClick={handleSearch}>
</Button>
<Button onClick={handleReset}></Button>
</Space>
</Col>
</Row>
</Form>
)}
<Table
columns={columns}
data={data}
rowSelection={{
type: 'checkbox',
checkAll: true,
}}
border={false}
pagePosition="bl"
/>
</>
);
}
export default SearchTable;

@ -0,0 +1,11 @@
.mb24 {
margin-bottom: 24px;
}
.w200 {
width: 200px;
}
.alignRight {
text-align: right;
}

@ -1,17 +1,20 @@
import React, { useState, useRef, useMemo, useEffect } from 'react'; import React, { useState, useRef, useMemo, useEffect, Children } from 'react';
import { Switch, Route, Redirect, useHistory } from 'react-router-dom'; import { Switch, Route, Redirect, useHistory } from 'react-router-dom';
import { Layout, Menu, Breadcrumb, Spin } from '@arco-design/web-react'; import { Layout, Menu, Breadcrumb, Spin } from '@arco-design/web-react';
import cs from 'classnames'; import cs from 'classnames';
import { import {
IconDashboard, IconBulb,
IconTag, IconUserGroup,
IconFileAudio,
IconDesktop,
IconTool,
IconMenuFold, IconMenuFold,
IconMenuUnfold, IconMenuUnfold,
} from '@arco-design/web-react/icon'; } from '@arco-design/web-react/icon';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import qs from 'query-string'; import qs from 'query-string';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import Navbar from './components/NavBar'; import Header from './components/Header';
import Footer from './components/Footer'; import Footer from './components/Footer';
import useRoute, { IRoute } from '@/routes'; import useRoute, { IRoute } from '@/routes';
import useLocale from './utils/useLocale'; import useLocale from './utils/useLocale';
@ -28,10 +31,20 @@ const Content = Layout.Content;
function getIconFromKey(key) { function getIconFromKey(key) {
switch (key) { switch (key) {
case 'dashboard': case 'index':
return <IconDashboard className={styles.icon} />; return <IconBulb className={styles.icon} />;
case 'example': case 'artists':
return <IconTag className={styles.icon} />; return <IconUserGroup className={styles.icon} />;
case 'albumWorks':
return <IconFileAudio className={styles.icon} />;
case 'financial':
return <IconDesktop className={styles.icon} />;
case 'settings':
return <IconTool className={styles.icon} />;
// case 'dashboard':
// return <IconDashboard className={styles.icon} />;
// case 'example':
// return <IconTag className={styles.icon} />;
default: default:
return <div className={styles['icon-empty']} />; return <div className={styles['icon-empty']} />;
} }
@ -157,10 +170,6 @@ function PageLayout() {
setCollapsed((collapsed) => !collapsed); setCollapsed((collapsed) => !collapsed);
} }
const paddingLeft = showMenu ? { paddingLeft: menuWidth } : {};
const paddingTop = showNavbar ? { paddingTop: navbarHeight } : {};
const paddingStyle = { ...paddingLeft, ...paddingTop };
function updateMenuStatus() { function updateMenuStatus() {
const pathKeys = pathname.split('/'); const pathKeys = pathname.split('/');
const newSelectedKeys: string[] = []; const newSelectedKeys: string[] = [];
@ -186,31 +195,22 @@ function PageLayout() {
setBreadCrumb(routeConfig || []); setBreadCrumb(routeConfig || []);
updateMenuStatus(); updateMenuStatus();
}, [pathname]); }, [pathname]);
return ( return (
<Layout className={styles.layout}> <Layout className={styles.layout}>
<div <Header mode="light" />
className={cs(styles['layout-navbar'], { <Layout className={styles.layoutContent}>
[styles['layout-navbar-hidden']]: !showNavbar,
})}
>
<Navbar show={showNavbar} />
</div>
{userLoading ? (
<Spin className={styles['spin']} />
) : (
<Layout>
{showMenu && ( {showMenu && (
<Sider <Sider
className={styles['layout-sider']} className={styles.layoutSider}
width={menuWidth} width={menuWidth}
collapsed={collapsed} collapsed={collapsed}
onCollapse={setCollapsed} onCollapse={setCollapsed}
trigger={null} trigger={null}
collapsible collapsible
breakpoint="xl" breakpoint="xl"
style={paddingTop}
> >
<div className={styles['menu-wrapper']}> <div className={styles.menuWrapper}>
<Menu <Menu
collapse={collapsed} collapse={collapsed}
onClickMenuItem={onClickMenuItem} onClickMenuItem={onClickMenuItem}
@ -221,15 +221,14 @@ function PageLayout() {
{renderRoutes(locale)(routes, 1)} {renderRoutes(locale)(routes, 1)}
</Menu> </Menu>
</div> </div>
<div className={styles['collapse-btn']} onClick={toggleCollapse}> <div className={styles.collapseBtn} onClick={toggleCollapse}>
{collapsed ? <IconMenuUnfold /> : <IconMenuFold />} {collapsed ? <IconMenuUnfold /> : <IconMenuFold />}
</div> </div>
</Sider> </Sider>
)} )}
<Layout className={styles['layout-content']} style={paddingStyle}> <Layout className={styles.layoutMainContent}>
<div className={styles['layout-content-wrapper']}>
{!!breadcrumb.length && ( {!!breadcrumb.length && (
<div className={styles['layout-breadcrumb']}> <div className={styles.layoutBreadcrumb}>
<Breadcrumb> <Breadcrumb>
{breadcrumb.map((node, index) => ( {breadcrumb.map((node, index) => (
<Breadcrumb.Item key={index}> <Breadcrumb.Item key={index}>
@ -259,11 +258,9 @@ function PageLayout() {
/> />
</Switch> </Switch>
</Content> </Content>
</div>
{showFooter && <Footer />}
</Layout> </Layout>
</Layout> </Layout>
)} {showFooter && <Footer />}
</Layout> </Layout>
); );
} }

@ -1,5 +1,10 @@
const i18n = { const i18n = {
'en-US': { 'en-US': {
'menu.index': 'Index',
'menu.artists': 'Artists',
'menu.albumWorks': 'Album Works',
'menu.financial': 'Financial',
'menu.settings': 'Settings',
'menu.dashboard': 'Dashboard', 'menu.dashboard': 'Dashboard',
'menu.dashboard.workplace': 'Workplace', 'menu.dashboard.workplace': 'Workplace',
'menu.user.info': 'User Info', 'menu.user.info': 'User Info',
@ -38,6 +43,11 @@ const i18n = {
'navbar.search.placeholder': 'Please search', 'navbar.search.placeholder': 'Please search',
}, },
'zh-CN': { 'zh-CN': {
'menu.index': '首页',
'menu.artists': '厂牌艺人',
'menu.albumWorks': '专辑作品',
'menu.financial': '财务报表',
'menu.settings': '个人设置',
'menu.dashboard': '仪表盘', 'menu.dashboard': '仪表盘',
'menu.dashboard.workplace': '工作台', 'menu.dashboard.workplace': '工作台',
'menu.user.info': '用户信息', 'menu.user.info': '用户信息',

@ -0,0 +1,13 @@
import React from 'react';
import { Typography, Card } from '@arco-design/web-react';
function AlbumWorks() {
return (
<Card>
<Typography.Title heading={6}></Typography.Title>
<Typography.Text>You can add content here :)</Typography.Text>
</Card>
);
}
export default AlbumWorks;

@ -0,0 +1,146 @@
import React, { useState } from 'react';
import {
Input,
Select,
Button,
Avatar,
Space,
DatePicker,
} from '@arco-design/web-react';
import { useAsyncEffect } from 'ahooks';
import { IconDelete } from '@arco-design/web-react/icon';
import SearchTable from '@/components/SearchTable';
const { RangePicker } = DatePicker;
const columns = [
{
title: '名称',
dataIndex: 'name',
render: (name) => {
return (
<Space size={8}>
<Avatar size={24} />
<span>{name}</span>
</Space>
);
},
},
{
title: '类型',
dataIndex: 'type',
},
{
title: '粉丝数',
dataIndex: 'fans',
sorter: true,
},
{
title: '专辑数',
dataIndex: 'album',
sorter: true,
},
{
title: '歌曲数',
dataIndex: 'song',
sorter: true,
},
{
title: '加入时间',
dataIndex: 'time',
},
{
title: '操作',
dataIndex: 'operation',
render: (_, record) => (
<>
<Button type="text"></Button>
<Button type="text"></Button>
<Button type="text" status="danger" icon={<IconDelete />}>
</Button>
</>
),
},
];
const defaultData = [
{
key: '1',
name: 'Jane Doe',
type: '个人',
fans: '1.2万',
album: '2张',
song: '30首',
time: '2024.05.26',
},
{
key: '2',
name: 'Jane Doe',
type: '个人',
fans: '1.2万',
album: '2张',
song: '30首',
time: '2024.05.26',
},
{
key: '3',
name: 'Jane Doe',
type: '个人',
fans: '1.2万',
album: '2张',
song: '30首',
time: '2024.05.26',
},
{
key: '4',
name: 'Jane Doe',
type: '个人',
fans: '1.2万',
album: '2张',
song: '30首',
time: '2024.05.26',
},
{
key: '5',
name: 'Jane Doe',
type: '个人',
fans: '1.2万',
album: '2张',
song: '30首',
time: '2024.05.26',
},
];
const options = [
{ label: '个人', value: 0 },
{ label: '团体', value: 1 },
];
const searchFields = [
{
field: 'q',
ele: <Input placeholder="输入搜索内容" allowClear />,
},
{
field: 'q2',
ele: <Select placeholder="请选择" options={options} allowClear />,
},
{
field: 'q3',
ele: <RangePicker mode="month" allowClear />,
},
];
function Artists() {
const [data, setData] = useState<any>();
useAsyncEffect(async () => {
setData(defaultData);
}, []);
return (
<SearchTable searchFields={searchFields} columns={columns} data={data} />
);
}
export default Artists;

@ -0,0 +1,13 @@
import React from 'react';
import { Typography, Card } from '@arco-design/web-react';
function Index() {
return (
<Card>
<Typography.Title heading={6}></Typography.Title>
<Typography.Text>You can add content here :)</Typography.Text>
</Card>
);
}
export default Index;

@ -13,8 +13,20 @@ export type IRoute = AuthParams & {
export const routes: IRoute[] = [ export const routes: IRoute[] = [
{ {
name: 'menu.dashboard', name: 'menu.index',
key: 'dashboard', key: 'index',
},
{
name: 'menu.artists',
key: 'artists',
},
{
name: 'menu.albumWorks',
key: 'albumWorks',
},
{
name: 'menu.financial',
key: 'financial',
children: [ children: [
{ {
name: 'menu.dashboard.workplace', name: 'menu.dashboard.workplace',
@ -23,9 +35,29 @@ export const routes: IRoute[] = [
], ],
}, },
{ {
name: 'Example', name: 'menu.settings',
key: 'example', key: 'settings',
children: [
{
name: 'menu.dashboard.workplace',
key: 'dashboard/workplace',
},
],
}, },
// {
// name: 'menu.dashboard',
// key: 'dashboard',
// children: [
// {
// name: 'menu.dashboard.workplace',
// key: 'dashboard/workplace',
// },
// ],
// },
// {
// name: 'Example',
// key: 'example',
// },
]; ];
export const getName = (path: string, routes) => { export const getName = (path: string, routes) => {

@ -4,5 +4,5 @@
"menu": true, "menu": true,
"footer": true, "footer": true,
"themeColor": "#165DFF", "themeColor": "#165DFF",
"menuWidth": 220 "menuWidth": 180
} }

@ -1,29 +1,44 @@
@import '@/style/variable.less';
@import '@/style/common.less';
@nav-size-height: 60px; @nav-size-height: 60px;
@layout-max-width: 1100px; @layout-max-width: 1100px;
.layout { .layout {
width: 100%; width: 100%;
height: 100%; min-height: 100vh;
margin: auto;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
background-color: #fff;
} }
.layout-navbar { .layoutBreadcrumb {
position: fixed; margin-bottom: 22px;
width: 100%; }
min-width: @layout-max-width;
top: 0;
left: 0;
height: @nav-size-height;
z-index: 100;
&-hidden { .spin {
height: 0; display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: calc(100vh - @nav-size-height);
} }
.layoutContent {
flex: 1;
width: @CONTENT_WIDTH;
display: flex;
padding-top: 72px;
position: relative;
} }
.layout-sider { .layoutContent .layoutSider {
position: fixed; position: sticky;
height: 100%; height: calc(100vh - 72px - 103px);
top: 0; top: 72px;
left: 0; left: 0;
z-index: 99; z-index: 99;
box-sizing: border-box; box-sizing: border-box;
@ -59,7 +74,7 @@
overflow-y: hidden; overflow-y: hidden;
} }
.collapse-btn { .collapseBtn {
height: 24px; height: 24px;
width: 24px; width: 24px;
background-color: var(--color-fill-1); background-color: var(--color-fill-1);
@ -80,7 +95,7 @@
} }
} }
.menu-wrapper { .menuWrapper {
overflow: auto; overflow: auto;
height: 100%; height: 100%;
@ -102,37 +117,11 @@
} }
} }
.icon { .layoutMainContent {
font-size: 18px; background-color: #fff;
vertical-align: text-bottom;
}
.icon-empty {
width: 12px;
height: 18px;
display: inline-block;
}
.layout-content {
background-color: var(--color-fill-2);
min-width: @layout-max-width;
min-height: 100vh;
transition: padding-left 0.2s; transition: padding-left 0.2s;
box-sizing: border-box; box-sizing: border-box;
} padding-top: 15px;
padding-left: 30px;
.layout-content-wrapper { padding-bottom: 50px;
padding: 16px 20px 0px 20px;
}
.layout-breadcrumb {
margin-bottom: 16px;
}
.spin {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: calc(100vh - @nav-size-height);
} }

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save