@coffic/cosy-ui 0.8.10 → 0.8.12

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 (157) hide show
  1. package/dist/app.css +1 -1
  2. package/dist/src-astro/alert/index.ts +7 -15
  3. package/dist/src-astro/assets/iconData.ts +100 -0
  4. package/dist/src-astro/button/index_astro.ts +2 -56
  5. package/dist/src-astro/card/CardCourse.astro +31 -11
  6. package/dist/src-astro/card/index.ts +15 -25
  7. package/dist/src-astro/code-container/CodeContainer.astro +205 -5
  8. package/dist/src-astro/code-container/index.ts +9 -13
  9. package/dist/src-astro/contact/index.ts +16 -20
  10. package/dist/src-astro/errors/index.ts +0 -62
  11. package/dist/src-astro/footer/index.ts +1 -30
  12. package/dist/src-astro/header/Header.astro +1 -2
  13. package/dist/src-astro/header/index.ts +1 -40
  14. package/dist/src-astro/hero/index.ts +1 -39
  15. package/dist/src-astro/icons/ChartIcon.astro +28 -0
  16. package/dist/src-astro/icons/DashboardIcon.astro +28 -0
  17. package/dist/src-astro/icons/DeleteIcon.astro +28 -0
  18. package/dist/src-astro/icons/DocumentIcon.astro +28 -0
  19. package/dist/src-astro/icons/DownloadIcon.astro +28 -0
  20. package/dist/src-astro/icons/EditIcon.astro +28 -0
  21. package/dist/src-astro/icons/FolderIcon.astro +28 -0
  22. package/dist/src-astro/icons/HeartIcon.astro +28 -0
  23. package/dist/src-astro/icons/HomeIcon.astro +28 -0
  24. package/dist/src-astro/icons/MailIcon.astro +28 -0
  25. package/dist/src-astro/icons/MessageIcon.astro +28 -0
  26. package/dist/src-astro/icons/NotificationIcon.astro +28 -0
  27. package/dist/src-astro/icons/ReportIcon.astro +28 -0
  28. package/dist/src-astro/icons/SaveIcon.astro +28 -0
  29. package/dist/src-astro/icons/SecurityIcon.astro +28 -0
  30. package/dist/src-astro/icons/StarIcon.astro +28 -0
  31. package/dist/src-astro/icons/ToolsIcon.astro +28 -0
  32. package/dist/src-astro/icons/UploadIcon.astro +28 -0
  33. package/dist/src-astro/icons/UsersIcon.astro +28 -0
  34. package/dist/src-astro/icons/WalletIcon.astro +28 -0
  35. package/dist/src-astro/icons/index.ts +20 -0
  36. package/dist/src-astro/layout-app/index.ts +5 -9
  37. package/dist/src-astro/layout-dashboard/DashboardLayout.astro +159 -555
  38. package/dist/src-astro/layout-dashboard/DashboardSidebar.astro +219 -0
  39. package/dist/src-astro/layout-dashboard/DashboardTopNavbar.astro +164 -0
  40. package/dist/src-astro/layout-dashboard/index.ts +14 -8
  41. package/dist/src-astro/layout-dashboard/tools.ts +0 -0
  42. package/dist/src-astro/layout-dashboard/types.ts +319 -0
  43. package/package.json +1 -1
  44. package/dist/src-astro/alert/EAlertBasic.astro +0 -11
  45. package/dist/src-astro/alert/EAlertBasicContainer.astro +0 -11
  46. package/dist/src-astro/alert/EAlertCustomStyle.astro +0 -16
  47. package/dist/src-astro/alert/EAlertCustomStyleContainer.astro +0 -11
  48. package/dist/src-astro/alert/EAlertError.astro +0 -6
  49. package/dist/src-astro/alert/EAlertInfo.astro +0 -6
  50. package/dist/src-astro/alert/EAlertSuccess.astro +0 -6
  51. package/dist/src-astro/alert/EAlertTypesContainer.astro +0 -43
  52. package/dist/src-astro/alert/EAlertWarning.astro +0 -6
  53. package/dist/src-astro/alert/EAlertWithTitle.astro +0 -12
  54. package/dist/src-astro/alert/EAlertWithTitleContainer.astro +0 -11
  55. package/dist/src-astro/button/ButtonBasic.astro +0 -18
  56. package/dist/src-astro/button/ButtonBasicContainer.astro +0 -11
  57. package/dist/src-astro/button/ButtonBlock.astro +0 -5
  58. package/dist/src-astro/button/ButtonError.astro +0 -5
  59. package/dist/src-astro/button/ButtonGhost.astro +0 -5
  60. package/dist/src-astro/button/ButtonIconBoth.astro +0 -9
  61. package/dist/src-astro/button/ButtonIconLeft.astro +0 -8
  62. package/dist/src-astro/button/ButtonIconRight.astro +0 -8
  63. package/dist/src-astro/button/ButtonInfo.astro +0 -5
  64. package/dist/src-astro/button/ButtonLinkContainer.astro +0 -19
  65. package/dist/src-astro/button/ButtonNeutral.astro +0 -5
  66. package/dist/src-astro/button/ButtonOutline.astro +0 -5
  67. package/dist/src-astro/button/ButtonShapeCircle.astro +0 -5
  68. package/dist/src-astro/button/ButtonShapeDefault.astro +0 -5
  69. package/dist/src-astro/button/ButtonShapeSquare.astro +0 -5
  70. package/dist/src-astro/button/ButtonShapes.astro +0 -23
  71. package/dist/src-astro/button/ButtonShapesContainer.astro +0 -29
  72. package/dist/src-astro/button/ButtonSizes.astro +0 -15
  73. package/dist/src-astro/button/ButtonSizesContainer.astro +0 -11
  74. package/dist/src-astro/button/ButtonStates.astro +0 -12
  75. package/dist/src-astro/button/ButtonStatesContainer.astro +0 -11
  76. package/dist/src-astro/button/ButtonSuccess.astro +0 -5
  77. package/dist/src-astro/button/ButtonVariantsContainer.astro +0 -73
  78. package/dist/src-astro/button/ButtonWarning.astro +0 -5
  79. package/dist/src-astro/button/ButtonWide.astro +0 -5
  80. package/dist/src-astro/button/ButtonWidth.astro +0 -8
  81. package/dist/src-astro/button/ButtonWidthContainer.astro +0 -19
  82. package/dist/src-astro/button/ButtonWithIcons.astro +0 -25
  83. package/dist/src-astro/button/ButtonWithIconsContainer.astro +0 -29
  84. package/dist/src-astro/card/CardBasic.astro +0 -5
  85. package/dist/src-astro/card/ECardBasic.astro +0 -13
  86. package/dist/src-astro/card/ECardBasicContainer.astro +0 -11
  87. package/dist/src-astro/card/ECardClickable.astro +0 -16
  88. package/dist/src-astro/card/ECardClickableContainer.astro +0 -11
  89. package/dist/src-astro/card/ECardCompact.astro +0 -13
  90. package/dist/src-astro/card/ECardCompactContainer.astro +0 -11
  91. package/dist/src-astro/card/ECardCustomStyle.astro +0 -17
  92. package/dist/src-astro/card/ECardCustomStyleContainer.astro +0 -11
  93. package/dist/src-astro/card/ECardWithImage.astro +0 -16
  94. package/dist/src-astro/card/ECardWithImageContainer.astro +0 -11
  95. package/dist/src-astro/card/ECardWithSubtitle.astro +0 -13
  96. package/dist/src-astro/card/ECardWithSubtitleContainer.astro +0 -11
  97. package/dist/src-astro/code-container/ECodeContainerBasic.astro +0 -19
  98. package/dist/src-astro/code-container/ECodeContainerMultiple.astro +0 -49
  99. package/dist/src-astro/contact/EContactBasic.astro +0 -15
  100. package/dist/src-astro/contact/EContactBasicContainer.astro +0 -11
  101. package/dist/src-astro/contact/EContactCompact.astro +0 -11
  102. package/dist/src-astro/contact/EContactCompactContainer.astro +0 -11
  103. package/dist/src-astro/contact/EContactCustomStyle.astro +0 -21
  104. package/dist/src-astro/contact/EContactCustomStyleContainer.astro +0 -11
  105. package/dist/src-astro/contact/EContactSocial.astro +0 -20
  106. package/dist/src-astro/contact/EContactSocialContainer.astro +0 -11
  107. package/dist/src-astro/contact/EContactWithTitle.astro +0 -17
  108. package/dist/src-astro/contact/EContactWithTitleContainer.astro +0 -11
  109. package/dist/src-astro/errors/E403Basic.astro +0 -11
  110. package/dist/src-astro/errors/E403BasicContainer.astro +0 -20
  111. package/dist/src-astro/errors/E404Basic.astro +0 -11
  112. package/dist/src-astro/errors/E404BasicContainer.astro +0 -20
  113. package/dist/src-astro/errors/E404WithDebug.astro +0 -18
  114. package/dist/src-astro/errors/E500Basic.astro +0 -11
  115. package/dist/src-astro/errors/E500BasicContainer.astro +0 -20
  116. package/dist/src-astro/errors/E503Maintenance.astro +0 -20
  117. package/dist/src-astro/errors/E503MaintenanceContainer.astro +0 -21
  118. package/dist/src-astro/errors/EErrorPageCustomStyle.astro +0 -21
  119. package/dist/src-astro/errors/EErrorPageCustomStyleContainer.astro +0 -21
  120. package/dist/src-astro/errors/EErrorPageShowcase.astro +0 -68
  121. package/dist/src-astro/errors/EErrorPageShowcaseContainer.astro +0 -21
  122. package/dist/src-astro/footer/EFooterBasic.astro +0 -18
  123. package/dist/src-astro/footer/EFooterBasicContainer.astro +0 -11
  124. package/dist/src-astro/footer/EFooterComplete.astro +0 -50
  125. package/dist/src-astro/footer/EFooterCompleteContainer.astro +0 -11
  126. package/dist/src-astro/footer/EFooterFeaturesContainer.astro +0 -40
  127. package/dist/src-astro/footer/EFooterWithLogo.astro +0 -28
  128. package/dist/src-astro/footer/EFooterWithLogoContainer.astro +0 -11
  129. package/dist/src-astro/footer/EFooterWithNavigation.astro +0 -24
  130. package/dist/src-astro/footer/EFooterWithNavigationContainer.astro +0 -11
  131. package/dist/src-astro/footer/EFooterWithProducts.astro +0 -24
  132. package/dist/src-astro/footer/EFooterWithProductsContainer.astro +0 -11
  133. package/dist/src-astro/footer/EFooterWithSocial.astro +0 -24
  134. package/dist/src-astro/footer/EFooterWithSocialContainer.astro +0 -11
  135. package/dist/src-astro/header/HeaderBasic.astro +0 -19
  136. package/dist/src-astro/header/HeaderCustomNavbarCenter.astro +0 -28
  137. package/dist/src-astro/header/HeaderCustomNavbarEnd.astro +0 -25
  138. package/dist/src-astro/header/HeaderCustomNavbarStart.astro +0 -27
  139. package/dist/src-astro/header/HeaderCustomPosition.astro +0 -34
  140. package/dist/src-astro/header/HeaderWithNavigation.astro +0 -27
  141. package/dist/src-astro/hero/EHeroAlignCenter.astro +0 -13
  142. package/dist/src-astro/hero/EHeroAlignContainer.astro +0 -27
  143. package/dist/src-astro/hero/EHeroAlignLeft.astro +0 -13
  144. package/dist/src-astro/hero/EHeroAlignRight.astro +0 -13
  145. package/dist/src-astro/hero/EHeroBackgroundContainer.astro +0 -18
  146. package/dist/src-astro/hero/EHeroBackgroundImage.astro +0 -16
  147. package/dist/src-astro/hero/EHeroBackgroundImageContainer.astro +0 -11
  148. package/dist/src-astro/hero/EHeroBasic.astro +0 -12
  149. package/dist/src-astro/hero/EHeroBasicContainer.astro +0 -11
  150. package/dist/src-astro/hero/EHeroGradientBackground.astro +0 -10
  151. package/dist/src-astro/hero/EHeroPlainBackground.astro +0 -10
  152. package/dist/src-astro/hero/EHeroWithButton.astro +0 -14
  153. package/dist/src-astro/hero/EHeroWithButtonContainer.astro +0 -11
  154. package/dist/src-astro/hero/EHeroWithImage.astro +0 -16
  155. package/dist/src-astro/hero/EHeroWithImageContainer.astro +0 -11
  156. package/dist/src-astro/layout-app/AppLayoutBasic.astro +0 -54
  157. package/dist/src-astro/layout-dashboard/DashboardLayoutBasic.astro +0 -51
