@duxweb/dvha-template 1.0.8 → 1.0.10
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/bin/index.js +41 -31
- package/package.json +9 -7
- package/template/base/package.json +3 -3
- package/template/base/uno.config.ts +3 -3
- package/template/ui-configs/elementui/pages/layout.vue +42 -40
- package/template/ui-configs/elementui/pages/login.vue +19 -14
- package/template/ui-configs/pro/main.ts +82 -0
- package/template/ui-configs/pro/pages/home.vue +181 -0
- package/template/ui-configs/pro.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 DuxWeb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/bin/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { input, select, confirm } from '@inquirer/prompts'
|
|
7
|
-
import { cyan, green, red, yellow, blue, bold } from 'kolorist'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { confirm, input, select } from '@inquirer/prompts'
|
|
8
6
|
import { Command } from 'commander'
|
|
7
|
+
import fs from 'fs-extra'
|
|
8
|
+
import { bold, cyan, green, red, yellow } from 'kolorist'
|
|
9
9
|
|
|
10
10
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
|
|
@@ -29,7 +29,7 @@ async function createProject(projectName) {
|
|
|
29
29
|
if (!targetDir) {
|
|
30
30
|
targetDir = await input({
|
|
31
31
|
message: '请输入项目名称 / Enter project name:',
|
|
32
|
-
default: 'my-dvha-app'
|
|
32
|
+
default: 'my-dvha-app',
|
|
33
33
|
})
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -43,7 +43,7 @@ async function createProject(projectName) {
|
|
|
43
43
|
if (fs.existsSync(root)) {
|
|
44
44
|
const overwrite = await confirm({
|
|
45
45
|
message: `目录 ${targetDir} 已存在,是否覆盖? / Directory ${targetDir} already exists, overwrite?`,
|
|
46
|
-
default: false
|
|
46
|
+
default: false,
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
if (!overwrite) {
|
|
@@ -58,15 +58,15 @@ async function createProject(projectName) {
|
|
|
58
58
|
const uiConfigsDir = path.resolve(__dirname, '..', 'template', 'ui-configs')
|
|
59
59
|
const availableUIs = fs.readdirSync(uiConfigsDir)
|
|
60
60
|
.filter(dir => fs.statSync(path.join(uiConfigsDir, dir)).isDirectory())
|
|
61
|
-
.map(dir => {
|
|
62
|
-
const configPath = path.join(uiConfigsDir, dir
|
|
61
|
+
.map((dir) => {
|
|
62
|
+
const configPath = path.join(uiConfigsDir, `${dir}.json`)
|
|
63
63
|
if (fs.existsSync(configPath)) {
|
|
64
64
|
const config = fs.readJsonSync(configPath)
|
|
65
65
|
return {
|
|
66
66
|
name: config.name,
|
|
67
67
|
display: config.display,
|
|
68
68
|
description: config.description,
|
|
69
|
-
value: config.name
|
|
69
|
+
value: config.name,
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
return null
|
|
@@ -78,8 +78,8 @@ async function createProject(projectName) {
|
|
|
78
78
|
choices: availableUIs.map(ui => ({
|
|
79
79
|
name: `${ui.display} - ${ui.description}`,
|
|
80
80
|
value: ui.value,
|
|
81
|
-
description: ui.description
|
|
82
|
-
}))
|
|
81
|
+
description: ui.description,
|
|
82
|
+
})),
|
|
83
83
|
})
|
|
84
84
|
|
|
85
85
|
if (!template) {
|
|
@@ -117,19 +117,19 @@ async function createProject(projectName) {
|
|
|
117
117
|
const fileName = path.basename(src)
|
|
118
118
|
|
|
119
119
|
// 过滤掉不需要的目录和文件
|
|
120
|
-
const shouldExclude
|
|
121
|
-
relativePath.includes('node_modules')
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
const shouldExclude
|
|
121
|
+
= relativePath.includes('node_modules')
|
|
122
|
+
|| relativePath.includes('.git')
|
|
123
|
+
|| relativePath.includes('dist')
|
|
124
|
+
|| relativePath.includes('.vite')
|
|
125
|
+
|| fileName === '.DS_Store'
|
|
126
|
+
|| fileName.endsWith('.log')
|
|
127
|
+
|| fileName === 'bun.lockb'
|
|
128
|
+
|| fileName === 'package-lock.json'
|
|
129
|
+
|| fileName === 'yarn.lock'
|
|
130
130
|
|
|
131
131
|
return !shouldExclude
|
|
132
|
-
}
|
|
132
|
+
},
|
|
133
133
|
})
|
|
134
134
|
|
|
135
135
|
// 2. 复制UI特定的pages文件
|
|
@@ -139,6 +139,13 @@ async function createProject(projectName) {
|
|
|
139
139
|
fs.copySync(uiPagesDir, targetPagesDir)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// 2.5. 检查并复制UI特定的main.ts文件(如果存在)
|
|
143
|
+
const uiMainTsPath = path.resolve(__dirname, '..', 'template', 'ui-configs', template, 'main.ts')
|
|
144
|
+
if (fs.existsSync(uiMainTsPath)) {
|
|
145
|
+
const targetMainTsPath = path.join(root, 'main.ts')
|
|
146
|
+
fs.copySync(uiMainTsPath, targetMainTsPath)
|
|
147
|
+
}
|
|
148
|
+
|
|
142
149
|
// 3. 更新package.json
|
|
143
150
|
const pkgPath = path.join(root, 'package.json')
|
|
144
151
|
if (fs.existsSync(pkgPath)) {
|
|
@@ -150,22 +157,24 @@ async function createProject(projectName) {
|
|
|
150
157
|
// 更新依赖
|
|
151
158
|
pkg.dependencies = {
|
|
152
159
|
...pkg.dependencies,
|
|
153
|
-
...uiConfig.dependencies
|
|
160
|
+
...uiConfig.dependencies,
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
if (uiConfig.devDependencies && Object.keys(uiConfig.devDependencies).length > 0) {
|
|
157
164
|
pkg.devDependencies = {
|
|
158
165
|
...pkg.devDependencies,
|
|
159
|
-
...uiConfig.devDependencies
|
|
166
|
+
...uiConfig.devDependencies,
|
|
160
167
|
}
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
fs.writeJsonSync(pkgPath, pkg, { spaces: 2 })
|
|
164
171
|
}
|
|
165
172
|
|
|
166
|
-
// 4. 更新main.ts
|
|
173
|
+
// 4. 更新main.ts(仅当UI配置没有自定义main.ts时)
|
|
167
174
|
const mainTsPath = path.join(root, 'main.ts')
|
|
168
|
-
|
|
175
|
+
|
|
176
|
+
// 如果UI配置有自定义main.ts,跳过修改;否则修改基础main.ts
|
|
177
|
+
if (!fs.existsSync(uiMainTsPath) && fs.existsSync(mainTsPath)) {
|
|
169
178
|
let mainTsContent = fs.readFileSync(mainTsPath, 'utf-8')
|
|
170
179
|
|
|
171
180
|
// 在导入语句后添加UI库的导入
|
|
@@ -173,7 +182,7 @@ async function createProject(projectName) {
|
|
|
173
182
|
const appUseStatements = uiConfig.appUse || []
|
|
174
183
|
|
|
175
184
|
// 找到现有导入的位置
|
|
176
|
-
const appImportIndex = mainTsContent.indexOf(
|
|
185
|
+
const appImportIndex = mainTsContent.indexOf('import App from \'./App.vue\'')
|
|
177
186
|
if (appImportIndex !== -1) {
|
|
178
187
|
const insertPosition = mainTsContent.indexOf('\n', appImportIndex) + 1
|
|
179
188
|
const additionalImports = importStatements.join('\n') + (importStatements.length > 0 ? '\n' : '')
|
|
@@ -184,7 +193,7 @@ async function createProject(projectName) {
|
|
|
184
193
|
if (appUseStatements.length > 0) {
|
|
185
194
|
const appUseIndex = mainTsContent.indexOf('app.use(createDux(config))')
|
|
186
195
|
if (appUseIndex !== -1) {
|
|
187
|
-
const additionalUse = appUseStatements.join('\n')
|
|
196
|
+
const additionalUse = `${appUseStatements.join('\n')}\n`
|
|
188
197
|
mainTsContent = mainTsContent.slice(0, appUseIndex) + additionalUse + mainTsContent.slice(appUseIndex)
|
|
189
198
|
}
|
|
190
199
|
}
|
|
@@ -223,7 +232,8 @@ program
|
|
|
223
232
|
.action(async (projectName) => {
|
|
224
233
|
try {
|
|
225
234
|
await createProject(projectName)
|
|
226
|
-
}
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
227
237
|
if (error.name === 'ExitPromptError') {
|
|
228
238
|
console.log(yellow('\n👋 操作已取消 / Operation cancelled'))
|
|
229
239
|
process.exit(0)
|
|
@@ -247,4 +257,4 @@ program
|
|
|
247
257
|
console.log()
|
|
248
258
|
})
|
|
249
259
|
|
|
250
|
-
program.parse()
|
|
260
|
+
program.parse()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duxweb/dvha-template",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.10",
|
|
5
5
|
"description": "Create DVHA project from template",
|
|
6
6
|
"author": "DuxWeb",
|
|
7
7
|
"license": "MIT",
|
|
@@ -36,15 +36,17 @@
|
|
|
36
36
|
"engines": {
|
|
37
37
|
"node": ">=20.0.0"
|
|
38
38
|
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"test": "./scripts/test.sh",
|
|
41
|
-
"push": "./scripts/publish.sh",
|
|
42
|
-
"dev": "bun run bin/index.js"
|
|
43
|
-
},
|
|
44
39
|
"dependencies": {
|
|
45
40
|
"@inquirer/prompts": "^7.5.1",
|
|
46
41
|
"commander": "^14.0.0",
|
|
47
42
|
"fs-extra": "^11.2.0",
|
|
48
43
|
"kolorist": "^1.8.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"test": "./scripts/test.sh",
|
|
47
|
+
"push": "./scripts/publish.sh",
|
|
48
|
+
"dev": "node bin/index.js",
|
|
49
|
+
"changeset": "changeset",
|
|
50
|
+
"changeset:version": "changeset version"
|
|
49
51
|
}
|
|
50
|
-
}
|
|
52
|
+
}
|
|
@@ -25,11 +25,11 @@
|
|
|
25
25
|
"clsx": "^2.1.1",
|
|
26
26
|
"lodash-es": "^4.17.21",
|
|
27
27
|
"petite-vue-i18n": "^11.1.4",
|
|
28
|
-
"pinia": "^3.0.
|
|
29
|
-
"pinia-plugin-persistedstate": "^4.
|
|
28
|
+
"pinia": "^3.0.3",
|
|
29
|
+
"pinia-plugin-persistedstate": "^4.3.0",
|
|
30
30
|
"unocss": "^66.1.2",
|
|
31
31
|
"vue": "^3.5.0",
|
|
32
|
-
"vue-router": "^4.
|
|
32
|
+
"vue-router": "^4.5.1"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@antfu/eslint-config": "^4.13.1",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { defineConfig, presetIcons, presetWind3 } from 'unocss'
|
|
2
1
|
import icons from '@iconify-json/tabler/icons.json'
|
|
2
|
+
import { defineConfig, presetIcons, presetWind3 } from 'unocss'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function generateSafeList() {
|
|
5
5
|
return Object.keys(icons.icons).flatMap((item) => {
|
|
6
6
|
return `i-tabler:${item}`
|
|
7
7
|
})
|
|
8
|
-
}
|
|
8
|
+
}
|
|
9
9
|
|
|
10
10
|
const safeList = generateSafeList()
|
|
11
11
|
|
|
@@ -1,30 +1,62 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useGetAuth, useLogout, useManage } from '@duxweb/dvha-core'
|
|
3
|
+
import { onMounted, onUnmounted, ref } from 'vue'
|
|
4
|
+
import Sidebar from './menu.vue'
|
|
5
|
+
|
|
6
|
+
const manage = useManage()
|
|
7
|
+
const user = useGetAuth()
|
|
8
|
+
|
|
9
|
+
const { mutate: logout } = useLogout({
|
|
10
|
+
onSuccess: () => console.log('退出成功'),
|
|
11
|
+
onError: error => console.error('退出失败:', error),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const sidebarOpen = ref(false)
|
|
15
|
+
const showUserMenu = ref(false)
|
|
16
|
+
|
|
17
|
+
const getUserName = () => user?.info?.name || user?.info?.username || 'Admin'
|
|
18
|
+
const toggleSidebar = () => sidebarOpen.value = !sidebarOpen.value
|
|
19
|
+
const handleLogout = () => logout()
|
|
20
|
+
|
|
21
|
+
function closeDropdowns(event: Event) {
|
|
22
|
+
if (!(event.target as HTMLElement).closest('.relative')) {
|
|
23
|
+
showUserMenu.value = false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
onMounted(() => document.addEventListener('click', closeDropdowns))
|
|
28
|
+
onUnmounted(() => document.removeEventListener('click', closeDropdowns))
|
|
29
|
+
</script>
|
|
30
|
+
|
|
1
31
|
<template>
|
|
2
32
|
<div class="min-h-screen bg-gray-50 flex flex-col">
|
|
3
33
|
<!-- 头部 -->
|
|
4
34
|
<header class="bg-white shadow-sm border-b fixed w-full top-0 z-50">
|
|
5
35
|
<div class="flex items-center justify-between h-16 px-4">
|
|
6
36
|
<div class="flex items-center">
|
|
7
|
-
<button
|
|
8
|
-
<div class="i-tabler:menu-2 text-xl"
|
|
37
|
+
<button class="p-2 rounded hover:bg-gray-100 lg:hidden" @click="toggleSidebar">
|
|
38
|
+
<div class="i-tabler:menu-2 text-xl" />
|
|
9
39
|
</button>
|
|
10
40
|
<div class="flex items-center ml-4 lg:ml-0">
|
|
11
|
-
<div class="i-tabler:brand-vue text-2xl text-blue-600 mr-3"
|
|
12
|
-
<h1 class="text-xl font-bold">
|
|
41
|
+
<div class="i-tabler:brand-vue text-2xl text-blue-600 mr-3" />
|
|
42
|
+
<h1 class="text-xl font-bold">
|
|
43
|
+
{{ manage.config?.title || 'Dux Admin' }}
|
|
44
|
+
</h1>
|
|
13
45
|
</div>
|
|
14
46
|
</div>
|
|
15
47
|
|
|
16
48
|
<!-- 用户菜单 -->
|
|
17
49
|
<div class="relative">
|
|
18
|
-
<button
|
|
19
|
-
<div class="i-tabler:user-circle text-xl mr-2"
|
|
50
|
+
<button class="flex items-center p-2 rounded hover:bg-gray-100" @click="showUserMenu = !showUserMenu">
|
|
51
|
+
<div class="i-tabler:user-circle text-xl mr-2" />
|
|
20
52
|
<span class="hidden md:block text-sm font-medium">{{ getUserName() }}</span>
|
|
21
|
-
<div class="i-tabler:chevron-down text-sm ml-1 hidden md:block"
|
|
53
|
+
<div class="i-tabler:chevron-down text-sm ml-1 hidden md:block" />
|
|
22
54
|
</button>
|
|
23
55
|
|
|
24
56
|
<div v-if="showUserMenu" class="absolute right-0 mt-2 w-40 bg-white rounded-md shadow-lg border z-50">
|
|
25
57
|
<div class="py-1">
|
|
26
|
-
<button
|
|
27
|
-
<div class="i-tabler:logout mr-3"
|
|
58
|
+
<button class="flex items-center w-full px-4 py-2 text-sm hover:bg-gray-100" @click="handleLogout">
|
|
59
|
+
<div class="i-tabler:logout mr-3" />
|
|
28
60
|
退出登录
|
|
29
61
|
</button>
|
|
30
62
|
</div>
|
|
@@ -38,7 +70,7 @@
|
|
|
38
70
|
<Sidebar :sidebar-open="sidebarOpen" />
|
|
39
71
|
|
|
40
72
|
<!-- 遮罩层 -->
|
|
41
|
-
<div v-if="sidebarOpen"
|
|
73
|
+
<div v-if="sidebarOpen" class="fixed inset-0 z-30 bg-black bg-opacity-50 lg:hidden" @click="toggleSidebar" />
|
|
42
74
|
|
|
43
75
|
<!-- 主内容 -->
|
|
44
76
|
<main class="flex-1">
|
|
@@ -47,33 +79,3 @@
|
|
|
47
79
|
</div>
|
|
48
80
|
</div>
|
|
49
81
|
</template>
|
|
50
|
-
|
|
51
|
-
<script setup lang="ts">
|
|
52
|
-
import { ref, onMounted, onUnmounted } from 'vue'
|
|
53
|
-
import { useManage, useGetAuth, useLogout } from '@duxweb/dvha-core'
|
|
54
|
-
import Sidebar from './menu.vue'
|
|
55
|
-
|
|
56
|
-
const manage = useManage()
|
|
57
|
-
const user = useGetAuth()
|
|
58
|
-
|
|
59
|
-
const { mutate: logout } = useLogout({
|
|
60
|
-
onSuccess: () => console.log('退出成功'),
|
|
61
|
-
onError: (error) => console.error('退出失败:', error)
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const sidebarOpen = ref(false)
|
|
65
|
-
const showUserMenu = ref(false)
|
|
66
|
-
|
|
67
|
-
const getUserName = () => user?.info?.name || user?.info?.username || 'Admin'
|
|
68
|
-
const toggleSidebar = () => sidebarOpen.value = !sidebarOpen.value
|
|
69
|
-
const handleLogout = () => logout()
|
|
70
|
-
|
|
71
|
-
const closeDropdowns = (event: Event) => {
|
|
72
|
-
if (!(event.target as HTMLElement).closest('.relative')) {
|
|
73
|
-
showUserMenu.value = false
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
onMounted(() => document.addEventListener('click', closeDropdowns))
|
|
78
|
-
onUnmounted(() => document.removeEventListener('click', closeDropdowns))
|
|
79
|
-
</script>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { FormInstance } from 'element-plus'
|
|
3
|
+
import { useLogin } from '@duxweb/dvha-core'
|
|
4
|
+
import { Lock, User } from '@element-plus/icons-vue'
|
|
3
5
|
import { ElMessage } from 'element-plus'
|
|
6
|
+
import { reactive, ref } from 'vue'
|
|
4
7
|
import { useRouter } from 'vue-router'
|
|
5
|
-
import { useLogin } from '@duxweb/dvha-core'
|
|
6
|
-
import type { FormInstance } from 'element-plus'
|
|
7
|
-
import { User, Lock } from '@element-plus/icons-vue'
|
|
8
8
|
import 'element-plus/dist/index.css'
|
|
9
9
|
|
|
10
10
|
const router = useRouter()
|
|
@@ -14,16 +14,16 @@ const formRef = ref<FormInstance>()
|
|
|
14
14
|
const formModel = reactive({
|
|
15
15
|
username: '',
|
|
16
16
|
password: '',
|
|
17
|
-
remember: false
|
|
17
|
+
remember: false,
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
const rules = reactive({
|
|
21
21
|
username: [
|
|
22
|
-
{ required: true, message: '请输入用户名', trigger: 'blur' }
|
|
22
|
+
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
|
23
23
|
],
|
|
24
24
|
password: [
|
|
25
|
-
{ required: true, message: '请输入密码', trigger: 'blur' }
|
|
26
|
-
]
|
|
25
|
+
{ required: true, message: '请输入密码', trigger: 'blur' },
|
|
26
|
+
],
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
const { mutate, isLoading } = useLogin({
|
|
@@ -33,18 +33,19 @@ const { mutate, isLoading } = useLogin({
|
|
|
33
33
|
onError: (error) => {
|
|
34
34
|
console.log(error)
|
|
35
35
|
ElMessage.error(error?.message || '登录失败,请检查用户名和密码')
|
|
36
|
-
}
|
|
36
|
+
},
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
if (!formRef.value)
|
|
39
|
+
function handleSubmit() {
|
|
40
|
+
if (!formRef.value)
|
|
41
|
+
return
|
|
41
42
|
|
|
42
43
|
formRef.value.validate((valid) => {
|
|
43
44
|
if (valid) {
|
|
44
45
|
mutate({
|
|
45
46
|
username: formModel.username,
|
|
46
47
|
password: formModel.password,
|
|
47
|
-
code:
|
|
48
|
+
code: '0000',
|
|
48
49
|
})
|
|
49
50
|
}
|
|
50
51
|
})
|
|
@@ -55,8 +56,12 @@ const handleSubmit = () => {
|
|
|
55
56
|
<div class="flex justify-center items-center min-h-screen bg-gray-100">
|
|
56
57
|
<div class="w-400px p-40px rounded-8px bg-white shadow-sm">
|
|
57
58
|
<div class="text-center mb-40px">
|
|
58
|
-
<h1 class="m-0 mb-8px text-24px font-600 text-gray-800"
|
|
59
|
-
|
|
59
|
+
<h1 class="m-0 mb-8px text-24px font-600 text-gray-800">
|
|
60
|
+
系统登录
|
|
61
|
+
</h1>
|
|
62
|
+
<p class="m-0 text-gray-500 text-14px">
|
|
63
|
+
欢迎使用管理系统,请登录
|
|
64
|
+
</p>
|
|
60
65
|
</div>
|
|
61
66
|
|
|
62
67
|
<el-form
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { IConfig } from '@duxweb/dvha-core'
|
|
2
|
+
import { createDux, i18nProvider, simpleAuthProvider, simpleDataProvider } from '@duxweb/dvha-core'
|
|
3
|
+
import * as DuxPro from '@duxweb/dvha-pro'
|
|
4
|
+
import * as NaiveUI from 'naive-ui'
|
|
5
|
+
import { createApp } from 'vue'
|
|
6
|
+
|
|
7
|
+
import '@duxweb/dvha-pro/style.css'
|
|
8
|
+
|
|
9
|
+
const { createDuxPro, DuxApp, DuxAuthLayout, DuxLayout, DuxLoginPage, DuxPage404, DuxPage500, DuxPageLoading, enUS, zhCN } = DuxPro
|
|
10
|
+
|
|
11
|
+
const app = createApp(DuxApp)
|
|
12
|
+
|
|
13
|
+
const config: IConfig = {
|
|
14
|
+
defaultManage: 'admin',
|
|
15
|
+
manages: [
|
|
16
|
+
{
|
|
17
|
+
name: 'admin',
|
|
18
|
+
title: 'Dux Pro 管理系统',
|
|
19
|
+
routePrefix: '/admin',
|
|
20
|
+
apiUrl: '/admin',
|
|
21
|
+
apiRoutePath: '/routes',
|
|
22
|
+
components: {
|
|
23
|
+
authLayout: DuxAuthLayout,
|
|
24
|
+
noAuthLayout: DuxLayout,
|
|
25
|
+
notFound: DuxPage404,
|
|
26
|
+
loading: DuxPageLoading,
|
|
27
|
+
error: DuxPage500,
|
|
28
|
+
},
|
|
29
|
+
userMenus: [
|
|
30
|
+
{
|
|
31
|
+
label: '设置',
|
|
32
|
+
key: 'setting',
|
|
33
|
+
icon: 'i-tabler:settings',
|
|
34
|
+
path: 'setting',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
routes: [
|
|
38
|
+
{
|
|
39
|
+
name: 'admin.login',
|
|
40
|
+
path: 'login',
|
|
41
|
+
component: DuxLoginPage,
|
|
42
|
+
meta: {
|
|
43
|
+
authorization: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
menus: [
|
|
48
|
+
{
|
|
49
|
+
name: 'home',
|
|
50
|
+
path: 'index',
|
|
51
|
+
icon: 'i-tabler:home',
|
|
52
|
+
label: '工作台',
|
|
53
|
+
component: () => import('./pages/home.vue'),
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
dataProvider: simpleDataProvider({
|
|
59
|
+
apiUrl: 'https://m1.apifoxmock.com/m1/4407506-4052338-default/admin',
|
|
60
|
+
}),
|
|
61
|
+
authProvider: simpleAuthProvider(),
|
|
62
|
+
i18nProvider: i18nProvider({
|
|
63
|
+
locale: 'zh-CN',
|
|
64
|
+
fallbackLocale: 'en-US',
|
|
65
|
+
messages: {
|
|
66
|
+
'zh-CN': zhCN,
|
|
67
|
+
'en-US': enUS,
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
remote: {
|
|
71
|
+
packages: {
|
|
72
|
+
'naive-ui': NaiveUI,
|
|
73
|
+
'@duxweb/dvha-pro': DuxPro,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
app.use(createDux(config))
|
|
79
|
+
app.use(NaiveUI)
|
|
80
|
+
app.use(createDuxPro())
|
|
81
|
+
|
|
82
|
+
app.mount('#app')
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { DuxCard, DuxChart, DuxDashboardQuick } from '@duxweb/dvha-pro'
|
|
3
|
+
import { NButton, NGi, NGrid } from 'naive-ui'
|
|
4
|
+
import { computed, ref } from 'vue'
|
|
5
|
+
|
|
6
|
+
// 基本统计数据
|
|
7
|
+
const stats = ref([
|
|
8
|
+
{
|
|
9
|
+
label: '总订单',
|
|
10
|
+
icon: 'i-tabler:shopping-cart',
|
|
11
|
+
value: 126,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: '总用户',
|
|
15
|
+
icon: 'i-tabler:users',
|
|
16
|
+
value: 89,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: '总收入',
|
|
20
|
+
icon: 'i-tabler:currency-dollar',
|
|
21
|
+
value: '¥12,543',
|
|
22
|
+
},
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
// 图表数据
|
|
26
|
+
const chartData = computed(() => ({
|
|
27
|
+
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
|
28
|
+
data: [
|
|
29
|
+
{
|
|
30
|
+
name: '访问量',
|
|
31
|
+
type: 'line',
|
|
32
|
+
data: [120, 132, 101, 134, 90, 230, 210],
|
|
33
|
+
color: '#18a058',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
}))
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<div class="p-6 space-y-6">
|
|
41
|
+
<!-- 欢迎区域 -->
|
|
42
|
+
<DuxCard>
|
|
43
|
+
<div class="text-center py-8">
|
|
44
|
+
<div class="i-tabler:brand-vue text-6xl text-blue-600 mx-auto mb-6" />
|
|
45
|
+
<h1 class="text-3xl font-bold text-gray-900 dark:text-gray-100 mb-4">
|
|
46
|
+
欢迎使用 Dux Pro
|
|
47
|
+
</h1>
|
|
48
|
+
<p class="text-gray-600 dark:text-gray-300 text-lg max-w-2xl mx-auto">
|
|
49
|
+
基于 Vue 3 + Naive UI + Dux Pro 组件库构建的现代化管理后台,提供丰富的业务组件和开箱即用的功能
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
</DuxCard>
|
|
53
|
+
|
|
54
|
+
<!-- 统计卡片 -->
|
|
55
|
+
<NGrid :cols="3" :x-gap="16">
|
|
56
|
+
<NGi v-for="item in stats" :key="item.label">
|
|
57
|
+
<DuxDashboardQuick
|
|
58
|
+
:label="item.label"
|
|
59
|
+
:icon="item.icon"
|
|
60
|
+
:value="item.value"
|
|
61
|
+
/>
|
|
62
|
+
</NGi>
|
|
63
|
+
</NGrid>
|
|
64
|
+
|
|
65
|
+
<!-- 图表区域 -->
|
|
66
|
+
<DuxCard>
|
|
67
|
+
<template #header>
|
|
68
|
+
<div class="flex items-center justify-between">
|
|
69
|
+
<h3 class="text-lg font-semibold">
|
|
70
|
+
数据概览
|
|
71
|
+
</h3>
|
|
72
|
+
<NButton size="small" type="primary">
|
|
73
|
+
查看详情
|
|
74
|
+
</NButton>
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
|
77
|
+
<DuxChart :option="chartData" style="height: 300px;" />
|
|
78
|
+
</DuxCard>
|
|
79
|
+
|
|
80
|
+
<!-- Dux Pro 特性展示 -->
|
|
81
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
|
|
82
|
+
<DuxCard>
|
|
83
|
+
<div class="text-center p-4">
|
|
84
|
+
<div class="i-tabler:components text-4xl text-purple-600 mb-4" />
|
|
85
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
|
86
|
+
丰富组件
|
|
87
|
+
</h3>
|
|
88
|
+
<p class="text-gray-600 dark:text-gray-300 text-sm">
|
|
89
|
+
提供丰富的业务组件,快速构建管理后台
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</DuxCard>
|
|
93
|
+
|
|
94
|
+
<DuxCard>
|
|
95
|
+
<div class="text-center p-4">
|
|
96
|
+
<div class="i-tabler:chart-bar text-4xl text-green-600 mb-4" />
|
|
97
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
|
98
|
+
数据可视化
|
|
99
|
+
</h3>
|
|
100
|
+
<p class="text-gray-600 dark:text-gray-300 text-sm">
|
|
101
|
+
内置图表组件,轻松展示数据分析结果
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
</DuxCard>
|
|
105
|
+
|
|
106
|
+
<DuxCard>
|
|
107
|
+
<div class="text-center p-4">
|
|
108
|
+
<div class="i-tabler:layout-dashboard text-4xl text-blue-600 mb-4" />
|
|
109
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
|
|
110
|
+
页面布局
|
|
111
|
+
</h3>
|
|
112
|
+
<p class="text-gray-600 dark:text-gray-300 text-sm">
|
|
113
|
+
灵活的布局组件,适应各种业务场景
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
</DuxCard>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<!-- 快速操作 -->
|
|
120
|
+
<DuxCard>
|
|
121
|
+
<template #header>
|
|
122
|
+
<h3 class="text-lg font-semibold">
|
|
123
|
+
快速操作
|
|
124
|
+
</h3>
|
|
125
|
+
</template>
|
|
126
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
127
|
+
<NButton block type="primary">
|
|
128
|
+
<template #icon>
|
|
129
|
+
<div class="i-tabler:plus" />
|
|
130
|
+
</template>
|
|
131
|
+
创建订单
|
|
132
|
+
</NButton>
|
|
133
|
+
<NButton block>
|
|
134
|
+
<template #icon>
|
|
135
|
+
<div class="i-tabler:users" />
|
|
136
|
+
</template>
|
|
137
|
+
用户管理
|
|
138
|
+
</NButton>
|
|
139
|
+
<NButton block>
|
|
140
|
+
<template #icon>
|
|
141
|
+
<div class="i-tabler:chart-bar" />
|
|
142
|
+
</template>
|
|
143
|
+
数据统计
|
|
144
|
+
</NButton>
|
|
145
|
+
<NButton block>
|
|
146
|
+
<template #icon>
|
|
147
|
+
<div class="i-tabler:settings" />
|
|
148
|
+
</template>
|
|
149
|
+
系统设置
|
|
150
|
+
</NButton>
|
|
151
|
+
</div>
|
|
152
|
+
</DuxCard>
|
|
153
|
+
|
|
154
|
+
<!-- 开始使用 -->
|
|
155
|
+
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950 dark:to-indigo-950 rounded-lg border p-8 text-center">
|
|
156
|
+
<h3 class="text-xl font-bold text-gray-900 dark:text-gray-100 mb-4">
|
|
157
|
+
开始构建您的 Pro 应用
|
|
158
|
+
</h3>
|
|
159
|
+
<p class="text-gray-600 dark:text-gray-300 mb-6">
|
|
160
|
+
Dux Pro 为您提供了构建现代化管理系统所需的所有专业组件
|
|
161
|
+
</p>
|
|
162
|
+
<div class="flex justify-center space-x-4">
|
|
163
|
+
<a
|
|
164
|
+
href="https://github.com/duxweb/dvha" target="_blank"
|
|
165
|
+
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium"
|
|
166
|
+
>
|
|
167
|
+
<div class="i-tabler:brand-github mr-2" />
|
|
168
|
+
查看源码
|
|
169
|
+
</a>
|
|
170
|
+
<a href="#" class="inline-flex items-center px-4 py-2 border border-gray-300 text-gray-700 dark:text-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 text-sm font-medium">
|
|
171
|
+
<div class="i-tabler:book mr-2" />
|
|
172
|
+
查看文档
|
|
173
|
+
</a>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<style scoped>
|
|
180
|
+
/* 自定义样式 */
|
|
181
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pro",
|
|
3
|
+
"display": "Vue 3 + Dux Pro",
|
|
4
|
+
"description": "使用 Dux Pro 组件库的 Vue 3 项目",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@duxweb/dvha-core": "latest",
|
|
7
|
+
"@duxweb/dvha-naiveui": "latest",
|
|
8
|
+
"@duxweb/dvha-pro": "latest",
|
|
9
|
+
"naive-ui": "^2.42.0",
|
|
10
|
+
"vue": "^3.5.0",
|
|
11
|
+
"vue-router": "^4.5.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {}
|
|
14
|
+
}
|