create-modsemi 0.1.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.
Files changed (53) hide show
  1. package/README.md +99 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +155 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +38 -0
  7. package/template/.browserslistrc +4 -0
  8. package/template/.env.example +9 -0
  9. package/template/.github/workflows/ci.yml +36 -0
  10. package/template/.nvmrc +2 -0
  11. package/template/README.md +199 -0
  12. package/template/_gitignore +33 -0
  13. package/template/_package.json +36 -0
  14. package/template/biome.json +37 -0
  15. package/template/modern.config.ts +38 -0
  16. package/template/orval.config.ts +98 -0
  17. package/template/src/api/instance.ts +87 -0
  18. package/template/src/components/Access/index.tsx +32 -0
  19. package/template/src/components/AppBreadcrumb/index.tsx +34 -0
  20. package/template/src/components/UserAvatar/index.less +66 -0
  21. package/template/src/components/UserAvatar/index.tsx +96 -0
  22. package/template/src/config/global.tsx +59 -0
  23. package/template/src/config/navigation.tsx +91 -0
  24. package/template/src/hooks/useAccess.ts +53 -0
  25. package/template/src/hooks/useMenuData.ts +171 -0
  26. package/template/src/hooks/usePageTitle.ts +26 -0
  27. package/template/src/layouts/ProLayout/DoubleLayout.tsx +157 -0
  28. package/template/src/layouts/ProLayout/LayoutBreadcrumb.tsx +32 -0
  29. package/template/src/layouts/ProLayout/MixLayout.tsx +134 -0
  30. package/template/src/layouts/ProLayout/SideLayout.tsx +108 -0
  31. package/template/src/layouts/ProLayout/TopLayout.tsx +98 -0
  32. package/template/src/layouts/ProLayout/index.tsx +75 -0
  33. package/template/src/layouts/SettingDrawer/index.tsx +390 -0
  34. package/template/src/modern-app-env.d.ts +1 -0
  35. package/template/src/modern.runtime.ts +3 -0
  36. package/template/src/pages/Dashboard/Workplace/index.tsx +7 -0
  37. package/template/src/pages/Error/NotFound/index.less +211 -0
  38. package/template/src/pages/Error/NotFound/index.tsx +64 -0
  39. package/template/src/pages/Login/index.less +491 -0
  40. package/template/src/pages/Login/index.tsx +204 -0
  41. package/template/src/pages/Welcome/index.less +351 -0
  42. package/template/src/pages/Welcome/index.tsx +164 -0
  43. package/template/src/routes/$.tsx +14 -0
  44. package/template/src/routes/dashboard/workplace/page.tsx +3 -0
  45. package/template/src/routes/layout.tsx +53 -0
  46. package/template/src/routes/login/page.tsx +3 -0
  47. package/template/src/routes/page.tsx +3 -0
  48. package/template/src/store/authStore.ts +61 -0
  49. package/template/src/store/layoutStore.ts +82 -0
  50. package/template/src/store/pageTitleStore.ts +12 -0
  51. package/template/src/styles/global.less +80 -0
  52. package/template/swagger/sample.json +263 -0
  53. package/template/tsconfig.json +16 -0