@@ -13,15 +13,23 @@
13
13
  * 3. 可定制性 - 支持自定义导航项、系统名称和用户信息
14
14
  * 4. 状态保持 - 记住用户的侧边栏折叠状态
15
15
  *
16
- * @example
16
+ * @usage
17
+ * 基本用法:
17
18
  * ```astro
18
19
  * ---
19
- * import DashboardLayout from '../layouts/DashboardLayout.astro';
20
+ * import { DashboardLayout } from '@coffic/cosy-ui';
20
21
  *
21
22
  * const navItems = [
22
- * { href: "/dashboard", icon: "home", text: "仪表盘" },
23
- * { href: "/dashboard/users", icon: "user", text: "用户管理" },
24
- * { href: "/dashboard/settings", icon: "settings", text: "系统设置" }
23
+ * { href: "/dashboard", text: "仪表盘" }, // 自动匹配 dashboard 图标
24
+ * { href: "/dashboard/users", text: "用户管理" }, // 自动匹配 users 图标
25
+ * { href: "/dashboard/settings", text: "系统设置" }, // 自动匹配 settings 图标
26
+ * { href: "/dashboard/reports", icon: "chart", text: "报表" } // 手动指定图标
27
+ * ];
28
+ *
29
+ * const userMenuItems = [
30
+ * { href: "/profile", text: "个人资料" }, // 自动匹配 user 图标
31
+ * { href: "/settings", text: "设置" }, // 自动匹配 settings 图标
32
+ * { href: "/logout", text: "退出登录" } // 自动匹配 logout 图标
25
33
  * ];
26
34
  * ---
27
35
  *
@@ -29,23 +37,87 @@
29
37
  * title="管理后台"
30
38
  * navItems={navItems}
31
39
  * userName="管理员"
40
+ * userMenuItems={userMenuItems}
41
+ * sidebarSize="lg"
42
+ * >
43
+ * <h1>仪表盘内容</h1>
44
+ * <p>这是仪表盘的主要内容</p>
45
+ * </DashboardLayout>
46
+ * ```
47
+ *
48
+ * 带有底部自定义内容的用法:
49
+ * ```astro
50
+ * <DashboardLayout
51
+ * title="管理后台"
52
+ * navItems={navItems}
53
+ * userName="管理员"
32
54
  * >
33
55
  * <h1>仪表盘内容</h1>
34
56
  * <p>这是仪表盘的主要内容</p>
57
+ *
58
+ * <div slot="sidebar-footer" class="p-4">
59
+ * <div class="flex items-center gap-3">
60
+ * <div class="avatar">
61
+ * <div class="w-10 rounded-full">
62
+ * <img src="/avatar.jpg" alt="用户头像" />
63
+ * </div>
64
+ * </div>
65
+ * <div>
66
+ * <div class="font-semibold">张三</div>
67
+ * <div class="text-sm opacity-70">产品经理</div>
68
+ * </div>
69
+ * </div>
70
+ * </div>
71
+ * </DashboardLayout>
72
+ * ```
73
+ *
74
+ * 带有主内容区域背景的用法:
75
+ * ```astro
76
+ * <DashboardLayout
77
+ * title="管理后台"
78
+ * navItems={navItems}
79
+ * userName="管理员"
80
+ * mainBackgroundTheme="gradient-cool"
81
+ * >
82
+ * <h1>仪表盘内容</h1>
83
+ * <p>这是带有背景的主要内容</p>
35
84
  * </DashboardLayout>
36
85
  * ```
