softleader-nuxt-core 1.0.9 → 1.2.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 +93 -64
- package/assets/css/layout.css +56 -20
- package/bin/init.mjs +244 -155
- package/commitlint.config.cjs +1 -0
- package/components/templates/Welcome.vue +715 -0
- package/components/uiBusiness/BDataTable.vue +977 -888
- package/components/uiBusiness/CitySelect.vue +2 -2
- package/components/uiBusiness/PolicyForm.vue +9 -3
- package/components/uiInterface/IAvatar.vue +1 -1
- package/components/uiInterface/IBreadcrumbs.vue +1 -1
- package/components/uiInterface/IButton.vue +77 -20
- package/components/uiInterface/ICard.vue +9 -4
- package/components/uiInterface/ICheckbox.vue +12 -6
- package/components/uiInterface/IDataTable.vue +534 -515
- package/components/uiInterface/IRadio.vue +8 -2
- package/components/uiInterface/ISelect.vue +44 -19
- package/components/uiInterface/IStack.vue +9 -2
- package/components/uiInterface/ISwitch.vue +14 -3
- package/components/uiInterface/ITextField.vue +51 -24
- package/components/uiInterface/ITextarea.vue +54 -33
- package/composables/useApiRegistry.ts +1 -1
- package/composables/useErrorHandler.ts +12 -0
- package/composables/useFeatureFlag.ts +1 -1
- package/composables/useFileUpload.ts +2 -1
- package/composables/useFormatter.ts +2 -2
- package/composables/useModules.ts +18 -0
- package/composables/useOptions.ts +41 -22
- package/composables/useRepo.ts +49 -0
- package/configs/default.json +36 -9
- package/core/config/app.ts +10 -21
- package/core/config/components.ts +13 -25
- package/core/config/css.ts +4 -1
- package/core/config/eslint.config.mjs +180 -0
- package/core/config/git/commit-types.cjs +30 -0
- package/core/config/git/commitlint.config.cjs +17 -0
- package/core/config/git/cz-config.cjs +21 -0
- package/core/config/git/lint-staged.config.js +6 -0
- package/core/config/gitignore +30 -0
- package/core/config/i18n.ts +9 -7
- package/core/config/layout.ts +22 -16
- package/core/config/modules.ts +10 -6
- package/core/config/nitro.ts +13 -15
- package/core/config/prettier.json +14 -0
- package/core/config/runtime.ts +5 -29
- package/core/config/security/README.md +3 -1
- package/core/config/theme-tokens.ts +36 -270
- package/core/config/vite.ts +3 -4
- package/core/options/registry.ts +13 -2
- package/core/options/types.ts +1 -1
- package/core/repositories/index.ts +1 -1
- package/core/templates/gitignore +38 -0
- package/core/templates/husky/commit-msg +6 -0
- package/core/templates/husky/pre-commit +6 -0
- package/core/templates/vscode/extensions.json +8 -0
- package/core/templates/vscode/settings.json +69 -0
- package/core/templates/vscode/snippets.code-snippets +16 -0
- package/core/templates/vscode/vue3Template.code-snippets +165 -0
- package/cz.config.cjs +1 -0
- package/eslint.config.mjs +3 -0
- package/i18n/locales/en-US.json +1 -1
- package/i18n/locales/zh-TW.json +1 -1
- package/layouts/default.vue +118 -35
- package/lint-staged.config.js +1 -0
- package/modules/options-scanner.ts +113 -0
- package/modules/repositories-scanner.ts +95 -0
- package/nuxt.config.ts +66 -13
- package/package.json +32 -6
- package/plugins/README.md +2 -0
- package/plugins/api.ts +7 -4
- package/plugins/vuetify.ts +10 -10
- package/prettier.config.cjs +1 -0
- package/schemas/config.schema.json +128 -0
- package/scripts/product-loader.ts +95 -19
- package/scripts/release.mjs +24 -0
- package/scripts/sync-configs.mjs +53 -0
- package/stores/common.ts +3 -3
- package/tsconfig.json +16 -1
- package/types/repo.ts +13 -0
- package/components/uiInterface/layout/IBreadcrumbs.vue +0 -120
package/bin/init.mjs
CHANGED
|
@@ -1,155 +1,244 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
path.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* softleader-nuxt-core CLI
|
|
5
|
+
* 支援指令:
|
|
6
|
+
* init <project-name> : 建立全新專案
|
|
7
|
+
* sync [path] : 同步現有專案架構與配置
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs'
|
|
11
|
+
import path from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { execSync } from 'node:child_process'
|
|
14
|
+
import { parseArgs } from 'node:util'
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
17
|
+
const __dirname = path.dirname(__filename)
|
|
18
|
+
|
|
19
|
+
// 解析參數
|
|
20
|
+
const options = {
|
|
21
|
+
help: { type: 'boolean', short: 'h' }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { values, positionals } = parseArgs({ options, allowPositionals: true })
|
|
25
|
+
|
|
26
|
+
if (values.help || positionals.length === 0) {
|
|
27
|
+
console.log(`
|
|
28
|
+
用法 (Usage):
|
|
29
|
+
npx softleader-nuxt-core init [project-name] 建立全新專案
|
|
30
|
+
npx softleader-nuxt-core sync [path] 同步現有專案架構 (不覆寫業務代碼)
|
|
31
|
+
|
|
32
|
+
參數 (Arguments):
|
|
33
|
+
[project-name] / [path] 目標資料夾路徑 (預設: 當前目錄)
|
|
34
|
+
|
|
35
|
+
選項 (Options):
|
|
36
|
+
-h, --help 顯示指令幫助訊息
|
|
37
|
+
`)
|
|
38
|
+
process.exit(0)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const command = positionals[0]
|
|
42
|
+
const isInit = command === 'init'
|
|
43
|
+
const isSync = command === 'sync'
|
|
44
|
+
|
|
45
|
+
if (!isInit && !isSync) {
|
|
46
|
+
console.error(`錯誤: 不支援的指令 "${command}"。請使用 init 或 sync。`)
|
|
47
|
+
process.exit(1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const targetPath = positionals[1] || (isInit ? 'my-nuxt-app' : '.')
|
|
51
|
+
const targetDir = path.resolve(process.cwd(), targetPath)
|
|
52
|
+
const projectName = path.basename(targetDir)
|
|
53
|
+
const packageName = projectName.toLowerCase().replace(/[^a-z0-9-]/g, '') || 'my-nuxt-app'
|
|
54
|
+
|
|
55
|
+
// --- Helpers ---
|
|
56
|
+
const log = {
|
|
57
|
+
info: (msg) => console.log(`\x1b[36m${msg}\x1b[0m`),
|
|
58
|
+
success: (msg) => console.log(`\x1b[32m${msg}\x1b[0m`),
|
|
59
|
+
warn: (msg) => console.log(`\x1b[33m${msg}\x1b[0m`),
|
|
60
|
+
error: (msg) => console.error(`\x1b[31m${msg}\x1b[0m`),
|
|
61
|
+
step: (msg) => console.log(`\n\x1b[35m[${msg}]\x1b[0m`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ensureDir = (dirPath) => {
|
|
65
|
+
const fullPath = path.join(targetDir, dirPath)
|
|
66
|
+
if (!fs.existsSync(fullPath)) {
|
|
67
|
+
fs.mkdirSync(fullPath, { recursive: true })
|
|
68
|
+
log.info(` [建立] 目錄: ${dirPath}`)
|
|
69
|
+
} else {
|
|
70
|
+
log.info(` [略過] 目錄已存在: ${dirPath}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const writeFile = (filePath, content, overwrite = true) => {
|
|
75
|
+
const fullPath = path.join(targetDir, filePath)
|
|
76
|
+
const dir = path.dirname(fullPath)
|
|
77
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
78
|
+
|
|
79
|
+
if (fs.existsSync(fullPath) && !overwrite) {
|
|
80
|
+
log.info(` [略過] 檔案已存在: ${filePath}`)
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(fullPath, content)
|
|
85
|
+
log.info(` [產生] 檔案: ${filePath}`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --- Main Execution ---
|
|
89
|
+
|
|
90
|
+
if (isInit) {
|
|
91
|
+
if (fs.existsSync(targetDir)) {
|
|
92
|
+
log.error(`錯誤: 目錄 "${targetPath}" 已經存在。初始化必須使用新目錄。`)
|
|
93
|
+
process.exit(1)
|
|
94
|
+
}
|
|
95
|
+
log.step('Initializing New Project...')
|
|
96
|
+
} else {
|
|
97
|
+
log.step('Syncing Project Architecture...')
|
|
98
|
+
if (!fs.existsSync(path.join(targetDir, 'package.json'))) {
|
|
99
|
+
log.error('錯誤: 找不到 package.json。請在專案根目錄執行 sync 指令。')
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 1. 基本目錄
|
|
105
|
+
const standardDirs = ['pages', 'composables', 'repositories', 'utils', 'assets/images', 'plugins', 'constants', 'configs']
|
|
106
|
+
standardDirs.forEach(ensureDir)
|
|
107
|
+
|
|
108
|
+
// 2. 基礎設定檔 (sync 時若已存在且內容包含繼承語法,則略過或僅更新 schema)
|
|
109
|
+
const extConfigs = [
|
|
110
|
+
{
|
|
111
|
+
name: 'eslint.config.mjs',
|
|
112
|
+
content: "import coreConfig from 'softleader-nuxt-core/core/config/eslint.config.mjs'\n\nexport default [\n ...coreConfig\n]\n"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: '.prettierrc.cjs',
|
|
116
|
+
content: "module.exports = {\n ...require('softleader-nuxt-core/core/config/prettier.json')\n}\n"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: '.commitlintrc.cjs',
|
|
120
|
+
content: "module.exports = require('softleader-nuxt-core/core/config/git/commitlint.config.cjs')\n"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: '.lintstagedrc.js',
|
|
124
|
+
content: "export { default } from 'softleader-nuxt-core/core/config/git/lint-staged.config.js'\n"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: '.cz-config.cjs',
|
|
128
|
+
content: "module.exports = require('softleader-nuxt-core/core/config/git/cz-config.cjs')\n"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
extConfigs.forEach(conf => writeFile(conf.name, conf.content, isInit)) // Init 模式覆寫, Sync 模式保留
|
|
132
|
+
|
|
133
|
+
// 3. Nuxt 配置 (僅在 Init 或檔案不存在時產生)
|
|
134
|
+
const nuxtConfigTs = `// https://nuxt.com/docs/api/configuration/nuxt-config
|
|
135
|
+
export default defineNuxtConfig({
|
|
136
|
+
extends: ['softleader-nuxt-core'],
|
|
137
|
+
compatibilityDate: '2024-04-03',
|
|
138
|
+
devtools: { enabled: true }
|
|
139
|
+
})
|
|
140
|
+
`
|
|
141
|
+
writeFile('nuxt.config.ts', nuxtConfigTs, false)
|
|
142
|
+
|
|
143
|
+
const appVue = `<template>
|
|
144
|
+
<NuxtLayout>
|
|
145
|
+
<NuxtPage />
|
|
146
|
+
</NuxtLayout>
|
|
147
|
+
</template>
|
|
148
|
+
`
|
|
149
|
+
writeFile('app.vue', appVue, false)
|
|
150
|
+
|
|
151
|
+
// 4. 首頁 (Init 模式才產生)
|
|
152
|
+
if (isInit) {
|
|
153
|
+
const indexVue = `<script setup lang="ts">
|
|
154
|
+
const { formatDateTime } = useDateTime()
|
|
155
|
+
const notify = useNotify()
|
|
156
|
+
const now = ref(formatDateTime(new Date()))
|
|
157
|
+
const handleClick = () => notify.success('連動成功!')
|
|
158
|
+
</script>
|
|
159
|
+
|
|
160
|
+
<template>
|
|
161
|
+
<div class="pa-10">
|
|
162
|
+
<IAlert type="info" title="專案已就緒" text="這是繼承自 softleader-nuxt-core 的新專案。" class="mb-6" />
|
|
163
|
+
<ICard title="功能示範">
|
|
164
|
+
<div class="d-flex align-center gap-4">
|
|
165
|
+
<div>時間: {{ now }}</div>
|
|
166
|
+
<IButton variant="primary" @click="handleClick">測試通知</IButton>
|
|
167
|
+
</div>
|
|
168
|
+
</ICard>
|
|
169
|
+
</div>
|
|
170
|
+
</template>
|
|
171
|
+
`
|
|
172
|
+
writeFile('pages/index.vue', indexVue, false)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 5. 範例檔案 (若不存在則補齊)
|
|
176
|
+
const useAppInfoTs = `export const useAppInfo = () => {
|
|
177
|
+
const config = useAppConfig()
|
|
178
|
+
return { getProjectTitle: () => config.title || '未命名專案' }
|
|
179
|
+
}
|
|
180
|
+
`
|
|
181
|
+
writeFile('composables/useAppInfo.ts', useAppInfoTs, false)
|
|
182
|
+
|
|
183
|
+
const exampleRepoTs = `export const exampleRepository = () => {
|
|
184
|
+
const api = useApi()
|
|
185
|
+
return {
|
|
186
|
+
getUsers: () => api.get('/users'),
|
|
187
|
+
createUser: (data: any) => api.post('/users', data)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
`
|
|
191
|
+
writeFile('repositories/exampleRepository.ts', exampleRepoTs, false)
|
|
192
|
+
|
|
193
|
+
// 6. Config Blueprint
|
|
194
|
+
const defaultConfig = `{
|
|
195
|
+
"$schema": "./schema.json",
|
|
196
|
+
"//": "Softleader Nuxt Core 專案設定檔",
|
|
197
|
+
"branding": { "name": "${projectName}", "shortName": "${packageName.toUpperCase()}" },
|
|
198
|
+
"meta": { "title": "${projectName}", "description": "企業級 Nuxt 3 核心架構", "lang": "zh-TW" },
|
|
199
|
+
"layout": { "menuStyle": "sidebar", "sidebar": { "width": 260, "title": "管理系統" } },
|
|
200
|
+
"theme": { "primaryColor": "#2563eb", "borderRadius": 12 },
|
|
201
|
+
"network": { "apiBaseUrl": "/api/v1" },
|
|
202
|
+
"features": { "enableAuth": true, "enableLog": true }
|
|
203
|
+
}`
|
|
204
|
+
writeFile('configs/default.json', defaultConfig, false)
|
|
205
|
+
|
|
206
|
+
// 7. VS Code & Schema
|
|
207
|
+
writeFile('.vscode/settings.json', JSON.stringify({ 'json.schemas': [{ fileMatch: ['/configs/*.json'], url: './configs/schema.json' }] }, null, 2), true)
|
|
208
|
+
const schemaPath = path.resolve(__dirname, '../schemas/config.schema.json')
|
|
209
|
+
if (fs.existsSync(schemaPath)) {
|
|
210
|
+
fs.copyFileSync(schemaPath, path.join(targetDir, 'configs/schema.json'))
|
|
211
|
+
log.info(' [更新] Config Schema 指引')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 8. README (僅在 Init 或檔案不存在時產生)
|
|
215
|
+
const readmeMd = `# ${projectName}\n\n基於 \`softleader-nuxt-core\` 核心架構。\n\n## 🚀 快速開始\n1. \`npm install\`\n2. \`npm run dev\`\n`
|
|
216
|
+
writeFile('README.md', readmeMd, false)
|
|
217
|
+
|
|
218
|
+
// 9. Git & Husky (Init 模式或缺件時補齊)
|
|
219
|
+
if (isInit) {
|
|
220
|
+
try {
|
|
221
|
+
execSync('git init', { cwd: targetDir, stdio: 'ignore' })
|
|
222
|
+
log.info(' [Git] 初始化儲存庫')
|
|
223
|
+
} catch (e) {}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const huskyDir = path.join(targetDir, '.husky')
|
|
227
|
+
if (!fs.existsSync(huskyDir)) {
|
|
228
|
+
fs.mkdirSync(huskyDir, { recursive: true })
|
|
229
|
+
const templateHuskyDir = path.resolve(__dirname, '../core/templates/husky')
|
|
230
|
+
const hooks = ['pre-commit', 'commit-msg']
|
|
231
|
+
hooks.forEach(hook => {
|
|
232
|
+
const templatePath = path.join(templateHuskyDir, hook)
|
|
233
|
+
if (fs.existsSync(templatePath)) {
|
|
234
|
+
fs.copyFileSync(templatePath, path.join(huskyDir, hook))
|
|
235
|
+
try { fs.chmodSync(path.join(huskyDir, hook), '755') } catch (e) {}
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
log.info(' [Husky] 補齊 Git Hooks 設定')
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
log.success(`\n[完成] ${isInit ? '專案建立成功' : '架構同步成功'}!`)
|
|
242
|
+
if (isInit) {
|
|
243
|
+
console.log(`\n請執行以下指令開始開發:\n cd ${targetPath}\n npm install\n npm run dev\n`)
|
|
244
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./core/config/git/commitlint.config.cjs')
|