xto-fronted 0.1.0 → 0.1.1

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 (54) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +4 -0
  3. package/index.html +13 -0
  4. package/package.json +18 -10
  5. package/public/vite.svg +10 -0
  6. package/src/App.vue +20 -0
  7. package/src/api/auth.ts +26 -0
  8. package/src/api/system.ts +65 -0
  9. package/src/api/user.ts +46 -0
  10. package/src/assets/styles/_dark.scss +407 -0
  11. package/src/assets/styles/_reset.scss +126 -0
  12. package/src/assets/styles/_root.scss +140 -0
  13. package/src/assets/styles/_transition.scss +119 -0
  14. package/src/assets/styles/_variables.scss +45 -0
  15. package/src/assets/styles/index.scss +187 -0
  16. package/src/components/Layout/Footer.vue +17 -0
  17. package/src/components/Layout/Header.vue +390 -0
  18. package/src/components/Layout/Sidebar.vue +297 -0
  19. package/src/components/Layout/Tabs.vue +134 -0
  20. package/src/components/Layout/index.vue +62 -0
  21. package/src/composables/useAuth.ts +45 -0
  22. package/src/composables/useForm.ts +79 -0
  23. package/src/composables/useTable.ts +97 -0
  24. package/src/directives/permission.ts +38 -0
  25. package/src/enums/index.ts +63 -0
  26. package/src/env.d.ts +17 -0
  27. package/src/index.ts +39 -0
  28. package/src/main.ts +34 -0
  29. package/src/router/dynamicRoutes.ts +163 -0
  30. package/src/router/index.ts +81 -0
  31. package/src/router/staticRoutes.ts +43 -0
  32. package/src/stores/app.ts +145 -0
  33. package/src/stores/auth.ts +32 -0
  34. package/src/stores/index.ts +15 -0
  35. package/src/stores/menu.ts +80 -0
  36. package/src/stores/user.ts +73 -0
  37. package/src/types/api.d.ts +84 -0
  38. package/src/types/global.d.ts +45 -0
  39. package/src/types/router.d.ts +48 -0
  40. package/src/types/xto.d.ts +149 -0
  41. package/src/utils/auth.ts +62 -0
  42. package/src/utils/permission.ts +42 -0
  43. package/src/utils/request.ts +126 -0
  44. package/src/utils/storage.ts +63 -0
  45. package/src/views/dashboard/index.vue +284 -0
  46. package/src/views/error/403.vue +57 -0
  47. package/src/views/error/404.vue +57 -0
  48. package/src/views/login/index.vue +248 -0
  49. package/src/views/system/menu/index.vue +381 -0
  50. package/src/views/system/role/index.vue +304 -0
  51. package/src/views/system/user/index.vue +327 -0
  52. package/tsconfig.json +26 -0
  53. package/tsconfig.node.json +11 -0
  54. package/vite.config.ts +139 -0