86
+ *
87
+ * @props
88
+ * - title: string - 页面标题
89
+ * - description?: string - 页面描述
90
+ * - systemName?: string - 系统名称,默认为"管理系统"
91
+ * - navItems: NavItem[] - 导航项目
92
+ * - userName?: string - 用户名
93
+ * - userAvatar?: string - 用户头像
94
+ * - userMenuItems?: UserMenuItem[] - 用户菜单项,默认包含个人资料、设置、退出登录,图标会自动匹配
95
+ * - sidebarCollapsed?: boolean - 是否折叠侧边栏,默认为false
96
+ * - sidebarSize?: 'sm' | 'md' | 'lg' | 'xl' - 侧边栏尺寸,默认为"md"
97
+ * - sidebarTheme?: SidebarTheme - 侧边栏主题,默认为"default"
98
+ * - mainBackgroundTheme?: MainBackgroundTheme - 主内容区域背景主题,默认为"transparent"
99
+ * - head?: astroHTML.JSX.Element - 自定义头部内容
100
+ * - class?: string - 页面类名
101
+ * - class:list?: any - 类名列表
102
+ * - debug?: boolean - 调试模式,默认为false
103
+ *
104
+ * @slots
105
+ * - default - 主要内容区域
106
+ * - sidebar-footer - 侧边栏底部自定义内容,可用于展示用户信息、版权信息等
37
107
  */
