@stdex/create 0.0.0-stage → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +66 -2
- package/README_EN.md +67 -0
- package/package.json +40 -3
- package/src/eslint.config.js +2 -0
- package/src/index.js +233 -0
- package/templates/base/eslint.config.js +2 -0
- package/templates/base/index.js +3 -0
- package/templates/base/package.json +16 -0
- package/templates/miniprogram/README.md +27 -0
- package/templates/miniprogram/_gitignore +3 -0
- package/templates/miniprogram/eslint.config.cjs +25 -0
- package/templates/miniprogram/gulpfile.js +81 -0
- package/templates/miniprogram/package.json +22 -0
- package/templates/miniprogram/project.config.json +31 -0
- package/templates/miniprogram/project.private.config.json +8 -0
- package/templates/miniprogram/src/app.json +16 -0
- package/templates/miniprogram/src/app.scss +0 -0
- package/templates/miniprogram/src/app.ts +6 -0
- package/templates/miniprogram/src/lib/app-data.ts +46 -0
- package/templates/miniprogram/src/pages/index/index.json +3 -0
- package/templates/miniprogram/src/pages/index/index.scss +11 -0
- package/templates/miniprogram/src/pages/index/index.ts +13 -0
- package/templates/miniprogram/src/pages/index/index.wxml +1 -0
- package/templates/miniprogram/src/sitemap.json +9 -0
- package/templates/miniprogram/tsconfig.json +11 -0
- package/templates/miniprogram/typings/index.d.ts +5 -0
- package/templates/miniprogram/typings/types/index.d.ts +1 -0
- package/templates/miniprogram/typings/types/wx/index.d.ts +162 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.api.d.ts +32387 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.app.d.ts +400 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.behavior.d.ts +68 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.canvas.d.ts +2623 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.cloud.d.ts +1016 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.component.d.ts +700 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.event.d.ts +1436 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.page.d.ts +272 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.phys3D.d.ts +409 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.wasm.d.ts +152 -0
- package/templates/miniprogram/typings/types/wx/lib.wx.xr-frame.d.ts +15631 -0
- package/templates/miniprogram/variables.json +11 -0
- package/templates/service/README.md +64 -0
- package/templates/service/_env +1 -0
- package/templates/service/_gitignore +5 -0
- package/templates/service/eslint.config.js +2 -0
- package/templates/service/package.json +48 -0
- package/templates/service/prisma/schema.prisma +40 -0
- package/templates/service/prisma/sql/demoTypedSql.sql +1 -0
- package/templates/service/prisma.config.ts +12 -0
- package/templates/service/setup.js +4 -0
- package/templates/service/src/cache.ts +28 -0
- package/templates/service/src/config.ts +49 -0
- package/templates/service/src/controllers/common.ts +43 -0
- package/templates/service/src/controllers/demo-school.ts +7 -0
- package/templates/service/src/controllers/demo-student.ts +7 -0
- package/templates/service/src/controllers/index.ts +15 -0
- package/templates/service/src/database.ts +52 -0
- package/templates/service/src/index.ts +15 -0
- package/templates/service/src/logger.ts +10 -0
- package/templates/service/src/routes/demo-school.ts +9 -0
- package/templates/service/src/routes/index.ts +3 -0
- package/templates/service/src/routes/router.ts +42 -0
- package/templates/service/src/tasks/common.ts +25 -0
- package/templates/service/src/tasks/demo-school.ts +10 -0
- package/templates/service/src/tasks/index.ts +1 -0
- package/templates/service/tsconfig.build.json +9 -0
- package/templates/service/tsconfig.json +10 -0
- package/templates/site/_gitignore +3 -0
- package/templates/site/_pnpm-workspace.yaml +4 -0
- package/templates/site/eslint.config.js +19 -0
- package/templates/site/package.json +41 -0
- package/templates/site/public/favicon.png +0 -0
- package/templates/site/src/app/header.tsx +31 -0
- package/templates/site/src/app/index.tsx +38 -0
- package/templates/site/src/app/layout.tsx +63 -0
- package/templates/site/src/app/menu.tsx +115 -0
- package/templates/site/src/app/router.tsx +50 -0
- package/templates/site/src/app/style.css +9 -0
- package/templates/site/src/assets/logo.png +0 -0
- package/templates/site/src/components/Breadcrumb.tsx +47 -0
- package/templates/site/src/components/DevStatus.tsx +14 -0
- package/templates/site/src/index.html +13 -0
- package/templates/site/src/index.tsx +5 -0
- package/templates/site/src/lib/config.ts +6 -0
- package/templates/site/src/lib/init-dayjs.ts +8 -0
- package/templates/site/src/lib/logging.ts +5 -0
- package/templates/site/src/views/dashboard.tsx +7 -0
- package/templates/site/src/views/school-detail.tsx +13 -0
- package/templates/site/src/views/school-ranking.tsx +7 -0
- package/templates/site/src/views/schools.tsx +43 -0
- package/templates/site/src/views/setting.tsx +7 -0
- package/templates/site/src/views/student-score.tsx +7 -0
- package/templates/site/src/views/students.tsx +7 -0
- package/templates/site/tsconfig.json +10 -0
- package/templates/site/vite.config.js +60 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ImageLogo from '@/assets/logo.png'
|
|
2
|
+
import { DevStatus } from '@/components/DevStatus'
|
|
3
|
+
|
|
4
|
+
export function AppHeader() {
|
|
5
|
+
return <div css={s.wrap}>
|
|
6
|
+
<img css={s.logo} src={ImageLogo} />
|
|
7
|
+
<div css={s.siteName}>演示项目</div>
|
|
8
|
+
<DevStatus />
|
|
9
|
+
</div>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const s = {
|
|
13
|
+
wrap: css`
|
|
14
|
+
align-items: center;
|
|
15
|
+
display: flex;
|
|
16
|
+
padding: 8px 24px;
|
|
17
|
+
`,
|
|
18
|
+
|
|
19
|
+
logo: css`
|
|
20
|
+
height: 42px;
|
|
21
|
+
width: 42px;
|
|
22
|
+
margin-right: 20px;
|
|
23
|
+
`,
|
|
24
|
+
|
|
25
|
+
siteName: css`
|
|
26
|
+
flex: 1;
|
|
27
|
+
font-size: 16px;
|
|
28
|
+
letter-spacing: 1px;
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
`,
|
|
31
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { StyleProvider } from '@ant-design/cssinjs'
|
|
2
|
+
import { EmotionCacheProvider } from '@stdex/utils-frontend/react/emotion.js'
|
|
3
|
+
import { ConfigProvider, App as AntdApp } from 'antd'
|
|
4
|
+
import zhCn from 'antd/locale/zh_CN'
|
|
5
|
+
import { createRoot } from 'react-dom/client'
|
|
6
|
+
import { RouterProvider } from 'react-router/dom'
|
|
7
|
+
import { initDayJs } from '@/lib/init-dayjs.js'
|
|
8
|
+
import { router } from './router'
|
|
9
|
+
|
|
10
|
+
import './style.css'
|
|
11
|
+
|
|
12
|
+
initDayJs()
|
|
13
|
+
|
|
14
|
+
export function renderApp() {
|
|
15
|
+
function AntdWrapper() {
|
|
16
|
+
return <EmotionCacheProvider>
|
|
17
|
+
<StyleProvider layer>
|
|
18
|
+
<ConfigProvider
|
|
19
|
+
locale={zhCn}
|
|
20
|
+
theme={{
|
|
21
|
+
token: {},
|
|
22
|
+
components: {},
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<AntdApp>
|
|
26
|
+
<RouterProvider router={router} />
|
|
27
|
+
</AntdApp>
|
|
28
|
+
</ConfigProvider>
|
|
29
|
+
</StyleProvider>
|
|
30
|
+
</EmotionCacheProvider>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const $root = document.createElement('div')
|
|
34
|
+
$root.id = 'root'
|
|
35
|
+
document.body.appendChild($root)
|
|
36
|
+
const root = createRoot($root)
|
|
37
|
+
root.render(<AntdWrapper />)
|
|
38
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Layout } from 'antd'
|
|
2
|
+
import { Outlet } from 'react-router'
|
|
3
|
+
import { AppHeader } from './header'
|
|
4
|
+
import { AppMenu } from './menu'
|
|
5
|
+
|
|
6
|
+
export function AppLayout() {
|
|
7
|
+
const [siderCollapsed, setSiderCollapsed] = useState(false)
|
|
8
|
+
|
|
9
|
+
return <Layout css={s.root}>
|
|
10
|
+
<Layout.Header css={s.header}>
|
|
11
|
+
<AppHeader />
|
|
12
|
+
</Layout.Header>
|
|
13
|
+
|
|
14
|
+
<Layout>
|
|
15
|
+
<Layout.Sider
|
|
16
|
+
css={s.sider}
|
|
17
|
+
theme="light"
|
|
18
|
+
collapsible
|
|
19
|
+
collapsed={siderCollapsed}
|
|
20
|
+
onCollapse={setSiderCollapsed}
|
|
21
|
+
>
|
|
22
|
+
<AppMenu />
|
|
23
|
+
</Layout.Sider>
|
|
24
|
+
|
|
25
|
+
<Layout css={s.main}>
|
|
26
|
+
<Outlet />
|
|
27
|
+
</Layout>
|
|
28
|
+
</Layout>
|
|
29
|
+
</Layout>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const s = {
|
|
33
|
+
root: css`
|
|
34
|
+
height: 100vh;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
`,
|
|
37
|
+
|
|
38
|
+
header: css`
|
|
39
|
+
background-color: #fff;
|
|
40
|
+
border-bottom: 1px solid #f5f5f5;
|
|
41
|
+
padding: 0;
|
|
42
|
+
height: auto;
|
|
43
|
+
line-height: unset;
|
|
44
|
+
`,
|
|
45
|
+
|
|
46
|
+
sider: css`
|
|
47
|
+
padding-top: 4px;
|
|
48
|
+
.ant-layout-sider-children {
|
|
49
|
+
overflow-y: auto;
|
|
50
|
+
}
|
|
51
|
+
`,
|
|
52
|
+
|
|
53
|
+
main: css`
|
|
54
|
+
& > .page {
|
|
55
|
+
background-color: #fff;
|
|
56
|
+
border-radius: 3px;
|
|
57
|
+
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.05);
|
|
58
|
+
flex: 1;
|
|
59
|
+
margin: 16px;
|
|
60
|
+
padding: 16px;
|
|
61
|
+
}
|
|
62
|
+
`,
|
|
63
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DashboardOutlined,
|
|
3
|
+
ApartmentOutlined,
|
|
4
|
+
UserOutlined,
|
|
5
|
+
SettingOutlined,
|
|
6
|
+
} from '@ant-design/icons'
|
|
7
|
+
import { Menu, type MenuProps } from 'antd'
|
|
8
|
+
import { useNavigate, useMatches } from 'react-router'
|
|
9
|
+
|
|
10
|
+
type MenuInfo = Parameters<Required<MenuProps>['onClick']>[0]
|
|
11
|
+
type MenuItem = Required<MenuProps>['items'][number]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 菜单对应的页面路由,不在此表的菜单则不跳转
|
|
15
|
+
*/
|
|
16
|
+
const routes = [
|
|
17
|
+
{ key: 'dashboard', path: '/' },
|
|
18
|
+
{ key: 'schools', path: '/schools' },
|
|
19
|
+
{ key: 'school-ranking', path: '/schools/ranking' },
|
|
20
|
+
{ key: 'students', path: '/students' },
|
|
21
|
+
{ key: 'student-score', path: '/students/score' },
|
|
22
|
+
{ key: 'setting', path: '/setting' },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const menuItems: MenuItem[] = [
|
|
26
|
+
{
|
|
27
|
+
key: 'dashboard',
|
|
28
|
+
label: '信息概览',
|
|
29
|
+
icon: <DashboardOutlined />,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
key: 'school-manage',
|
|
34
|
+
label: '学校管理',
|
|
35
|
+
icon: <ApartmentOutlined />,
|
|
36
|
+
children: [
|
|
37
|
+
{
|
|
38
|
+
key: 'schools',
|
|
39
|
+
label: '学校列表',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
key: 'school-ranking',
|
|
43
|
+
label: '学校排名',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
key: 'student-manage',
|
|
50
|
+
label: '学生管理',
|
|
51
|
+
icon: <UserOutlined />,
|
|
52
|
+
children: [
|
|
53
|
+
{
|
|
54
|
+
key: 'students',
|
|
55
|
+
label: '学生列表',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: 'student-score',
|
|
59
|
+
label: '成绩管理',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
{
|
|
65
|
+
key: 'setting',
|
|
66
|
+
label: '系统设置',
|
|
67
|
+
icon: <SettingOutlined />,
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 返回指定 key 的菜单项的完整 keyPath
|
|
73
|
+
*/
|
|
74
|
+
function getKeyPath(key: string, items?: MenuItem[]): string[] {
|
|
75
|
+
items ??= menuItems
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
if (item?.key === undefined) continue
|
|
78
|
+
if (item.key === key) return [key]
|
|
79
|
+
if ('children' in item && item.children) {
|
|
80
|
+
const subKeyPath = getKeyPath(key, item.children)
|
|
81
|
+
if (subKeyPath.length) return [String(item.key), ...subKeyPath]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return []
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function AppMenu() {
|
|
88
|
+
const navigate = useNavigate()
|
|
89
|
+
const matches = useMatches()
|
|
90
|
+
|
|
91
|
+
const { defaultSelectedKeys, defaultOpenKeys } = useMemo(() => {
|
|
92
|
+
const currentPath = matches[matches.length - 1]?.pathname ?? null
|
|
93
|
+
const currentRoute = routes.find(v => v.path === currentPath)
|
|
94
|
+
if (currentRoute) {
|
|
95
|
+
return {
|
|
96
|
+
defaultSelectedKeys: [currentRoute.key],
|
|
97
|
+
defaultOpenKeys: getKeyPath(currentRoute.key).filter(key => key !== currentRoute.key),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { defaultSelectedKeys: [], defaultOpenKeys: [] }
|
|
101
|
+
}, [matches])
|
|
102
|
+
|
|
103
|
+
function onClick(info: MenuInfo) {
|
|
104
|
+
const route = routes.find(v => v.key === info.key)
|
|
105
|
+
if (route) void navigate(route.path)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return <Menu
|
|
109
|
+
mode="inline"
|
|
110
|
+
items={menuItems}
|
|
111
|
+
defaultSelectedKeys={defaultSelectedKeys}
|
|
112
|
+
defaultOpenKeys={defaultOpenKeys}
|
|
113
|
+
onClick={onClick}
|
|
114
|
+
/>
|
|
115
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createBrowserRouter, Navigate } from 'react-router'
|
|
2
|
+
import { Dashboard } from '@/views/dashboard'
|
|
3
|
+
import { SchoolDetail } from '@/views/school-detail'
|
|
4
|
+
import { SchoolRanking } from '@/views/school-ranking'
|
|
5
|
+
import { Schools } from '@/views/schools'
|
|
6
|
+
import { Setting } from '@/views/setting'
|
|
7
|
+
import { StudentScore } from '@/views/student-score'
|
|
8
|
+
import { Students } from '@/views/students'
|
|
9
|
+
import { AppLayout } from './layout'
|
|
10
|
+
|
|
11
|
+
export const router = createBrowserRouter([
|
|
12
|
+
{
|
|
13
|
+
path: '/',
|
|
14
|
+
Component: AppLayout,
|
|
15
|
+
children: [
|
|
16
|
+
{
|
|
17
|
+
index: true,
|
|
18
|
+
Component: Dashboard,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
path: '/schools',
|
|
22
|
+
Component: Schools,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
path: '/schools/ranking',
|
|
26
|
+
Component: SchoolRanking,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: '/schools/:id',
|
|
30
|
+
Component: SchoolDetail,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
path: '/students',
|
|
34
|
+
Component: Students,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
path: '/students/score',
|
|
38
|
+
Component: StudentScore,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '/setting',
|
|
42
|
+
Component: Setting,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: '*',
|
|
46
|
+
element: <Navigate to="/" />,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
])
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Breadcrumb as AntdBreadcrumb, type BreadcrumbProps } from 'antd'
|
|
2
|
+
import { Link } from 'react-router'
|
|
3
|
+
|
|
4
|
+
// 字符串是纯文本节点;对象是可点击节点
|
|
5
|
+
type Item =
|
|
6
|
+
| string
|
|
7
|
+
| {
|
|
8
|
+
path: string
|
|
9
|
+
title: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type ItemRender = Required<BreadcrumbProps>['itemRender']
|
|
13
|
+
|
|
14
|
+
export function Breadcrumb({ items }: { items: (string | Item)[] }) {
|
|
15
|
+
const antdItems = useMemo(
|
|
16
|
+
() =>
|
|
17
|
+
items.map(item =>
|
|
18
|
+
typeof item === 'string' ? { title: item } : { href: item.path, title: item.title },
|
|
19
|
+
),
|
|
20
|
+
[items],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const itemRender: ItemRender = item => {
|
|
24
|
+
return item.href ? (
|
|
25
|
+
<Link to={item.href} css={s.link}>
|
|
26
|
+
{item.title}
|
|
27
|
+
</Link>
|
|
28
|
+
) : (
|
|
29
|
+
<span>{item.title}</span>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return <AntdBreadcrumb
|
|
34
|
+
css={{ margin: '16px 16px 0' }}
|
|
35
|
+
itemRender={itemRender}
|
|
36
|
+
items={antdItems}
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const s = {
|
|
41
|
+
link: css`
|
|
42
|
+
&:hover {
|
|
43
|
+
background-color: transparent;
|
|
44
|
+
color: var(--ant-color-link);
|
|
45
|
+
}
|
|
46
|
+
`,
|
|
47
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Tag } from 'antd'
|
|
2
|
+
import { config } from '@/lib/config'
|
|
3
|
+
|
|
4
|
+
export function DevStatus() {
|
|
5
|
+
return config.inDev ? (
|
|
6
|
+
<Tag color="green" variant="solid">
|
|
7
|
+
开发模式
|
|
8
|
+
</Tag>
|
|
9
|
+
) : (
|
|
10
|
+
<Tag variant="solid" color="#999">
|
|
11
|
+
非开发模式
|
|
12
|
+
</Tag>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="shortcut icon" href="./favicon.png" />
|
|
7
|
+
<title>演示项目</title>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body></body>
|
|
11
|
+
|
|
12
|
+
<script type="module" src="./index.tsx"></script>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
|
|
2
|
+
import { useParams } from 'react-router'
|
|
3
|
+
import { Breadcrumb } from '@/components/Breadcrumb'
|
|
4
|
+
|
|
5
|
+
export function SchoolDetail() {
|
|
6
|
+
usePageTitle('学校详情')
|
|
7
|
+
const params = useParams<{ id: string }>()
|
|
8
|
+
|
|
9
|
+
return <>
|
|
10
|
+
<Breadcrumb items={[{ path: '/schools', title: '学校列表' }, '学校详情']} />
|
|
11
|
+
<div className="page">学校详情 - {params.id}</div>
|
|
12
|
+
</>
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { usePageTitle } from '@stdex/utils-frontend/react/hooks.js'
|
|
2
|
+
import { useNavigate } from 'react-router'
|
|
3
|
+
|
|
4
|
+
const schools = [
|
|
5
|
+
{ id: 1, name: '学校-1' },
|
|
6
|
+
{ id: 2, name: '学校-2' },
|
|
7
|
+
{ id: 3, name: '学校-3' },
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
export function Schools() {
|
|
11
|
+
usePageTitle('学校列表')
|
|
12
|
+
const navigate = useNavigate()
|
|
13
|
+
|
|
14
|
+
function goDetail(id: number) {
|
|
15
|
+
void navigate('/schools/' + id.toString())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <div className="page">
|
|
19
|
+
<div css={s.title}>学校列表</div>
|
|
20
|
+
<div css={s.list}>
|
|
21
|
+
{schools.map(school => <div key={school.id} className="item">
|
|
22
|
+
<div className="name">{school.name}</div>
|
|
23
|
+
<a onClick={() => goDetail(school.id)}>详情</a>
|
|
24
|
+
</div>)}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const s = {
|
|
30
|
+
title: css`
|
|
31
|
+
margin-bottom: 20px;
|
|
32
|
+
`,
|
|
33
|
+
|
|
34
|
+
list: css`
|
|
35
|
+
width: 12em;
|
|
36
|
+
.item {
|
|
37
|
+
display: flex;
|
|
38
|
+
.name {
|
|
39
|
+
flex: 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`,
|
|
43
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import react from '@vitejs/plugin-react-swc'
|
|
4
|
+
import { defineConfig } from 'vite'
|
|
5
|
+
import svgr from 'vite-plugin-svgr'
|
|
6
|
+
|
|
7
|
+
const base = path.dirname(fileURLToPath(new URL(import.meta.url)))
|
|
8
|
+
const src = path.join(base, 'src')
|
|
9
|
+
|
|
10
|
+
export default defineConfig(({ mode }) => {
|
|
11
|
+
const inDev = mode === 'development'
|
|
12
|
+
const appConfig = {
|
|
13
|
+
inDev,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
mode,
|
|
18
|
+
root: src,
|
|
19
|
+
base: '',
|
|
20
|
+
publicDir: path.join(base, 'public'),
|
|
21
|
+
|
|
22
|
+
define: {
|
|
23
|
+
'window.APP_CONFIG': JSON.stringify(appConfig),
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
resolve: {
|
|
27
|
+
alias: {
|
|
28
|
+
'@': src,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// 让 vite 对 node_modules 里的 jsx 文件也进行依赖解析
|
|
33
|
+
// 不然 @stdex/utils 里的 jsx 文件会无法正常引用类库
|
|
34
|
+
optimizeDeps: {
|
|
35
|
+
extensions: ['.jsx'],
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
plugins: [
|
|
39
|
+
react({
|
|
40
|
+
jsxImportSource: '@emotion/react',
|
|
41
|
+
plugins: [['@swc/plugin-emotion', {}]],
|
|
42
|
+
}),
|
|
43
|
+
svgr(),
|
|
44
|
+
],
|
|
45
|
+
|
|
46
|
+
server: {
|
|
47
|
+
host: true,
|
|
48
|
+
port: 3000,
|
|
49
|
+
strictPort: true,
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
build: {
|
|
53
|
+
outDir: path.join(base, 'dist'),
|
|
54
|
+
emptyOutDir: true,
|
|
55
|
+
rollupOptions: {
|
|
56
|
+
input: path.join(src, 'index.html'),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
})
|