@@ -0,0 +1,284 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { Card, Tag, Progress } from '@xto/data'
4
+
5
+ const stats = ref([
6
+ { title: '用户总数', value: 1234, icon: '👥', color: '#409eff' },
7
+ { title: '今日访问', value: 567, icon: '👀', color: '#67c23a' },
8
+ { title: '订单数量', value: 890, icon: '📦', color: '#e6a23c' },
9
+ { title: '销售金额', value: 123456, icon: '💰', color: '#f56c6c' }
10
+ ])
11
+
12
+ const activities = ref([
13
+ { user: '张三', action: '登录系统', time: '2分钟前', type: 'success' },
14
+ { user: '李四', action: '修改了用户信息', time: '5分钟前', type: 'warning' },
15
+ { user: '王五', action: '创建了新订单', time: '10分钟前', type: 'info' },
16
+ { user: '赵六', action: '删除了测试数据', time: '30分钟前', type: 'danger' },
17
+ { user: '钱七', action: '更新了系统配置', time: '1小时前', type: 'primary' }
18
+ ])
19
+
20
+ const quickLinks = ref([
21
+ { title: '用户管理', path: '/system/user', icon: '👤' },
22
+ { title: '角色管理', path: '/system/role', icon: '👥' },
23
+ { title: '菜单管理', path: '/system/menu', icon: '📋' },
24
+ { title: '系统设置', path: '/system', icon: '⚙️' }
25
+ ])
26
+ </script>
27
+
28
+ <template>
29
+ <div class="dashboard">
30
+ <!-- 统计卡片 -->
31
+ <div class="dashboard__stats">
32
+ <Card v-for="stat in stats" :key="stat.title" class="stat-card">
33
+ <div class="stat-card__content">
34
+ <div class="stat-card__icon" :style="{ backgroundColor: stat.color + '20' }">
35
+ {{ stat.icon }}
36
+ </div>
37
+ <div class="stat-card__info">
38
+ <div class="stat-card__title">{{ stat.title }}</div>
39
+ <div class="stat-card__value" :style="{ color: stat.color }">
40
+ {{ stat.value.toLocaleString() }}
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </Card>
45
+ </div>
46
+
47
+ <!-- 主体内容 -->
48
+ <div class="dashboard__main">
49
+ <!-- 快捷入口 -->
50
+ <Card class="dashboard__quick">
51
+ <template #header>
52
+ <span class="card-title">快捷入口</span>
53
+ </template>
54
+ <div class="quick-links">
55
+ <router-link
56
+ v-for="link in quickLinks"
57
+ :key="link.path"
58
+ :to="link.path"
59
+ class="quick-link"
60
+ >
61
+ <span class="quick-link__icon">{{ link.icon }}</span>
62
+ <span class="quick-link__title">{{ link.title }}</span>
63
+ </router-link>
64
+ </div>
65
+ </Card>
66
+
67
+ <!-- 最近活动 -->
68
+ <Card class="dashboard__activity">
69
+ <template #header>
70
+ <span class="card-title">最近活动</span>
71
+ </template>
72
+ <div class="activity-list">
73
+ <div v-for="(activity, index) in activities" :key="index" class="activity-item">
74
+ <Tag :type="activity.type as any" size="small">{{ activity.user }}</Tag>
75
+ <span class="activity-item__action">{{ activity.action }}</span>
76
+ <span class="activity-item__time">{{ activity.time }}</span>
77
+ </div>
78
+ </div>
79
+ </Card>
80
+ </div>
81
+
82
+ <!-- 系统信息 -->
83
+ <Card class="dashboard__system">
84
+ <template #header>
85
+ <span class="card-title">系统信息</span>
86
+ </template>
87
+ <div class="system-info">
88
+ <div class="system-info__item">
89
+ <span class="system-info__label">系统版本</span>
90
+ <span class="system-info__value">v1.0.0</span>
91
+ </div>
92
+ <div class="system-info__item">
93
+ <span class="system-info__label">Vue 版本</span>
94
+ <span class="system-info__value">3.4.21</span>
95
+ </div>
96
+ <div class="system-info__item">
97
+ <span class="system-info__label">构建工具</span>
98
+ <span class="system-info__value">Vite 5</span>
99
+ </div>
100
+ <div class="system-info__item">
101
+ <span class="system-info__label">UI 组件库</span>
102
+ <span class="system-info__value">xto</span>
103
+ </div>
104
+ <div class="system-info__item">
105
+ <span class="system-info__label">服务器状态</span>
106
+ <Progress :percentage="75" status="success" />
107
+ </div>
108
+ <div class="system-info__item">
109
+ <span class="system-info__label">内存使用</span>
110
+ <Progress :percentage="45" />
111
+ </div>
112
+ </div>
113
+ </Card>
114
+ </div>
115
+ </template>
116
+
117
+ <style lang="scss" scoped>
118
+ .dashboard {
119
+ padding: 20px;
120
+
121
+ &__stats {
122
+ display: grid;
123
+ grid-template-columns: repeat(4, 1fr);
124
+ gap: 20px;
125
+ margin-bottom: 20px;
126
+
127
+ @media (max-width: 1200px) {
128
+ grid-template-columns: repeat(2, 1fr);
129
+ }
130
+
131
+ @media (max-width: 768px) {
132
+ grid-template-columns: 1fr;
133
+ }
134
+ }
135
+
136
+ &__main {
137
+ display: grid;
138
+ grid-template-columns: repeat(2, 1fr);
139
+ gap: 20px;
140
+ margin-bottom: 20px;
141
+
142
+ @media (max-width: 992px) {
143
+ grid-template-columns: 1fr;
144
+ }
145
+ }
146
+
147
+ &__quick,
148
+ &__activity {
149
+ min-height: 300px;
150
+ }
151
+ }
152
+
153
+ .card-title {
154
+ font-size: 16px;
155
+ font-weight: 500;
156
+ }
157
+
158
+ .stat-card {
159
+ &__content {
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 15px;
163
+ }
164
+
165
+ &__icon {
166
+ width: 50px;
167
+ height: 50px;
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: center;
171
+ font-size: 24px;
172
+ border-radius: var(--border-radius-base);
173
+ }
174
+
175
+ &__info {
176
+ flex: 1;
177
+ }
178
+
179
+ &__title {
180
+ font-size: 14px;
181
+ color: var(--color-text-secondary);
182
+ margin-bottom: 5px;
183
+ }
184
+
185
+ &__value {
186
+ font-size: 24px;
187
+ font-weight: 600;
188
+ }
189
+ }
190
+
191
+ .quick-links {
192
+ display: grid;
193
+ grid-template-columns: repeat(4, 1fr);
194
+ gap: 15px;
195
+
196
+ @media (max-width: 768px) {
197
+ grid-template-columns: repeat(2, 1fr);
198
+ }
199
+ }
200
+
201
+ .quick-link {
202
+ display: flex;
203
+ flex-direction: column;
204
+ align-items: center;
205
+ justify-content: center;
206
+ padding: 20px;
207
+ border-radius: var(--border-radius-base);
208
+ background-color: var(--color-fill);
209
+ text-decoration: none;
210
+ transition: all var(--transition-duration-fast);
211
+
212
+ &:hover {
213
+ background-color: var(--color-primary-light-9);
214
+ transform: translateY(-2px);
215
+ }
216
+
217
+ &__icon {
218
+ font-size: 28px;
219
+ margin-bottom: 10px;
220
+ }
221
+
222
+ &__title {
223
+ font-size: 14px;
224
+ color: var(--color-text-primary);
225
+ }
226
+ }
227
+
228
+ .activity-list {
229
+ display: flex;
230
+ flex-direction: column;
231
+ gap: 15px;
232
+ }
233
+
234
+ .activity-item {
235
+ display: flex;
236
+ align-items: center;
237
+ gap: 10px;
238
+ padding-bottom: 15px;
239
+ border-bottom: 1px solid var(--color-border-lighter);
240
+
241
+ &:last-child {
242
+ border-bottom: none;
243
+ padding-bottom: 0;
244
+ }
245
+
246
+ &__action {
247
+ flex: 1;
248
+ font-size: 14px;
249
+ color: var(--color-text-regular);
250
+ }
251
+
252
+ &__time {
253
+ font-size: 12px;
254
+ color: var(--color-text-placeholder);
255
+ }
256
+ }
257
+
258
+ .system-info {
259
+ display: grid;
260
+ grid-template-columns: repeat(2, 1fr);
261
+ gap: 20px;
262
+
263
+ @media (max-width: 768px) {
264
+ grid-template-columns: 1fr;
265
+ }
266
+
267
+ &__item {
268
+ display: flex;
269
+ align-items: center;
270
+ gap: 10px;
271
+ }
272
+
273
+ &__label {
274
+ font-size: 14px;
275
+ color: var(--color-text-secondary);
276
+ min-width: 80px;
277
+ }
278
+
279
+ &__value {
280
+ font-size: 14px;
281
+ color: var(--color-text-primary);
282
+ }
283
+ }
284
+ </style>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { useRouter } from 'vue-router'
3
+ import { Button } from '@xto/base'
4
+
5
+ const router = useRouter()
6
+
7
+ const goBack = () => {
8
+ router.push('/')
9
+ }
10
+ </script>
11
+
12
+ <template>
13
+ <div class="error-page">
14
+ <div class="error-page__content">
15
+ <div class="error-page__code">403</div>
16
+ <div class="error-page__title">无访问权限</div>
17
+ <div class="error-page__desc">抱歉,您没有权限访问此页面</div>
18
+ <Button type="primary" @click="goBack">返回首页</Button>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <style lang="scss" scoped>
24
+ .error-page {
25
+ width: 100%;
26
+ height: 100vh;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ background-color: var(--bg-color-page);
31
+
32
+ &__content {
33
+ text-align: center;
34
+ }
35
+
36
+ &__code {
37
+ font-size: 120px;
38
+ font-weight: 600;
39
+ color: var(--color-warning);
40
+ line-height: 1;
41
+ margin-bottom: 20px;
42
+ }
43
+
44
+ &__title {
45
+ font-size: 24px;
46
+ font-weight: 500;
47
+ color: var(--color-text-primary);
48
+ margin-bottom: 10px;
49
+ }
50
+
51
+ &__desc {
52
+ font-size: 14px;
53
+ color: var(--color-text-secondary);
54
+ margin-bottom: 30px;
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { useRouter } from 'vue-router'
3
+ import { Button } from '@xto/base'
4
+
5
+ const router = useRouter()
6
+
7
+ const goBack = () => {
8
+ router.push('/')
9
+ }
10
+ </script>
11
+
12
+ <template>
13
+ <div class="error-page">
14
+ <div class="error-page__content">
15
+ <div class="error-page__code">404</div>
16
+ <div class="error-page__title">页面不存在</div>
17
+ <div class="error-page__desc">抱歉,您访问的页面不存在或已被删除</div>
18
+ <Button type="primary" @click="goBack">返回首页</Button>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <style lang="scss" scoped>
24
+ .error-page {
25
+ width: 100%;
26
+ height: 100vh;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ background-color: var(--bg-color-page);
31
+
32
+ &__content {
33
+ text-align: center;
34
+ }
35
+
36
+ &__code {
37
+ font-size: 120px;
38
+ font-weight: 600;
39
+ color: var(--color-primary);
40
+ line-height: 1;
41
+ margin-bottom: 20px;
42
+ }
43
+
44
+ &__title {
45
+ font-size: 24px;
46
+ font-weight: 500;
47
+ color: var(--color-text-primary);
48
+ margin-bottom: 10px;
49
+ }
50
+
51
+ &__desc {
52
+ font-size: 14px;
53
+ color: var(--color-text-secondary);
54
+ margin-bottom: 30px;
55
+ }
56
+ }
57
+ </style>
@@ -0,0 +1,248 @@
1
+ <script setup lang="ts">
2
+ import { ref, reactive } from 'vue'
3
+ import { useRouter } from 'vue-router'
4
+ import { Button } from '@xto/base'
5
+ import { Form, FormItem, Input, Checkbox } from '@xto/form'
6
+ import { Message } from '@xto/feedback'
7
+ import { useAuthStore } from '@/stores/auth'
8
+ import { useUserStore } from '@/stores/user'
9
+ import { useMenuStore } from '@/stores/menu'
10
+
11
+ const router = useRouter()
12
+ const authStore = useAuthStore()
13
+ const userStore = useUserStore()
14
+ const menuStore = useMenuStore()
15
+
16
+ const loading = ref(false)
17
+ const rememberMe = ref(false)
18
+
19
+ const formData = reactive({
20
+ username: 'admin',
21
+ password: '123456'
22
+ })
23
+
24
+ const rules: Record<string, any[]> = {
25
+ username: [
26
+ { required: true, message: '请输入用户名', trigger: 'blur' }
27
+ ],
28
+ password: [
29
+ { required: true, message: '请输入密码', trigger: 'blur' },
30
+ { min: 6, message: '密码长度至少6位', trigger: 'blur' }
31
+ ]
32
+ }
33
+
34
+ const formRef = ref()
35
+
36
+ // 登录
37
+ const handleLogin = async () => {
38
+ try {
39
+ await formRef.value?.validate()
40
+ loading.value = true
41
+
42
+ // Mock 登录
43
+ setTimeout(() => {
44
+ // 设置 token
45
+ authStore.login({
46
+ token: 'mock_token_' + Date.now(),
47
+ refreshToken: 'mock_refresh_token',
48
+ expireTime: Date.now() + 7 * 24 * 60 * 60 * 1000
49
+ })
50
+
51
+ // 设置用户信息
52
+ userStore.setUserInfo({
53
+ id: 1,
54
+ username: formData.username,
55
+ nickname: '管理员',
56
+ avatar: '',
57
+ email: 'admin@example.com',
58
+ phone: '13800138000',
59
+ status: 1,
60
+ roles: ['admin'],
61
+ permissions: ['*'],
62
+ createTime: new Date().toISOString()
63
+ })
64
+
65
+ // 设置菜单
66
+ menuStore.setMenuList([
67
+ {
68
+ id: 1,
69
+ name: 'Dashboard',
70
+ path: '/dashboard',
71
+ component: 'dashboard/index',
72
+ icon: 'dashboard',
73
+ title: '仪表盘',
74
+ keepAlive: true,
75
+ affix: true
76
+ },
77
+ {
78
+ id: 2,
79
+ name: 'System',
80
+ path: '/system',
81
+ redirect: '/system/user',
82
+ icon: 'setting',
83
+ title: '系统管理',
84
+ children: [
85
+ {
86
+ id: 21,
87
+ name: 'SystemUser',
88
+ path: '/system/user',
89
+ component: 'system/user/index',
90
+ icon: 'user',
91
+ title: '用户管理',
92
+ keepAlive: true
93
+ },
94
+ {
95
+ id: 22,
96
+ name: 'SystemRole',
97
+ path: '/system/role',
98
+ component: 'system/role/index',
99
+ icon: 'role',
100
+ title: '角色管理',
101
+ keepAlive: true
102
+ },
103
+ {
104
+ id: 23,
105
+ name: 'SystemMenu',
106
+ path: '/system/menu',
107
+ component: 'system/menu/index',
108
+ icon: 'menu',
109
+ title: '菜单管理',
110
+ keepAlive: true
111
+ }
112
+ ]
113
+ }
114
+ ])
115
+
116
+ Message.success('登录成功')
117
+ router.push('/')
118
+ loading.value = false
119
+ }, 1000)
120
+ } catch (error) {
121
+ loading.value = false
122
+ }
123
+ }
124
+ </script>
125
+
126
+ <template>
127
+ <div class="login">
128
+ <div class="login__container">
129
+ <div class="login__header">
130
+ <img src="/vite.svg" alt="Logo" class="login__logo" />
131
+ <h1 class="login__title">Xto Demo</h1>
132
+ <p class="login__subtitle">后台管理系统</p>
133
+ </div>
134
+
135
+ <Form
136
+ ref="formRef"
137
+ :model="formData"
138
+ :rules="rules"
139
+ class="login__form"
140
+ label-width="0"
141
+ >
142
+ <FormItem prop="username">
143
+ <Input
144
+ v-model="formData.username"
145
+ placeholder="用户名"
146
+ prefix-icon="👤"
147
+ size="large"
148
+ />
149
+ </FormItem>
150
+
151
+ <FormItem prop="password">
152
+ <Input
153
+ v-model="formData.password"
154
+ type="password"
155
+ placeholder="密码"
156
+ prefix-icon="🔒"
157
+ size="large"
158
+ show-password
159
+ @keyup.enter="handleLogin"
160
+ />
161
+ </FormItem>
162
+
163
+ <FormItem>
164
+ <Checkbox v-model="rememberMe">记住我</Checkbox>
165
+ </FormItem>
166
+
167
+ <FormItem>
168
+ <Button
169
+ type="primary"
170
+ size="large"
171
+ :loading="loading"
172
+ class="login__submit"
173
+ @click="handleLogin"
174
+ >
175
+ 登录
176
+ </Button>
177
+ </FormItem>
178
+ </Form>
179
+
180
+ <div class="login__footer">
181
+ <p>提示: 任意用户名密码即可登录 (Mock 模式)</p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </template>
186
+
187
+ <style lang="scss" scoped>
188
+ .login {
189
+ width: 100%;
190
+ min-height: 100vh;
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: flex-end;
194
+ padding-right: 15%;
195
+ background: linear-gradient(135deg, var(--color-primary-light-9) 0%, var(--color-primary-light-7) 100%);
196
+
197
+ &__container {
198
+ width: 400px;
199
+ padding: 40px;
200
+ background-color: var(--bg-color);
201
+ border-radius: var(--border-radius-large);
202
+ box-shadow: var(--box-shadow-dark);
203
+ }
204
+
205
+ &__header {
206
+ text-align: center;
207
+ margin-bottom: 30px;
208
+ }
209
+
210
+ &__logo {
211
+ width: 60px;
212
+ height: 60px;
213
+ }
214
+
215
+ &__title {
216
+ font-size: 28px;
217
+ font-weight: 600;
218
+ color: var(--color-primary);
219
+ margin: 15px 0 5px;
220
+ }
221
+
222
+ &__subtitle {
223
+ font-size: 14px;
224
+ color: var(--color-text-secondary);
225
+ }
226
+
227
+ &__form {
228
+ :deep(.t-form-item) {
229
+ margin-bottom: 20px;
230
+ }
231
+
232
+ :deep(.x-input__prefix) {
233
+ margin-right: 8px;
234
+ }
235
+ }
236
+
237
+ &__submit {
238
+ width: 100%;
239
+ }
240
+
241
+ &__footer {
242
+ text-align: center;
243
+ margin-top: 20px;
244
+ font-size: 12px;
245
+ color: var(--color-text-placeholder);
246
+ }
247
+ }
248
+ </style>