38
108
 
39
109
  import { BaseLayout } from '../../index-astro';
110
+ import DashboardSidebar from './DashboardSidebar.astro';
111
+ import DashboardTopNavbar from './DashboardTopNavbar.astro';
40
112
  import '../../style.ts';
41
-
42
- export interface NavItem {
43
- href: string;
44
- icon: string;
45
- text: string;
46
- badge?: string | number;
47
- items?: NavItem[];
48
- }
113
+ import {
114
+ type NavItem,
115
+ type SidebarSize,
116
+ type SidebarTheme,
117
+ type MainBackgroundTheme,
118
+ type UserMenuItem,
119
+ getMainBackgroundTheme,
120
+ } from './types';
49
121
 
50
122
  export interface Props {
51
123
  /**
@@ -79,12 +151,35 @@ export interface Props {
79
151
  */
80
152
  userAvatar?: string;
81
153
 
154
+ /**
155
+ * 用户菜单项
156
+ */
157
+ userMenuItems?: UserMenuItem[];
158
+
82
159
  /**
83
160
  * 是否折叠侧边栏
84
161
  * @default false
85
162
  */
86
163
  sidebarCollapsed?: boolean;
87
164
 
165
+ /**
166
+ * 侧边栏尺寸
167
+ * @default "md"
168
+ */
169
+ sidebarSize?: SidebarSize;
170
+
171
+ /**
172
+ * 侧边栏主题
173
+ * @default "default"
174
+ */
175
+ sidebarTheme?: SidebarTheme;
176
+
177
+ /**
178
+ * 主内容区域背景主题
179
+ * @default "transparent"
180
+ */
181
+ mainBackgroundTheme?: MainBackgroundTheme;
182
+
88
183
  /**
89
184
  * 自定义头部内容
90
185
  */
@@ -99,6 +194,12 @@ export interface Props {
99
194
  * 类名列表
100
195
  */
101
196
  'class:list'?: any;
197
+
198
+ /**
199
+ * 调试模式
200
+ * @default false
201
+ */
202
+ debug?: boolean;
102
203
  }
103
204
 
104
205
  const {
@@ -108,31 +209,20 @@ const {
108
209
  navItems,
109
210
  userName,
110
211
  userAvatar,
212
+ userMenuItems,
111
213
  sidebarCollapsed = false,
214
+ sidebarSize = 'md',
215
+ sidebarTheme = 'default',
216
+ mainBackgroundTheme = 'transparent',
112
217
  head,
113
218
  class: className,
114
219
  'class:list': classList,
220
+ debug = false,
115
221
  ...rest
116
222
  } = Astro.props;
117
223
 
118
224
  const currentPath = Astro.url.pathname;
119
-
120
- // 图标映射
121
- const iconMap: Record<string, string> = {
122
- home: '🏠',
123
- user: '👤',
124
- users: '👥',
125
- settings: '⚙️',
126
- chart: '📊',
127
- document: '📄',
128
- calendar: '📅',
129
- notification: '🔔',
130
- message: '💬',
131
- search: '🔍',
132
- star: '⭐',
133
- heart: '❤️',
134
- menu: '☰',
135
- };
225
+ const mainBackgroundStyles = getMainBackgroundTheme(mainBackgroundTheme);
136
226
  ---
137
227
 
138
228
  <BaseLayout
@@ -142,545 +232,59 @@ const iconMap: Record<string, string> = {
142
232
  keywords=""
143
233
  author=""
144
234
  robots=""
145
- class="dashboard-layout"
235
+ debug={debug}
236
+ class:list={['cosy:min-h-screen cosy:bg-base-200', className, classList]}
146
237
  {...rest}>
147
- <div class:list={['dashboard-container', { collapsed: sidebarCollapsed }]}>
148
- <!-- 侧边栏 -->
149
- <aside class="sidebar">
150
- <div class="sidebar-header">
151
- <a href="/dashboard" class="logo">
152
- <span class="logo-icon">⚡</span>
153
- <span class="logo-text">{systemName}</span>
154
- </a>
155
- <button class="collapse-button" id="collapse-sidebar">
156
- <span class="collapse-icon">{iconMap.menu}</span>
157
- </button>
158
- </div>
159
-
160
- <nav class="sidebar-nav">
161
- <ul class="nav-list">
162
- {
163
- navItems.map((item: NavItem) => {
164
- const isActive =
165
- currentPath === item.href ||
166
- (item.items &&
167
- item.items.some(
168
- (subitem: NavItem) => currentPath === subitem.href
169
- ));
170
-
171
- return (
172
- <li class:list={['nav-item', { active: isActive }]}>
173
- <a href={item.href} class="nav-link">
174
- <span class="nav-icon">{iconMap[item.icon] || '📁'}</span>
175
- <span class="nav-text">{item.text}</span>
176
- {item.badge && <span class="nav-badge">{item.badge}</span>}
177
- </a>
238
+ <div class:list={['cosy:drawer', { 'cosy:drawer-open': !sidebarCollapsed }]}>
239
+ <input id="dashboard-drawer" type="checkbox" class="cosy:drawer-toggle" />
178
240
 
179
- {item.items && (
180
- <ul class="subnav-list">
181
- {item.items.map((subitem: NavItem) => {
182
- const isSubActive = currentPath === subitem.href;
183
- return (
184
- <li
185
- class:list={[
186
- 'subnav-item',
187
- { active: isSubActive },
188
- ]}>
189
- <a href={subitem.href} class="subnav-link">
190
- <span class="subnav-icon">
191
- {iconMap[subitem.icon] || '📄'}
192
- </span>
193
- <span class="subnav-text">{subitem.text}</span>
194
- {subitem.badge && (
195
- <span class="subnav-badge">
196
- {subitem.badge}
197
- </span>
198
- )}
199
- </a>
200
- </li>
201
- );
202
- })}
203
- </ul>
204
- )}
205
- </li>
206
- );
207
- })
208
- }
209
- </ul>
210
- </nav>
211
- </aside>
241
+ <!-- 侧边栏 -->
242
+ <div class="cosy:drawer-side">
243
+ <label
244
+ for="dashboard-drawer"
245
+ aria-label="close sidebar"
246
+ class="cosy:drawer-overlay"></label>
247
+ <DashboardSidebar
248
+ systemName={systemName}
249
+ navItems={navItems}
250
+ currentPath={currentPath}
251
+ size={sidebarSize}
252
+ theme={sidebarTheme}>
253
+ <slot name="sidebar-footer" slot="footer" />
254
+ </DashboardSidebar>
255
+ </div>
212
256
 
213
257
  <!-- 主内容区 -->
214
- <div class="main-content">
258
+ <div class="cosy:drawer-content cosy:flex cosy:flex-col">
215
259
  <!-- 顶部导航栏 -->
216
- <header class="top-navbar">
217
- <div class="navbar-left">
218
- <button class="menu-button" id="toggle-sidebar">
219
- <span class="menu-icon">{iconMap.menu}</span>
220
- </button>
221
-
222
- <div class="breadcrumb">
223
- <span class="breadcrumb-item">{title}</span>
224
- </div>
225
- </div>
226
-
227
- <div class="navbar-right">
228
- <div class="search-box">
229
- <input type="text" placeholder="搜索..." class="search-input" />
230
- <span class="search-icon">{iconMap.search}</span>
231
- </div>
232
-
233
- <button class="notification-button">
234
- <span class="notification-icon">{iconMap.notification}</span>
235
- <span class="notification-badge">3</span>
236
- </button>
237
-
238
- {
239
- userName && (
240
- <div class="user-dropdown">
241
- <button class="user-button">
242
- {userAvatar ? (
243
- <img src={userAvatar} alt={userName} class="user-avatar" />
244
- ) : (
245
- <span class="user-avatar-placeholder">
246
- {userName.charAt(0)}
247
- </span>
248
- )}
249
- <span class="user-name">{userName}</span>
250
- </button>
251
- </div>
252
- )
253
- }
254
- </div>
255
- </header>
260
+ <DashboardTopNavbar
261
+ title={title}
262
+ userName={userName}
263
+ userAvatar={userAvatar}
264
+ userMenuItems={userMenuItems}
265
+ />
256
266
 
257
267
  <!-- 页面内容 -->
258
- <main class="page-content">
259
- <div class="content-container">
260
- <slot />
261
- </div>
268
+ <main
269
+ class:list={['cosy:flex-1 cosy:p-4 cosy:lg:p-6', mainBackgroundStyles]}>
270
+ <slot />
262
271
  </main>
263
272
  </div>
264
273
  </div>
265
274
  </BaseLayout>
266
275
 
267
276
  <script>
268
- // 侧边栏折叠/展开功能
277
+ // 从本地存储恢复状态
269
278
  document.addEventListener('DOMContentLoaded', () => {
270
- const toggleSidebarBtn = document.getElementById('toggle-sidebar');
271
- const collapseSidebarBtn = document.getElementById('collapse-sidebar');
272
- const dashboardContainer = document.querySelector('.dashboard-container');
273
-
274
- if (toggleSidebarBtn && dashboardContainer) {
275
- toggleSidebarBtn.addEventListener('click', () => {
276
- dashboardContainer.classList.toggle('collapsed');
277
- // 保存状态到本地存储
278
- localStorage.setItem(
279
- 'sidebarCollapsed',
280
- dashboardContainer.classList.contains('collapsed') ? 'true' : 'false'
281
- );
282
- });
283
- }
284
-
285
- if (collapseSidebarBtn && dashboardContainer) {
286
- collapseSidebarBtn.addEventListener('click', () => {
287
- dashboardContainer.classList.toggle('collapsed');
288
- // 保存状态到本地存储
289
- localStorage.setItem(
290
- 'sidebarCollapsed',
291
- dashboardContainer.classList.contains('collapsed') ? 'true' : 'false'
292
- );
293
- });
294
- }
295
-
296
- // 从本地存储恢复状态
297
- const savedState = localStorage.getItem('sidebarCollapsed');
298
- if (savedState === 'true' && dashboardContainer) {
299
- dashboardContainer.classList.add('collapsed');
279
+ const drawerToggle = document.getElementById(
280
+ 'dashboard-drawer'
281
+ ) as HTMLInputElement;
282
+
283
+ if (drawerToggle) {
284
+ const savedState = localStorage.getItem('sidebarCollapsed');
285
+ if (savedState) {
286
+ drawerToggle.checked = savedState === 'true';
287
+ }
300
288
  }
301
289
  });
302
290
  </script>
303
-
304
- <style>
305
- /* 基础布局 */
306
- .dashboard-layout {
307
- min-height: 100vh;
308
- background-color: #f5f7fa;
309
- }
310
-
311
- .dashboard-container {
312
- display: flex;
313
- min-height: 100vh;
314
- }
315
-
316
- /* 侧边栏 */
317
- .sidebar {
318
- width: 260px;
319
- background-color: #1e293b;
320
- color: #e2e8f0;
321
- transition: width 0.3s ease;
322
- display: flex;
323
- flex-direction: column;
324
- position: fixed;
325
- top: 0;
326
- left: 0;
327
- bottom: 0;
328
- z-index: 50;
329
- overflow-y: auto;
330
- }
331
-
332
- .sidebar-header {
333
- display: flex;
334
- align-items: center;
335
- justify-content: space-between;
336
- padding: 1rem;
337
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
338
- }
339
-
340
- .logo {
341
- display: flex;
342
- align-items: center;
343
- gap: 0.75rem;
344
- color: #fff;
345
- text-decoration: none;
346
- }
347
-
348
- .logo-icon {
349
- font-size: 1.5rem;
350
- }
351
-
352
- .logo-text {
353
- font-size: 1.25rem;
354
- font-weight: 600;
355
- white-space: nowrap;
356
- }
357
-
358
- .collapse-button {
359
- background: none;
360
- border: none;
361
- color: #e2e8f0;
362
- cursor: pointer;
363
- padding: 0.5rem;
364
- border-radius: 0.375rem;
365
- }
366
-
367
- .collapse-button:hover {
368
- background-color: rgba(255, 255, 255, 0.1);
369
- }
370
-
371
- .sidebar-nav {
372
- flex: 1;
373
- padding: 1rem 0;
374
- }
375
-
376
- .nav-list {
377
- list-style: none;
378
- padding: 0;
379
- margin: 0;
380
- }
381
-
382
- .nav-item {
383
- margin-bottom: 0.25rem;
384
- }
385
-
386
- .nav-link {
387
- display: flex;
388
- align-items: center;
389
- padding: 0.75rem 1rem;
390
- color: #e2e8f0;
391
- text-decoration: none;
392
- border-radius: 0.375rem;
393
- margin: 0 0.5rem;
394
- gap: 0.75rem;
395
- }
396
-
397
- .nav-link:hover {
398
- background-color: rgba(255, 255, 255, 0.1);
399
- }
400
-
401
- .nav-item.active .nav-link {
402
- background-color: rgba(59, 130, 246, 0.5);
403
- color: #fff;
404
- }
405
-
406
- .nav-icon {
407
- font-size: 1.25rem;
408
- width: 1.5rem;
409
- text-align: center;
410
- }
411
-
412
- .nav-text {
413
- flex: 1;
414
- white-space: nowrap;
415
- }
416
-
417
- .nav-badge {
418
- background-color: #ef4444;
419
- color: #fff;
420
- font-size: 0.75rem;
421
- padding: 0.125rem 0.375rem;
422
- border-radius: 9999px;
423
- }
424
-
425
- .subnav-list {
426
- list-style: none;
427
- padding: 0;
428
- margin: 0.25rem 0 0.5rem 2.5rem;
429
- }
430
-
431
- .subnav-link {
432
- display: flex;
433
- align-items: center;
434
- padding: 0.5rem 1rem;
435
- color: #cbd5e1;
436
- text-decoration: none;
437
- border-radius: 0.375rem;
438
- gap: 0.5rem;
439
- font-size: 0.875rem;
440
- }
441
-
442
- .subnav-link:hover {
443
- background-color: rgba(255, 255, 255, 0.05);
444
- }
445
-
446
- .subnav-item.active .subnav-link {
447
- color: #3b82f6;
448
- font-weight: 500;
449
- }
450
-
451
- .subnav-icon {
452
- font-size: 1rem;
453
- width: 1.25rem;
454
- text-align: center;
455
- }
456
-
457
- /* 主内容区 */
458
- .main-content {
459
- flex: 1;
460
- margin-left: 260px;
461
- transition: margin-left 0.3s ease;
462
- display: flex;
463
- flex-direction: column;
464
- min-height: 100vh;
465
- }
466
-
467
- /* 顶部导航栏 */
468
- .top-navbar {
469
- display: flex;
470
- justify-content: space-between;
471
- align-items: center;
472
- padding: 0.75rem 1.5rem;
473
- background-color: #fff;
474
- border-bottom: 1px solid #e2e8f0;
475
- height: 64px;
476
- position: sticky;
477
- top: 0;
478
- z-index: 40;
479
- }
480
-
481
- .navbar-left {
482
- display: flex;
483
- align-items: center;
484
- gap: 1rem;
485
- }
486
-
487
- .menu-button {
488
- background: none;
489
- border: none;
490
- color: #64748b;
491
- cursor: pointer;
492
- padding: 0.5rem;
493
- border-radius: 0.375rem;
494
- display: flex;
495
- align-items: center;
496
- justify-content: center;
497
- }
498
-
499
- .menu-button:hover {
500
- background-color: #f1f5f9;
501
- }
502
-
503
- .breadcrumb {
504
- color: #64748b;
505
- font-size: 0.875rem;
506
- }
507
-
508
- .navbar-right {
509
- display: flex;
510
- align-items: center;
511
- gap: 1rem;
512
- }
513
-
514
- .search-box {
515
- position: relative;
516
- }
517
-
518
- .search-input {
519
- padding: 0.5rem 1rem 0.5rem 2.5rem;
520
- border: 1px solid #e2e8f0;
521
- border-radius: 0.375rem;
522
- background-color: #f8fafc;
523
- width: 240px;
524
- font-size: 0.875rem;
525
- }
526
-
527
- .search-icon {
528
- position: absolute;
529
- left: 0.75rem;
530
- top: 50%;
531
- transform: translateY(-50%);
532
- color: #94a3b8;
533
- }
534
-
535
- .notification-button {
536
- background: none;
537
- border: none;
538
- color: #64748b;
539
- cursor: pointer;
540
- padding: 0.5rem;
541
- border-radius: 0.375rem;
542
- position: relative;
543
- }
544
-
545
- .notification-button:hover {
546
- background-color: #f1f5f9;
547
- }
548
-
549
- .notification-badge {
550
- position: absolute;
551
- top: 0;
552
- right: 0;
553
- background-color: #ef4444;
554
- color: #fff;
555
- font-size: 0.75rem;
556
- width: 1rem;
557
- height: 1rem;
558
- border-radius: 9999px;
559
- display: flex;
560
- align-items: center;
561
- justify-content: center;
562
- }
563
-
564
- .user-dropdown {
565
- position: relative;
566
- }
567
-
568
- .user-button {
569
- display: flex;
570
- align-items: center;
571
- gap: 0.5rem;
572
- background: none;
573
- border: none;
574
- cursor: pointer;
575
- padding: 0.5rem;
576
- border-radius: 0.375rem;
577
- }
578
-
579
- .user-button:hover {
580
- background-color: #f1f5f9;
581
- }
582
-
583
- .user-avatar {
584
- width: 2rem;
585
- height: 2rem;
586
- border-radius: 9999px;
587
- object-fit: cover;
588
- }
589
-
590
- .user-avatar-placeholder {
591
- width: 2rem;
592
- height: 2rem;
593
- border-radius: 9999px;
594
- background-color: #3b82f6;
595
- color: #fff;
596
- display: flex;
597
- align-items: center;
598
- justify-content: center;
599
- font-weight: 600;
600
- }
601
-
602
- .user-name {
603
- color: #334155;
604
- font-weight: 500;
605
- }
606
-
607
- /* 页面内容 */
608
- .page-content {
609
- flex: 1;
610
- padding: 1.5rem;
611
- }
612
-
613
- .content-container {
614
- background-color: #fff;
615
- border-radius: 0.5rem;
616
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
617
- padding: 1.5rem;
618
- }
619
-
620
- /* 折叠状态 */
621
- .dashboard-container.collapsed .sidebar {
622
- width: 80px;
623
- }
624
-
625
- .dashboard-container.collapsed .logo-text,
626
- .dashboard-container.collapsed .nav-text,
627
- .dashboard-container.collapsed .nav-badge,
628
- .dashboard-container.collapsed .subnav-list {
629
- display: none;
630
- }
631
-
632
- .dashboard-container.collapsed .nav-link {
633
- justify-content: center;
634
- padding: 0.75rem;
635
- }
636
-
637
- .dashboard-container.collapsed .nav-icon {
638
- margin: 0;
639
- }
640
-
641
- .dashboard-container.collapsed .main-content {
642
- margin-left: 80px;
643
- }
644
-
645
- /* 响应式调整 */
646
- @media (max-width: 1024px) {
647
- .sidebar {
648
- transform: translateX(-100%);
649
- width: 260px;
650
- }
651
-
652
- .main-content {
653
- margin-left: 0;
654
- }
655
-
656
- .dashboard-container.collapsed .sidebar {
657
- transform: translateX(0);
658
- }
659
-
660
- .dashboard-container:not(.collapsed) .sidebar {
661
- transform: translateX(-100%);
662
- }
663
- }
664
-
665
- @media (max-width: 640px) {
666
- .search-box {
667
- display: none;
668
- }
669
-
670
- .user-name {
671
- display: none;
672
- }
673
-
674
- .top-navbar {
675
- padding: 0.75rem 1rem;
676
- }
677
-
678
- .page-content {
679
- padding: 1rem;
680
- }
681
-
682
- .content-container {
683
- padding: 1rem;
684
- }
685
- }
686
- </style>