@@ -0,0 +1,82 @@
1
+ import { create } from 'zustand';
2
+ import { persist } from 'zustand/middleware';
3
+
4
+ export type LayoutMode = 'side' | 'top' | 'mix' | 'double';
5
+ export type ThemeMode = 'light' | 'dark';
6
+ export type ColorTheme = 'blue' | 'violet' | 'teal' | 'rose';
7
+
8
+ export interface LayoutState {
9
+ /** 佈局模式 */
10
+ layoutMode: LayoutMode;
11
+ /** 主題模式(亮色 / 暗色) */
12
+ themeMode: ThemeMode;
13
+ /** 色系主題 */
14
+ colorTheme: ColorTheme;
15
+ /** 是否固定 Header */
16
+ fixedHeader: boolean;
17
+ /** 是否顯示麵包屑 */
18
+ showBreadcrumb: boolean;
19
+ /** 設定面板是否開啟 */
20
+ settingDrawerOpen: boolean;
21
+ /** double 模式下第一欄選中的 itemKey */
22
+ doubleFirstKey: string;
23
+ }
24
+
25
+ export interface LayoutActions {
26
+ setLayoutMode: (mode: LayoutMode) => void;
27
+ setThemeMode: (theme: ThemeMode) => void;
28
+ toggleTheme: () => void;
29
+ setColorTheme: (color: ColorTheme) => void;
30
+ setFixedHeader: (fixed: boolean) => void;
31
+ setShowBreadcrumb: (show: boolean) => void;
32
+ setSettingDrawerOpen: (open: boolean) => void;
33
+ setDoubleFirstKey: (key: string) => void;
34
+ }
35
+
36
+ const initialState: LayoutState = {
37
+ layoutMode: 'double',
38
+ themeMode: 'light',
39
+ colorTheme: 'blue',
40
+ fixedHeader: true,
41
+ showBreadcrumb: true,
42
+ settingDrawerOpen: false,
43
+ doubleFirstKey: '/dashboard',
44
+ };
45
+
46
+ export const useLayoutStore = create<LayoutState & LayoutActions>()(
47
+ persist(
48
+ set => ({
49
+ ...initialState,
50
+
51
+ setLayoutMode: mode => set({ layoutMode: mode }),
52
+
53
+ setThemeMode: theme => set({ themeMode: theme }),
54
+
55
+ toggleTheme: () =>
56
+ set(state => ({
57
+ themeMode: state.themeMode === 'light' ? 'dark' : 'light',
58
+ })),
59
+
60
+ setColorTheme: color => set({ colorTheme: color }),
61
+
62
+ setFixedHeader: fixed => set({ fixedHeader: fixed }),
63
+
64
+ setShowBreadcrumb: show => set({ showBreadcrumb: show }),
65
+
66
+ setSettingDrawerOpen: open => set({ settingDrawerOpen: open }),
67
+
68
+ setDoubleFirstKey: key => set({ doubleFirstKey: key }),
69
+ }),
70
+ {
71
+ name: 'modsemi-layout',
72
+ partialize: (state: LayoutState & LayoutActions) => ({
73
+ layoutMode: state.layoutMode,
74
+ themeMode: state.themeMode,
75
+ colorTheme: state.colorTheme,
76
+ fixedHeader: state.fixedHeader,
77
+ showBreadcrumb: state.showBreadcrumb,
78
+ doubleFirstKey: state.doubleFirstKey,
79
+ }),
80
+ },
81
+ ),
82
+ );
@@ -0,0 +1,12 @@
1
+ import { create } from 'zustand';
2
+
3
+ interface PageTitleState {
4
+ /** 頁面元件主動設定的自訂標題,null 代表使用 breadcrumb 自動推導 */
5
+ customTitle: string | null;
6
+ setCustomTitle: (title: string | null) => void;
7
+ }
8
+
9
+ export const usePageTitleStore = create<PageTitleState>(set => ({
10
+ customTitle: null,
11
+ setCustomTitle: title => set({ customTitle: title }),
12
+ }));
@@ -0,0 +1,80 @@
1
+ // ============================================================
2
+ // ModSemi Global Styles
3
+ // 所有全域樣式在此定義;使用 Semi Design CSS 變數確保主題一致性
4
+ // ============================================================
5
+
6
+ // Reset
7
+ *,
8
+ *::before,
9
+ *::after {
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ html,
14
+ body,
15
+ #root {
16
+ margin: 0;
17
+ padding: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ min-height: 100vh;
21
+ }
22
+
23
+ body {
24
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
25
+ 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
26
+ font-size: 14px;
27
+ line-height: 1.5715;
28
+ color: var(--semi-color-text-0);
29
+ background-color: var(--semi-color-bg-0);
30
+ -webkit-font-smoothing: antialiased;
31
+ -moz-osx-font-smoothing: grayscale;
32
+ }
33
+
34
+ // 捲軸樣式
35
+ ::-webkit-scrollbar {
36
+ width: 6px;
37
+ height: 6px;
38
+ }
39
+
40
+ ::-webkit-scrollbar-track {
41
+ background: transparent;
42
+ }
43
+
44
+ ::-webkit-scrollbar-thumb {
45
+ border-radius: 3px;
46
+ background-color: var(--semi-color-fill-2);
47
+
48
+ &:hover {
49
+ background-color: var(--semi-color-fill-3);
50
+ }
51
+ }
52
+
53
+ // 通用工具類別
54
+ .text-ellipsis {
55
+ overflow: hidden;
56
+ white-space: nowrap;
57
+ text-overflow: ellipsis;
58
+ }
59
+
60
+ .flex-center {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ }
65
+
66
+ .full-height {
67
+ height: 100%;
68
+ }
69
+
70
+ // 頁面內容過渡動畫
71
+ .page-enter {
72
+ opacity: 0;
73
+ transform: translateY(8px);
74
+ }
75
+
76
+ .page-enter-active {
77
+ opacity: 1;
78
+ transform: translateY(0);
79
+ transition: opacity 0.25s ease, transform 0.25s ease;
80
+ }
@@ -0,0 +1,263 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "ModSemi Sample API",
5
+ "description": "示範用 Swagger / OpenAPI 規格,用於測試 orval 程式碼生成。",
6
+ "version": "1.0.0"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "/api",
11
+ "description": "本地開發伺服器"
12
+ }
13
+ ],
14
+ "tags": [
15
+ { "name": "User", "description": "使用者管理" },
16
+ { "name": "Article", "description": "文章管理" }
17
+ ],
18
+ "paths": {
19
+ "/users": {
20
+ "get": {
21
+ "tags": ["User"],
22
+ "summary": "取得使用者列表",
23
+ "operationId": "getUsers",
24
+ "parameters": [
25
+ {
26
+ "name": "page",
27
+ "in": "query",
28
+ "schema": { "type": "integer", "default": 1 }
29
+ },
30
+ {
31
+ "name": "pageSize",
32
+ "in": "query",
33
+ "schema": { "type": "integer", "default": 20 }
34
+ },
35
+ {
36
+ "name": "keyword",
37
+ "in": "query",
38
+ "schema": { "type": "string" }
39
+ }
40
+ ],
41
+ "responses": {
42
+ "200": {
43
+ "description": "成功",
44
+ "content": {
45
+ "application/json": {
46
+ "schema": { "$ref": "#/components/schemas/UserListResponse" }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ },
52
+ "post": {
53
+ "tags": ["User"],
54
+ "summary": "建立使用者",
55
+ "operationId": "createUser",
56
+ "requestBody": {
57
+ "required": true,
58
+ "content": {
59
+ "application/json": {
60
+ "schema": { "$ref": "#/components/schemas/CreateUserRequest" }
61
+ }
62
+ }
63
+ },
64
+ "responses": {
65
+ "201": {
66
+ "description": "建立成功",
67
+ "content": {
68
+ "application/json": {
69
+ "schema": { "$ref": "#/components/schemas/User" }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ },
76
+ "/users/{id}": {
77
+ "get": {
78
+ "tags": ["User"],
79
+ "summary": "取得單一使用者",
80
+ "operationId": "getUserById",
81
+ "parameters": [
82
+ {
83
+ "name": "id",
84
+ "in": "path",
85
+ "required": true,
86
+ "schema": { "type": "integer" }
87
+ }
88
+ ],
89
+ "responses": {
90
+ "200": {
91
+ "description": "成功",
92
+ "content": {
93
+ "application/json": {
94
+ "schema": { "$ref": "#/components/schemas/User" }
95
+ }
96
+ }
97
+ },
98
+ "404": {
99
+ "description": "使用者不存在"
100
+ }
101
+ }
102
+ },
103
+ "put": {
104
+ "tags": ["User"],
105
+ "summary": "更新使用者",
106
+ "operationId": "updateUser",
107
+ "parameters": [
108
+ {
109
+ "name": "id",
110
+ "in": "path",
111
+ "required": true,
112
+ "schema": { "type": "integer" }
113
+ }
114
+ ],
115
+ "requestBody": {
116
+ "required": true,
117
+ "content": {
118
+ "application/json": {
119
+ "schema": { "$ref": "#/components/schemas/UpdateUserRequest" }
120
+ }
121
+ }
122
+ },
123
+ "responses": {
124
+ "200": {
125
+ "description": "更新成功",
126
+ "content": {
127
+ "application/json": {
128
+ "schema": { "$ref": "#/components/schemas/User" }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ },
134
+ "delete": {
135
+ "tags": ["User"],
136
+ "summary": "刪除使用者",
137
+ "operationId": "deleteUser",
138
+ "parameters": [
139
+ {
140
+ "name": "id",
141
+ "in": "path",
142
+ "required": true,
143
+ "schema": { "type": "integer" }
144
+ }
145
+ ],
146
+ "responses": {
147
+ "204": { "description": "刪除成功" }
148
+ }
149
+ }
150
+ },
151
+ "/articles": {
152
+ "get": {
153
+ "tags": ["Article"],
154
+ "summary": "取得文章列表",
155
+ "operationId": "getArticles",
156
+ "parameters": [
157
+ {
158
+ "name": "page",
159
+ "in": "query",
160
+ "schema": { "type": "integer", "default": 1 }
161
+ },
162
+ {
163
+ "name": "status",
164
+ "in": "query",
165
+ "schema": {
166
+ "type": "string",
167
+ "enum": ["draft", "published", "archived"]
168
+ }
169
+ }
170
+ ],
171
+ "responses": {
172
+ "200": {
173
+ "description": "成功",
174
+ "content": {
175
+ "application/json": {
176
+ "schema": { "$ref": "#/components/schemas/ArticleListResponse" }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ },
184
+ "components": {
185
+ "schemas": {
186
+ "User": {
187
+ "type": "object",
188
+ "properties": {
189
+ "id": { "type": "integer", "example": 1 },
190
+ "name": { "type": "string", "example": "Admin" },
191
+ "email": { "type": "string", "format": "email", "example": "admin@modsemi.dev" },
192
+ "roles": {
193
+ "type": "array",
194
+ "items": { "type": "string" },
195
+ "example": ["admin"]
196
+ },
197
+ "createdAt": { "type": "string", "format": "date-time" }
198
+ }
199
+ },
200
+ "CreateUserRequest": {
201
+ "type": "object",
202
+ "required": ["name", "email"],
203
+ "properties": {
204
+ "name": { "type": "string" },
205
+ "email": { "type": "string", "format": "email" },
206
+ "roles": {
207
+ "type": "array",
208
+ "items": { "type": "string" }
209
+ }
210
+ }
211
+ },
212
+ "UpdateUserRequest": {
213
+ "type": "object",
214
+ "properties": {
215
+ "name": { "type": "string" },
216
+ "email": { "type": "string", "format": "email" },
217
+ "roles": {
218
+ "type": "array",
219
+ "items": { "type": "string" }
220
+ }
221
+ }
222
+ },
223
+ "UserListResponse": {
224
+ "type": "object",
225
+ "properties": {
226
+ "data": {
227
+ "type": "array",
228
+ "items": { "$ref": "#/components/schemas/User" }
229
+ },
230
+ "total": { "type": "integer" },
231
+ "page": { "type": "integer" },
232
+ "pageSize": { "type": "integer" }
233
+ }
234
+ },
235
+ "Article": {
236
+ "type": "object",
237
+ "properties": {
238
+ "id": { "type": "integer" },
239
+ "title": { "type": "string" },
240
+ "content": { "type": "string" },
241
+ "status": {
242
+ "type": "string",
243
+ "enum": ["draft", "published", "archived"]
244
+ },
245
+ "author": { "$ref": "#/components/schemas/User" },
246
+ "createdAt": { "type": "string", "format": "date-time" }
247
+ }
248
+ },
249
+ "ArticleListResponse": {
250
+ "type": "object",
251
+ "properties": {
252
+ "data": {
253
+ "type": "array",
254
+ "items": { "$ref": "#/components/schemas/Article" }
255
+ },
256
+ "total": { "type": "integer" },
257
+ "page": { "type": "integer" },
258
+ "pageSize": { "type": "integer" }
259
+ }
260
+ }
261
+ }
262
+ }
263
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "@modern-js/tsconfig/base",
3
+ "compilerOptions": {
4
+ "moduleResolution": "bundler",
5
+ "ignoreDeprecations": "6.0",
6
+ "declaration": false,
7
+ "jsx": "preserve",
8
+ "baseUrl": "./",
9
+ "paths": {
10
+ "@/*": ["./src/*"],
11
+ "@shared/*": ["./shared/*"]
12
+ }
13
+ },
14
+ "include": ["src", "shared", "config", "modern.config.ts"],
15
+ "exclude": ["**/node_modules"]
16
+ }