@k3000/ce 0.0.1 → 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.
- package/README.md +125 -62
- package/app/comp/comp-test.mjs +24 -0
- package/{comp/new-form.mjs → app/comp/my-form.mjs} +2 -5
- package/app/comp/nav-bar.mjs +92 -0
- package/app/comp/page-container.mjs +55 -0
- package/app/comp/tab-bar.mjs +60 -0
- package/app/index.html +39 -0
- package/app/pages/not-found.mjs +7 -0
- package/app/pages/page-category.mjs +7 -0
- package/app/pages/page-home.mjs +14 -0
- package/app/pages/page-my.mjs +7 -0
- package/app/pages/page-test.mjs +22 -0
- package/comm/router-view.mjs +96 -0
- package/console/api/mock.mjs +211 -0
- package/console/components/common-action-button.mjs +63 -0
- package/console/components/common-header.mjs +26 -0
- package/console/components/common-input.mjs +44 -0
- package/console/components/common-modal.mjs +45 -0
- package/console/components/common-pagination.mjs +87 -0
- package/console/components/common-toolbar.mjs +39 -0
- package/console/components/console-header.mjs +26 -0
- package/console/components/layout-container.mjs +46 -0
- package/console/components/layout-header.mjs +179 -0
- package/console/components/layout-menu.mjs +221 -0
- package/console/index.html +50 -0
- package/console/pages/page-dashboard.mjs +15 -0
- package/console/pages/system-menu.mjs +179 -0
- package/console/pages/system-role.mjs +199 -0
- package/console/pages/system-user.mjs +199 -0
- package/index.mjs +854 -98
- package/package.json +1 -1
- package/comp/div-box.mjs +0 -20
- package/index.html +0 -17
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
|
|
2
|
+
export const innerHTML = `
|
|
3
|
+
<div class="h-20 w-full flex justify-between items-center relative z-30">
|
|
4
|
+
<!-- Floating Glass Container -->
|
|
5
|
+
<div class="absolute inset-x-2 top-2 bottom-2 rounded-2xl bg-white/60 dark:bg-gray-900/60 backdrop-blur-2xl border border-white/50 dark:border-white/10 shadow-[0_8px_32px_rgba(0,0,0,0.04)] ring-1 ring-black/5 flex justify-between items-center px-6 transition-all duration-300 hover:shadow-[0_12px_40px_rgba(0,0,0,0.06)] hover:bg-white/70 dark:hover:bg-gray-900/70">
|
|
6
|
+
|
|
7
|
+
<div class="flex items-center gap-6">
|
|
8
|
+
<!-- Logo -->
|
|
9
|
+
<div class="flex items-center gap-3 font-bold text-xl tracking-tight text-gray-900">
|
|
10
|
+
<div class="w-9 h-9 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-xl flex items-center justify-center shadow-lg shadow-blue-500/30 text-white">
|
|
11
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10l-2 1m0 0l-2-1m2 1v2.5M20 7l-2 1m2-1l-2-1m2 1v2.5M14 4l-2-1-2 1M4 7l2-1M4 7l2 1M4 7v2.5M12 21l-2-1m2 1l2-1m-2 1v-2.5M6 18l-2-1v-2.5M18 18l2-1v-2.5"></path></svg>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<span class="bg-clip-text text-transparent bg-gradient-to-r from-gray-900 to-gray-600 dark:from-white dark:to-gray-300">Console</span>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Separator -->
|
|
18
|
+
<div class="h-4 w-px bg-gray-300/50 rounded-full"></div>
|
|
19
|
+
|
|
20
|
+
<!-- Breadcrumbs -->
|
|
21
|
+
<div class="flex items-center text-sm font-medium">
|
|
22
|
+
<span class="text-gray-400 hover:text-blue-600 cursor-pointer transition-colors duration-200">Start</span>
|
|
23
|
+
<svg class="w-3 h-3 mx-2 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /></svg>
|
|
24
|
+
<span class="text-gray-800 dark:text-gray-100 bg-gray-100/50 dark:bg-gray-800/50 px-2 py-0.5 rounded-md">Dashboard</span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Right: User Actions -->
|
|
29
|
+
<div class="flex items-center gap-3">
|
|
30
|
+
<!-- Theme Dropdown -->
|
|
31
|
+
<div class="relative group" tabindex="0">
|
|
32
|
+
<button class="w-9 h-9 flex items-center justify-center text-gray-500 hover:text-purple-600 dark:text-gray-400 dark:hover:text-purple-400 transition-all duration-200 rounded-xl hover:bg-purple-50/50 dark:hover:bg-purple-500/10 active:scale-95 border border-transparent hover:border-purple-100 dark:hover:border-purple-500/20">
|
|
33
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"></path></svg>
|
|
34
|
+
</button>
|
|
35
|
+
|
|
36
|
+
<!-- Dropdown Menu -->
|
|
37
|
+
<div class="absolute right-0 top-full mt-2 w-56 bg-white/70 dark:bg-gray-800/80 backdrop-blur-3xl border border-white/50 dark:border-white/10 rounded-2xl shadow-[0_20px_50px_rgba(0,0,0,0.15)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 transform origin-top-right z-50 scale-95 group-hover:scale-100 translate-y-2 group-hover:translate-y-0 filter backdrop-brightness-110 dark:backdrop-brightness-90">
|
|
38
|
+
<div class="p-2 space-y-1">
|
|
39
|
+
<!-- Light Mode -->
|
|
40
|
+
<div onclick="{{setTheme}}" data-theme="light" class="flex items-center justify-between px-3 py-2.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 cursor-pointer group/item transition-colors theme-option">
|
|
41
|
+
<div class="flex items-center gap-3 text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
42
|
+
<div class="w-8 h-8 rounded-lg bg-orange-50 dark:bg-orange-500/10 text-orange-500 flex items-center justify-center">
|
|
43
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>
|
|
44
|
+
</div>
|
|
45
|
+
Day Mode
|
|
46
|
+
</div>
|
|
47
|
+
<!-- Checkmark for active state -->
|
|
48
|
+
<svg class="w-4 h-4 text-orange-500 opacity-0 group-[.active]:opacity-100 transition-opacity check-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Dark Mode -->
|
|
52
|
+
<div onclick="{{setTheme}}" data-theme="dark" class="flex items-center justify-between px-3 py-2.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 cursor-pointer group/item transition-colors theme-option">
|
|
53
|
+
<div class="flex items-center gap-3 text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
54
|
+
<div class="w-8 h-8 rounded-lg bg-indigo-50 dark:bg-indigo-500/10 text-indigo-500 dark:text-indigo-400 flex items-center justify-center">
|
|
55
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path></svg>
|
|
56
|
+
</div>
|
|
57
|
+
Night Mode
|
|
58
|
+
</div>
|
|
59
|
+
<svg class="w-4 h-4 text-indigo-500 dark:text-indigo-400 opacity-0 group-[.active]:opacity-100 transition-opacity check-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Eye Protection -->
|
|
63
|
+
<div onclick="{{setTheme}}" data-theme="eye-protection" class="flex items-center justify-between px-3 py-2.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 cursor-pointer group/item transition-colors theme-option">
|
|
64
|
+
<div class="flex items-center gap-3 text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
65
|
+
<div class="w-8 h-8 rounded-lg bg-green-50 dark:bg-green-500/10 text-green-600 dark:text-green-400 flex items-center justify-center">
|
|
66
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path></svg>
|
|
67
|
+
</div>
|
|
68
|
+
Eye Protect
|
|
69
|
+
</div>
|
|
70
|
+
<svg class="w-4 h-4 text-green-600 dark:text-green-400 opacity-0 group-[.active]:opacity-100 transition-opacity check-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<!-- Notifications -->
|
|
77
|
+
<button class="w-9 h-9 flex items-center justify-center text-gray-500 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-all duration-200 rounded-xl hover:bg-blue-50/50 dark:hover:bg-blue-500/10 active:scale-95">
|
|
78
|
+
<div class="relative">
|
|
79
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path></svg>
|
|
80
|
+
<span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full border border-white"></span>
|
|
81
|
+
</div>
|
|
82
|
+
</button>
|
|
83
|
+
|
|
84
|
+
<!-- User Profile -->
|
|
85
|
+
<div class="relative group" tabindex="0">
|
|
86
|
+
<div class="flex items-center gap-3 pl-2 pr-1 py-1 rounded-full hover:bg-gray-100/50 transition-all duration-200 cursor-pointer border border-transparent hover:border-white/50">
|
|
87
|
+
<div class="hidden md:block text-right">
|
|
88
|
+
<div class="text-[10px] uppercase tracking-wider text-gray-400 font-bold">Admin</div>
|
|
89
|
+
<div class="text-sm font-bold text-gray-800 dark:text-gray-200 leading-none">{{user.name}}</div>
|
|
90
|
+
</div>
|
|
91
|
+
<img src="{{user.avatar}}" class="w-9 h-9 rounded-full shadow-md ring-2 ring-white object-cover" />
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<!-- Glass Dropdown Menu -->
|
|
95
|
+
<div class="absolute right-0 top-full mt-2 w-64 bg-white/70 dark:bg-gray-800/80 backdrop-blur-3xl border border-white/50 dark:border-white/10 rounded-2xl shadow-[0_20px_50px_rgba(0,0,0,0.15)] opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 transform origin-top-right z-50 scale-95 group-hover:scale-100 translate-y-2 group-hover:translate-y-0 filter backdrop-brightness-110 dark:backdrop-brightness-90">
|
|
96
|
+
<div class="p-2">
|
|
97
|
+
<div class="p-3 bg-gradient-to-br from-gray-50 to-white dark:from-gray-700 dark:to-gray-800 rounded-xl border border-white/50 dark:border-white/10 mb-1">
|
|
98
|
+
<p class="text-sm font-bold text-gray-900 dark:text-white">{{user.name}}</p>
|
|
99
|
+
<p class="text-xs text-gray-500 truncate mt-0.5">admin@example.com</p>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div class="space-y-1">
|
|
103
|
+
<a href="#profile" class="flex items-center px-3 py-2.5 text-sm font-medium text-gray-600 dark:text-gray-300 rounded-xl hover:bg-blue-50/80 dark:hover:bg-blue-500/20 hover:text-blue-600 dark:hover:text-blue-400 transition-all duration-200 group/item">
|
|
104
|
+
<div class="w-8 h-8 rounded-lg bg-blue-100/50 text-blue-600 flex items-center justify-center mr-3 group-hover/item:bg-blue-500 group-hover/item:text-white transition-colors duration-200">
|
|
105
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
|
|
106
|
+
</div>
|
|
107
|
+
Profile
|
|
108
|
+
</a>
|
|
109
|
+
<a href="#settings" class="flex items-center px-3 py-2.5 text-sm font-medium text-gray-600 dark:text-gray-300 rounded-xl hover:bg-indigo-50/80 dark:hover:bg-indigo-500/20 hover:text-indigo-600 dark:hover:text-indigo-400 transition-all duration-200 group/item">
|
|
110
|
+
<div class="w-8 h-8 rounded-lg bg-indigo-100/50 text-indigo-600 flex items-center justify-center mr-3 group-hover/item:bg-indigo-500 group-hover/item:text-white transition-colors duration-200">
|
|
111
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
|
|
112
|
+
</div>
|
|
113
|
+
Settings
|
|
114
|
+
</a>
|
|
115
|
+
<div class="h-px bg-gray-100 dark:bg-gray-700 my-1"></div>
|
|
116
|
+
<div class="flex items-center px-3 py-2.5 text-sm font-medium text-red-600 rounded-xl hover:bg-red-50/80 transition-all duration-200 cursor-pointer btn-logout group/item">
|
|
117
|
+
<div class="w-8 h-8 rounded-lg bg-red-100/50 text-red-500 flex items-center justify-center mr-3 group-hover/item:bg-red-500 group-hover/item:text-white transition-colors duration-200">
|
|
118
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path></svg>
|
|
119
|
+
</div>
|
|
120
|
+
Logout
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
`
|
|
130
|
+
|
|
131
|
+
export default class extends HTMLElement {
|
|
132
|
+
|
|
133
|
+
user = {
|
|
134
|
+
name: 'Admin',
|
|
135
|
+
avatar: 'https://ui-avatars.com/api/?name=Admin&background=0D8ABC&color=fff'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
constructor() {
|
|
139
|
+
|
|
140
|
+
super()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
ready() {
|
|
144
|
+
this.updateThemeUI()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setTheme(e) {
|
|
148
|
+
const target = e.currentTarget
|
|
149
|
+
const theme = target.dataset.theme
|
|
150
|
+
|
|
151
|
+
localStorage.theme = theme
|
|
152
|
+
|
|
153
|
+
if (theme === 'dark') {
|
|
154
|
+
document.documentElement.classList.add('dark')
|
|
155
|
+
document.documentElement.classList.remove('eye-protection')
|
|
156
|
+
} else if (theme === 'eye-protection') {
|
|
157
|
+
document.documentElement.classList.add('eye-protection')
|
|
158
|
+
document.documentElement.classList.remove('dark')
|
|
159
|
+
} else {
|
|
160
|
+
document.documentElement.classList.remove('dark')
|
|
161
|
+
document.documentElement.classList.remove('eye-protection')
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this.updateThemeUI()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
updateThemeUI() {
|
|
168
|
+
const currentTheme = localStorage.theme || 'light'
|
|
169
|
+
const options = this.querySelectorAll('.theme-option')
|
|
170
|
+
|
|
171
|
+
options.forEach(option => {
|
|
172
|
+
if (option.dataset.theme === currentTheme) {
|
|
173
|
+
option.classList.add('active', 'bg-gray-100', 'dark:bg-white/10')
|
|
174
|
+
} else {
|
|
175
|
+
option.classList.remove('active', 'bg-gray-100', 'dark:bg-white/10')
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { useEvent, bind } from "../../index.mjs";
|
|
2
|
+
|
|
3
|
+
export default class extends HTMLElement {
|
|
4
|
+
|
|
5
|
+
template = null
|
|
6
|
+
|
|
7
|
+
menu = [
|
|
8
|
+
{
|
|
9
|
+
title: '系统管理',
|
|
10
|
+
icon: 'system',
|
|
11
|
+
path: '/system',
|
|
12
|
+
children: [
|
|
13
|
+
{
|
|
14
|
+
title: '用户管理',
|
|
15
|
+
icon: 'user',
|
|
16
|
+
path: '/user',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: '角色管理',
|
|
20
|
+
icon: 'role',
|
|
21
|
+
path: '/role',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
title: '菜单管理',
|
|
25
|
+
icon: 'menu',
|
|
26
|
+
path: '/menu',
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: '系统工具',
|
|
32
|
+
icon: 'tools',
|
|
33
|
+
path: '/tools',
|
|
34
|
+
children: [
|
|
35
|
+
{
|
|
36
|
+
title: '表单生成',
|
|
37
|
+
icon: 'form',
|
|
38
|
+
path: '/form',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: '代码生成',
|
|
42
|
+
icon: 'code',
|
|
43
|
+
path: '/code',
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
constructor() {
|
|
50
|
+
super()
|
|
51
|
+
this.template = bind(`
|
|
52
|
+
<div class="group h-full flex flex-col p-4 bg-white/30 dark:bg-gray-900/40 backdrop-blur-3xl border-r border-white/20 dark:border-white/5 transition-all duration-300 group-[.collapsed]:w-20 shadow-[20px_0_40px_rgba(0,0,0,0.02)]">
|
|
53
|
+
<!-- Menu Control Bar -->
|
|
54
|
+
<div class="mb-4 flex items-center gap-2 group-[.collapsed]:justify-center group-[.collapsed]:px-0">
|
|
55
|
+
<!-- Toggle Button -->
|
|
56
|
+
<div onclick="{{toggleMenu}}" class="cursor-pointer text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white transition-colors">
|
|
57
|
+
<div class="w-9 h-9 rounded-xl bg-gray-100 dark:bg-gray-800/50 flex items-center justify-center hover:bg-white dark:hover:bg-white/10 hover:shadow-sm transition-all duration-300">
|
|
58
|
+
<!-- Expanded Icon (Collapse) -->
|
|
59
|
+
<svg class="w-5 h-5 group-[.collapsed]:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7"></path></svg>
|
|
60
|
+
<!-- Collapsed Icon (Expand) -->
|
|
61
|
+
<svg class="w-5 h-5 hidden group-[.collapsed]:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7"></path></svg>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Search Input -->
|
|
66
|
+
<div class="flex-1 relative group-[.collapsed]:hidden">
|
|
67
|
+
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
|
|
68
|
+
<input type="text" oninput="{{onInput}}" placeholder="搜索" class="w-full h-9 pl-9 pr-3 text-sm bg-gray-100 dark:bg-gray-800/50 border-none rounded-xl focus:ring-2 focus:ring-blue-500/20 text-gray-700 dark:text-gray-200 placeholder-gray-400 dark:placeholder-gray-500 transition-all outline-none" />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<ul class="space-y-2 flex-1">
|
|
73
|
+
${this.menu.map(item => `
|
|
74
|
+
<li>
|
|
75
|
+
${item?.children ? `
|
|
76
|
+
<details open class="group/menu" ontoggle="{{onToggle}}">
|
|
77
|
+
<summary class="menu-header flex items-center justify-between px-4 py-3 rounded-2xl hover:bg-white/40 dark:hover:bg-white/10 hover:backdrop-blur-md hover:shadow-sm cursor-pointer select-none transition-all duration-300 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 mb-1 group-open/menu:text-blue-600 dark:group-open/menu:text-blue-400 group-open/menu:font-semibold group-[.collapsed]:justify-center group-[.collapsed]:px-0 group-[.collapsed]:gap-0 border border-transparent hover:border-white/20">
|
|
78
|
+
<div class="flex items-center gap-3">
|
|
79
|
+
<div class="menu-icon w-8 h-8 rounded-xl bg-gray-100 dark:bg-gray-800/50 flex items-center justify-center group-hover/menu:bg-white dark:group-hover/menu:bg-white/10 group-hover/menu:shadow-sm group-open/menu:bg-blue-100 dark:group-open/menu:bg-blue-500/20 group-open/menu:text-blue-600 dark:group-open/menu:text-blue-400 transition-all duration-300 group-[.collapsed]:mr-0 group-[.collapsed]:bg-transparent group-open/menu:group-[.collapsed]:bg-blue-500/10 group-open/menu:group-[.collapsed]:text-blue-600">
|
|
80
|
+
<!-- You can switch icons based on item.icon -->
|
|
81
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
|
|
82
|
+
</div>
|
|
83
|
+
<span class="menu-text group-[.collapsed]:hidden">${item.title}</span>
|
|
84
|
+
</div>
|
|
85
|
+
<svg class="menu-arrow w-4 h-4 text-gray-400 dark:text-gray-600 transition-transform duration-300 group-open/menu:rotate-180 group-[.collapsed]:hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
86
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|
87
|
+
</svg>
|
|
88
|
+
</summary>
|
|
89
|
+
<div class="overflow-hidden transition-all duration-300 group-[.collapsed]:hidden">
|
|
90
|
+
<ul class="pl-4 mt-1 space-y-1 border-l-2 border-dashed border-gray-200 dark:border-gray-700 ml-6 pb-2">
|
|
91
|
+
${item.children.map(child => `
|
|
92
|
+
<li>
|
|
93
|
+
<span onclick="{{navigate}}" data-path="${item.path}${child.path}" data-key="${child.title}" class="flex items-center gap-3 px-4 py-2.5 text-sm rounded-xl hover:bg-blue-50/80 dark:hover:bg-blue-500/10 hover:text-blue-600 dark:hover:text-blue-400 cursor-pointer text-gray-500 dark:text-gray-500 transition-all duration-200 group/item hover:translate-x-1">
|
|
94
|
+
<div class="w-1.5 h-1.5 rounded-full bg-gray-300 dark:bg-gray-600 group-hover/item:bg-blue-500 dark:group-hover/item:bg-blue-400 transition-colors"></div>
|
|
95
|
+
${child.title}
|
|
96
|
+
</span>
|
|
97
|
+
</li>
|
|
98
|
+
`).join('')}
|
|
99
|
+
</ul>
|
|
100
|
+
</div>
|
|
101
|
+
</details>
|
|
102
|
+
` : `
|
|
103
|
+
<span onclick="{{navigate}}" data-path="${item.path}" class="menu-header flex items-center gap-3 px-4 py-3 rounded-2xl hover:bg-white/40 dark:hover:bg-white/10 hover:backdrop-blur-md hover:shadow-sm cursor-pointer text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-all duration-300 group/item group-[.collapsed]:justify-center group-[.collapsed]:px-0 border border-transparent hover:border-white/20">
|
|
104
|
+
<div class="menu-icon w-8 h-8 rounded-xl bg-gray-100 dark:bg-gray-800/50 flex items-center justify-center group-hover/item:bg-white dark:group-hover/item:bg-white/10 group-hover/item:shadow-sm group-hover/item:text-blue-600 dark:group-hover/item:text-blue-400 transition-all duration-300 group-[.collapsed]:mr-0">
|
|
105
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
|
|
106
|
+
</div>
|
|
107
|
+
<span class="menu-text font-medium group-[.collapsed]:hidden">${item.title}</span>
|
|
108
|
+
</span>
|
|
109
|
+
`}
|
|
110
|
+
</li>
|
|
111
|
+
`).join('')}
|
|
112
|
+
</ul>
|
|
113
|
+
</div>`)
|
|
114
|
+
this.render()
|
|
115
|
+
const event = useEvent()
|
|
116
|
+
event.dispatch('init routers', [
|
|
117
|
+
{
|
|
118
|
+
path: '/',
|
|
119
|
+
dir: './console/pages',
|
|
120
|
+
component: 'page-dashboard',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
path: '/system/user',
|
|
124
|
+
dir: './console/pages',
|
|
125
|
+
component: 'system-user'
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
path: '/system/role',
|
|
129
|
+
dir: './console/pages',
|
|
130
|
+
component: 'system-role'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
path: '/system/menu',
|
|
134
|
+
dir: './console/pages',
|
|
135
|
+
component: 'system-menu'
|
|
136
|
+
},
|
|
137
|
+
])
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
toggleMenu() {
|
|
141
|
+
const container = this.firstElementChild
|
|
142
|
+
container.classList.toggle('collapsed')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
navigate(e) {
|
|
146
|
+
location.hash = '#' + e.target.dataset.path
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
onToggle(e) {
|
|
150
|
+
if (e.target.open) {
|
|
151
|
+
this.querySelectorAll('details').forEach(details => {
|
|
152
|
+
if (details !== e.target && details.open) {
|
|
153
|
+
details.open = false
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
onInput(event) {
|
|
160
|
+
const keyword = event.target.value.toLowerCase().trim()
|
|
161
|
+
|
|
162
|
+
// Helper to handle visibility
|
|
163
|
+
const setVisible = (el, visible) => {
|
|
164
|
+
if (visible) el.style.removeProperty('display')
|
|
165
|
+
else el.style.display = 'none'
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Loop through all top-level list items
|
|
169
|
+
const topItems = this.querySelectorAll('ul.space-y-2 > li')
|
|
170
|
+
|
|
171
|
+
topItems.forEach(item => {
|
|
172
|
+
const details = item.querySelector('details')
|
|
173
|
+
|
|
174
|
+
if (details) {
|
|
175
|
+
// It is a group
|
|
176
|
+
const groupTitle = details.querySelector('.menu-text')?.textContent?.toLowerCase().trim() || ''
|
|
177
|
+
const childItems = details.querySelectorAll('ul > li')
|
|
178
|
+
let hasVisibleChild = false
|
|
179
|
+
|
|
180
|
+
// Check if group title matches
|
|
181
|
+
const groupMatches = groupTitle.includes(keyword)
|
|
182
|
+
|
|
183
|
+
childItems.forEach(child => {
|
|
184
|
+
const span = child.querySelector('span')
|
|
185
|
+
const childTitle = span?.dataset.key?.toLowerCase() || span?.textContent?.toLowerCase().trim()
|
|
186
|
+
const isMatch = childTitle?.includes(keyword)
|
|
187
|
+
|
|
188
|
+
if (groupMatches || isMatch || !keyword) {
|
|
189
|
+
setVisible(child, true)
|
|
190
|
+
hasVisibleChild = true
|
|
191
|
+
} else {
|
|
192
|
+
setVisible(child, false)
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
if (hasVisibleChild || groupMatches) {
|
|
197
|
+
setVisible(item, true)
|
|
198
|
+
// Expand if searching and matches found
|
|
199
|
+
if (keyword) details.open = true
|
|
200
|
+
} else {
|
|
201
|
+
setVisible(item, false)
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
// It is a single leaf item
|
|
205
|
+
const span = item.querySelector('span[data-path]')
|
|
206
|
+
const title = span?.querySelector('.menu-text')?.textContent?.toLowerCase().trim() || ''
|
|
207
|
+
|
|
208
|
+
if (title.includes(keyword) || !keyword) {
|
|
209
|
+
setVisible(item, true)
|
|
210
|
+
} else {
|
|
211
|
+
setVisible(item, false)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
render() {
|
|
218
|
+
this.innerHTML = ''
|
|
219
|
+
this.appendChild(this.template.create(this))
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport"
|
|
6
|
+
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
|
7
|
+
<title>Console Management</title>
|
|
8
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
+
<style>
|
|
10
|
+
.eye-protection {
|
|
11
|
+
filter: sepia(0.3) contrast(0.9) brightness(0.95);
|
|
12
|
+
-webkit-filter: sepia(0.3) contrast(0.9) brightness(0.95);
|
|
13
|
+
}
|
|
14
|
+
</style>
|
|
15
|
+
<script>
|
|
16
|
+
tailwind.config = {
|
|
17
|
+
darkMode: 'class',
|
|
18
|
+
}
|
|
19
|
+
// Initialize Theme
|
|
20
|
+
const theme = localStorage.theme || 'light'
|
|
21
|
+
if (theme === 'dark') {
|
|
22
|
+
document.documentElement.classList.add('dark')
|
|
23
|
+
document.documentElement.classList.remove('eye-protection')
|
|
24
|
+
} else if (theme === 'eye-protection') {
|
|
25
|
+
document.documentElement.classList.add('eye-protection')
|
|
26
|
+
document.documentElement.classList.remove('dark')
|
|
27
|
+
} else {
|
|
28
|
+
document.documentElement.classList.remove('dark')
|
|
29
|
+
document.documentElement.classList.remove('eye-protection')
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
<script type="module">
|
|
33
|
+
import { config } from '../index.mjs'
|
|
34
|
+
|
|
35
|
+
config({
|
|
36
|
+
dir: './console/components',
|
|
37
|
+
ext: 'mjs'
|
|
38
|
+
})
|
|
39
|
+
</script>
|
|
40
|
+
</head>
|
|
41
|
+
|
|
42
|
+
<body class="bg-gradient-to-br from-indigo-50 via-purple-50 to-pink-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 min-h-screen text-gray-800 dark:text-gray-100 transition-colors duration-300">
|
|
43
|
+
<layout-container>
|
|
44
|
+
<layout-header slot="header"></layout-header>
|
|
45
|
+
<layout-menu slot="menu"></layout-menu>
|
|
46
|
+
<router-view dir="../../comm" slot="main"></router-view>
|
|
47
|
+
</layout-container>
|
|
48
|
+
</body>
|
|
49
|
+
|
|
50
|
+
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
export const shadowRoot = `
|
|
3
|
+
<div class="p-6">
|
|
4
|
+
<div class="h-64 rounded-3xl bg-white/60 dark:bg-gray-800/60 backdrop-blur-3xl border border-white/40 dark:border-white/10 shadow-[0_20px_50px_rgba(0,0,0,0.1)] flex items-center justify-center">
|
|
5
|
+
<div class="text-center">
|
|
6
|
+
<h1 class="text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600 dark:from-blue-400 dark:to-indigo-400 mb-4">Liquid Glass UI</h1>
|
|
7
|
+
<p class="text-gray-600 dark:text-gray-300">Welcome to the new console experience.</p>
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
`
|
|
12
|
+
|
|
13
|
+
export default class extends HTMLElement {
|
|
14
|
+
|
|
15
|
+
}
|