@aibai/create-myvue 0.0.1 → 0.0.3
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 +11 -11
- package/bin/cli.js +46 -766
- package/create-my-vue.config.example.js +1 -1
- package/package.json +1 -1
- package/templates/plugins/axios/files/README.md +361 -0
- package/templates/plugins/axios/files/axiosConfig.ts +27 -0
- package/templates/plugins/axios/files/axiosInstance.ts +74 -0
- package/templates/plugins/axios/files/errorHandler.ts +109 -0
- package/templates/plugins/axios/files/imageUtils.ts +62 -0
- package/templates/plugins/axios/files/index.ts +16 -0
- package/templates/plugins/unocss/files/main.js +1 -0
- package/templates/plugins/unocss/import.js +1 -0
- package/templates/plugins/unocss/plugin.js +1 -0
- package/templates/plugins/unocss/uno.config.ts +16 -0
- package/templates/plugins/unplugin-auto-import/import.js +1 -0
- package/templates/plugins/unplugin-auto-import/plugin.js +18 -0
- package/templates/plugins/unplugin-vue-components/import.js +1 -0
- package/templates/plugins/unplugin-vue-components/plugin.js +6 -0
- package/templates/plugins/vite-plugin-svg-icons/files/Icon.vue +27 -0
- package/templates/plugins/vite-plugin-svg-icons/files/main.js +1 -0
- package/templates/plugins/vite-plugin-svg-icons/import.js +1 -0
- package/templates/plugins/vite-plugin-svg-icons/plugin.js +6 -0
package/bin/cli.js
CHANGED
|
@@ -4,6 +4,17 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const inquirer = require('inquirer');
|
|
6
6
|
|
|
7
|
+
// 读取模板文件内容
|
|
8
|
+
function readTemplateFile(templatePath) {
|
|
9
|
+
try {
|
|
10
|
+
const fullPath = path.join(__dirname, '../templates', templatePath);
|
|
11
|
+
return fs.readFileSync(fullPath, 'utf8').trim();
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error(`Error reading template file ${templatePath}: ${error.message}`);
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
// 插件配置
|
|
8
19
|
const plugins = [
|
|
9
20
|
{
|
|
@@ -12,27 +23,8 @@ const plugins = [
|
|
|
12
23
|
dependencies: ['unplugin-auto-import'],
|
|
13
24
|
config: {
|
|
14
25
|
vite: {
|
|
15
|
-
imports: [
|
|
16
|
-
plugins: [
|
|
17
|
-
`AutoImport({
|
|
18
|
-
include: [
|
|
19
|
-
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
|
|
20
|
-
/\.vue$/,
|
|
21
|
-
/\.vue\?vue/ // .vue
|
|
22
|
-
],
|
|
23
|
-
imports: [
|
|
24
|
-
'vue',
|
|
25
|
-
'vue-router',
|
|
26
|
-
'pinia'
|
|
27
|
-
],
|
|
28
|
-
dts: 'src/types/auto-import.d.ts',
|
|
29
|
-
eslintrc: {
|
|
30
|
-
enabled: true,
|
|
31
|
-
filepath: './.eslintrc-auto-import.json',
|
|
32
|
-
globalsPropValue: true,
|
|
33
|
-
},
|
|
34
|
-
}),`
|
|
35
|
-
]
|
|
26
|
+
imports: [readTemplateFile('plugins/unplugin-auto-import/import.js')],
|
|
27
|
+
plugins: [readTemplateFile('plugins/unplugin-auto-import/plugin.js')]
|
|
36
28
|
}
|
|
37
29
|
}
|
|
38
30
|
},
|
|
@@ -42,15 +34,8 @@ const plugins = [
|
|
|
42
34
|
dependencies: ['unplugin-vue-components'],
|
|
43
35
|
config: {
|
|
44
36
|
vite: {
|
|
45
|
-
imports: [
|
|
46
|
-
plugins: [
|
|
47
|
-
`Components({
|
|
48
|
-
dirs: ['src/components'],
|
|
49
|
-
extensions: ['vue'],
|
|
50
|
-
dts: 'src/types/auto-components.d.ts',
|
|
51
|
-
resolvers: []
|
|
52
|
-
}),`
|
|
53
|
-
]
|
|
37
|
+
imports: [readTemplateFile('plugins/unplugin-vue-components/import.js')],
|
|
38
|
+
plugins: [readTemplateFile('plugins/unplugin-vue-components/plugin.js')]
|
|
54
39
|
}
|
|
55
40
|
}
|
|
56
41
|
},
|
|
@@ -60,36 +45,17 @@ const plugins = [
|
|
|
60
45
|
dependencies: ['unocss'],
|
|
61
46
|
config: {
|
|
62
47
|
vite: {
|
|
63
|
-
imports: [
|
|
64
|
-
plugins: [
|
|
65
|
-
`UnoCSS()`
|
|
66
|
-
]
|
|
48
|
+
imports: [readTemplateFile('plugins/unocss/import.js')],
|
|
49
|
+
plugins: [readTemplateFile('plugins/unocss/plugin.js')]
|
|
67
50
|
},
|
|
68
51
|
files: [
|
|
69
52
|
{
|
|
70
53
|
path: 'uno.config.ts',
|
|
71
|
-
content:
|
|
72
|
-
import { defineConfig, transformerDirectives, transformerVariantGroup } from 'unocss'
|
|
73
|
-
|
|
74
|
-
export default defineConfig({theme: {
|
|
75
|
-
colors: {
|
|
76
|
-
primary: 'var(--primary-color)',
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
shortcuts: {
|
|
80
|
-
'content': 'relative max-w-[1024px] mx-auto',
|
|
81
|
-
'btn': '',
|
|
82
|
-
},
|
|
83
|
-
transformers: [
|
|
84
|
-
transformerDirectives(),
|
|
85
|
-
transformerVariantGroup()
|
|
86
|
-
]
|
|
87
|
-
})
|
|
88
|
-
`
|
|
54
|
+
content: readTemplateFile('plugins/unocss/uno.config.ts')
|
|
89
55
|
},
|
|
90
56
|
{
|
|
91
57
|
path: 'src/main.js',
|
|
92
|
-
content:
|
|
58
|
+
content: readTemplateFile('plugins/unocss/files/main.js') + '\n',
|
|
93
59
|
append: true
|
|
94
60
|
}
|
|
95
61
|
]
|
|
@@ -101,52 +67,17 @@ const plugins = [
|
|
|
101
67
|
dependencies: ['vite-plugin-svg-icons'],
|
|
102
68
|
config: {
|
|
103
69
|
vite: {
|
|
104
|
-
imports: [
|
|
105
|
-
plugins: [
|
|
106
|
-
`createSvgIconsPlugin({
|
|
107
|
-
// 指定需要缓存的图标文件夹
|
|
108
|
-
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
|
|
109
|
-
// 指定symbolId格式
|
|
110
|
-
symbolId: 'icon-[dir]-[name]',
|
|
111
|
-
}),`
|
|
112
|
-
]
|
|
70
|
+
imports: [readTemplateFile('plugins/vite-plugin-svg-icons/import.js')],
|
|
71
|
+
plugins: [readTemplateFile('plugins/vite-plugin-svg-icons/plugin.js')]
|
|
113
72
|
},
|
|
114
73
|
files: [
|
|
115
74
|
{
|
|
116
75
|
path: 'src/components/Icon.vue',
|
|
117
|
-
content:
|
|
118
|
-
<script setup lang="ts">
|
|
119
|
-
import { computed } from 'vue';
|
|
120
|
-
|
|
121
|
-
const props = defineProps({
|
|
122
|
-
prefix: { type: String, default: 'icon', },
|
|
123
|
-
iconName: { type: String, required: true, },
|
|
124
|
-
color: { type: String, default: '' }
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
const symbolId = computed(() => '#' + props.prefix + '-' + props.iconName);
|
|
128
|
-
</script>
|
|
129
|
-
|
|
130
|
-
<template>
|
|
131
|
-
<svg aria-hidden="true" class="svg-icon">
|
|
132
|
-
<use :xlink:href="symbolId" :fill="color" />
|
|
133
|
-
</svg>
|
|
134
|
-
</template>
|
|
135
|
-
|
|
136
|
-
<style>
|
|
137
|
-
.svg-icon {
|
|
138
|
-
width: 1em;
|
|
139
|
-
height: 1em;
|
|
140
|
-
vertical-align: -0.15em;
|
|
141
|
-
overflow: hidden;
|
|
142
|
-
fill: currentColor;
|
|
143
|
-
}
|
|
144
|
-
</style>
|
|
145
|
-
`
|
|
76
|
+
content: readTemplateFile('plugins/vite-plugin-svg-icons/files/Icon.vue')
|
|
146
77
|
},
|
|
147
78
|
{
|
|
148
79
|
path: 'src/main.js',
|
|
149
|
-
content:
|
|
80
|
+
content: readTemplateFile('plugins/vite-plugin-svg-icons/files/main.js') + '\n',
|
|
150
81
|
append: true
|
|
151
82
|
}
|
|
152
83
|
]
|
|
@@ -160,673 +91,27 @@ const plugins = [
|
|
|
160
91
|
files: [
|
|
161
92
|
{
|
|
162
93
|
path: 'src/utils/axiosConfig.ts',
|
|
163
|
-
content:
|
|
164
|
-
// axios 配置文件
|
|
165
|
-
// 定义全局axios配置,包括基础URL、超时时间、响应格式验证等
|
|
166
|
-
|
|
167
|
-
// API基础URL,使用相对路径以便通过Vite代理转发
|
|
168
|
-
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://110.41.59.11/api';
|
|
169
|
-
// 注意:请勿使用绝对URL,否则会绕过Vite代理
|
|
170
|
-
|
|
171
|
-
// 请求超时时间(毫秒)
|
|
172
|
-
export const REQUEST_TIMEOUT = 40000;
|
|
173
|
-
|
|
174
|
-
// 响应数据格式验证
|
|
175
|
-
export const validateResponseFormat = (data: unknown): boolean => {
|
|
176
|
-
return typeof data === 'object' && data !== null;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// 错误消息映射
|
|
180
|
-
export const errorMessageMap: Record<number, string> = {
|
|
181
|
-
400: '请求参数错误',
|
|
182
|
-
401: '未授权,请重新登录',
|
|
183
|
-
403: '拒绝访问',
|
|
184
|
-
404: '请求的资源不存在',
|
|
185
|
-
405: '请求方法不允许',
|
|
186
|
-
500: '服务器内部错误',
|
|
187
|
-
502: '网关错误',
|
|
188
|
-
503: '服务不可用',
|
|
189
|
-
504: '网关超时',
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
`
|
|
94
|
+
content: readTemplateFile('plugins/axios/files/axiosConfig.ts')
|
|
193
95
|
},
|
|
194
96
|
{
|
|
195
97
|
path: 'src/utils/axiosInstance.ts',
|
|
196
|
-
content:
|
|
197
|
-
// axios 实例封装
|
|
198
|
-
import axios, { type AxiosRequestConfig, type AxiosError } from 'axios';
|
|
199
|
-
import { API_BASE_URL, REQUEST_TIMEOUT } from './axiosConfig';
|
|
200
|
-
import { handleAxiosError, isBusinessError, formatBusinessError } from './errorHandler';
|
|
201
|
-
|
|
202
|
-
interface HttpResponse<T = unknown> {
|
|
203
|
-
code: number;
|
|
204
|
-
msg: string;
|
|
205
|
-
data: T;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// 创建axios实例
|
|
209
|
-
const http = axios.create({
|
|
210
|
-
baseURL: API_BASE_URL,
|
|
211
|
-
timeout: REQUEST_TIMEOUT,
|
|
212
|
-
headers: {
|
|
213
|
-
'Content-Type': 'application/json;charset=UTF-8',
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// 请求拦截器
|
|
218
|
-
http.interceptors.request.use(
|
|
219
|
-
(config) => {
|
|
220
|
-
config.headers = config.headers || {};
|
|
221
|
-
// 从本地存储获取token
|
|
222
|
-
const token = localStorage.getItem('APP_TOKEN');
|
|
223
|
-
// 如果存在token,则添加到请求头
|
|
224
|
-
if (token) {
|
|
225
|
-
config.headers = config.headers || {};
|
|
226
|
-
config.headers['x-token'] = token;
|
|
227
|
-
}
|
|
228
|
-
return config;
|
|
229
|
-
},
|
|
230
|
-
(error) => {
|
|
231
|
-
// 请求错误处理
|
|
232
|
-
console.error('请求配置错误:', error);
|
|
233
|
-
return Promise.reject(error);
|
|
234
|
-
}
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
// 响应拦截器
|
|
238
|
-
http.interceptors.response.use(
|
|
239
|
-
(response) => {
|
|
240
|
-
const data = response.data;
|
|
241
|
-
|
|
242
|
-
// 检查是否为业务错误
|
|
243
|
-
if (isBusinessError(data)) {
|
|
244
|
-
const businessError = formatBusinessError(data);
|
|
245
|
-
// globalErrorHandler(businessError);
|
|
246
|
-
return Promise.reject(businessError);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// 直接返回响应数据
|
|
250
|
-
return data.data;
|
|
251
|
-
},
|
|
252
|
-
(error: AxiosError) => {
|
|
253
|
-
// 处理HTTP错误
|
|
254
|
-
const apiError = handleAxiosError(error);
|
|
255
|
-
// globalErrorHandler(apiError);
|
|
256
|
-
return Promise.reject(apiError);
|
|
257
|
-
}
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
// 导出http实例
|
|
261
|
-
export default http;
|
|
262
|
-
|
|
263
|
-
// 导出常用的请求方法
|
|
264
|
-
export const request = {
|
|
265
|
-
// 使用类型断言来匹配响应拦截器实际返回的data.data
|
|
266
|
-
get: <T = unknown>(url: string, config?: AxiosRequestConfig) => http.get<HttpResponse<T>>(url, config) as unknown as Promise<T>,
|
|
267
|
-
post: <T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig) => http.post<HttpResponse<T>>(url, data, config) as unknown as Promise<T>,
|
|
268
|
-
// 导出axios引用,用于取消请求等高级功能
|
|
269
|
-
axios: axios
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
`
|
|
98
|
+
content: readTemplateFile('plugins/axios/files/axiosInstance.ts')
|
|
273
99
|
},
|
|
274
100
|
{
|
|
275
101
|
path: 'src/utils/errorHandler.ts',
|
|
276
|
-
content:
|
|
277
|
-
// 错误处理工具
|
|
278
|
-
import type { AxiosError } from 'axios';
|
|
279
|
-
import { errorMessageMap } from './axiosConfig';
|
|
280
|
-
|
|
281
|
-
// 错误类型接口
|
|
282
|
-
export interface ApiError extends Error {
|
|
283
|
-
code?: number;
|
|
284
|
-
msg?: string;
|
|
285
|
-
data?: unknown;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* 创建API错误对象
|
|
290
|
-
* @param message 错误消息
|
|
291
|
-
* @param status 状态码
|
|
292
|
-
* @param code 错误代码
|
|
293
|
-
* @param data 错误数据
|
|
294
|
-
* @returns ApiError对象
|
|
295
|
-
*/
|
|
296
|
-
export function createApiError<T>(message: string, code?: number, data?: T): ApiError {
|
|
297
|
-
const error = new Error(message) as ApiError;
|
|
298
|
-
error.msg = message;
|
|
299
|
-
error.code = code;
|
|
300
|
-
error.data = data;
|
|
301
|
-
return error;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* 处理axios错误
|
|
306
|
-
* @param error axios错误对象
|
|
307
|
-
* @returns 标准化的ApiError
|
|
308
|
-
*/
|
|
309
|
-
export function handleAxiosError<T>(error: AxiosError): ApiError {
|
|
310
|
-
// 网络错误
|
|
311
|
-
if (!error.response) {
|
|
312
|
-
return createApiError('网络错误,请检查您的网络连接', -1);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const { response } = error;
|
|
316
|
-
const status = response.status;
|
|
317
|
-
|
|
318
|
-
// 从响应数据中提取错误信息
|
|
319
|
-
const responseData = response.data;
|
|
320
|
-
// 安全地访问响应数据
|
|
321
|
-
const data = responseData as Record<string, T>;
|
|
322
|
-
const errorMessage = data?.msg as string ||
|
|
323
|
-
errorMessageMap[status] ||
|
|
324
|
-
\`请求失败,状态码: ${status}\`;
|
|
325
|
-
|
|
326
|
-
const errorCode = data?.code as number || status;
|
|
327
|
-
|
|
328
|
-
return createApiError(errorMessage, errorCode, responseData);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* 处理业务错误
|
|
333
|
-
* @param data 响应数据
|
|
334
|
-
* @returns 是否为业务错误
|
|
335
|
-
*/
|
|
336
|
-
export function isBusinessError(data: unknown): boolean {
|
|
337
|
-
// 这里根据后端返回的业务错误格式进行判断
|
|
338
|
-
return data != null && typeof data === 'object' && 'code' in data && data.code !== 0;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* 格式化业务错误
|
|
343
|
-
* @param data 响应数据
|
|
344
|
-
* @returns ApiError对象
|
|
345
|
-
*/
|
|
346
|
-
export function formatBusinessError<T>(data: T): ApiError {
|
|
347
|
-
const businessData = data as Record<string, T>;
|
|
348
|
-
return createApiError(
|
|
349
|
-
(businessData?.msg as string) || '业务处理失败',
|
|
350
|
-
(businessData?.code as number) || 400,
|
|
351
|
-
data
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* 全局错误处理器
|
|
357
|
-
* 可以在这里添加错误日志记录、上报等功能
|
|
358
|
-
* @param error 错误对象
|
|
359
|
-
*/
|
|
360
|
-
export function globalErrorHandler(error: Error | ApiError): void {
|
|
361
|
-
console.error('Global Error:', error);
|
|
362
|
-
|
|
363
|
-
// 可以在这里添加错误上报逻辑
|
|
364
|
-
// 例如:上报到Sentry等错误监控服务
|
|
365
|
-
// reportErrorToSentry(error);
|
|
366
|
-
|
|
367
|
-
// 根据错误类型进行不同处理
|
|
368
|
-
// if ('code' in error && error.code === 401) {
|
|
369
|
-
// // 处理未授权错误,如跳转到登录页
|
|
370
|
-
// handleUnauthorizedError();
|
|
371
|
-
// }
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* 处理未授权错误
|
|
376
|
-
*/
|
|
377
|
-
// function handleUnauthorizedError(): void {
|
|
378
|
-
// // 清除本地存储的token
|
|
379
|
-
// localStorage.removeItem('token');
|
|
380
|
-
|
|
381
|
-
// // 可以在这里添加跳转到登录页的逻辑
|
|
382
|
-
// // 注意:由于这是工具函数,避免直接操作Vue Router
|
|
383
|
-
// // 实际使用时,应该在组件或业务逻辑中处理跳转
|
|
384
|
-
// console.warn('用户未授权,请重新登录');
|
|
385
|
-
// }
|
|
386
|
-
|
|
387
|
-
`
|
|
102
|
+
content: readTemplateFile('plugins/axios/files/errorHandler.ts')
|
|
388
103
|
},
|
|
389
104
|
{
|
|
390
105
|
path: 'src/utils/imageUtils.ts',
|
|
391
|
-
content:
|
|
392
|
-
// 图片地址处理工具函数
|
|
393
|
-
|
|
394
|
-
// 导出默认的图片基础URL配置(可以从环境变量读取)
|
|
395
|
-
export const DEFAULT_IMAGE_BASE_URL = import.meta.env.VITE_IMAGE_BASE_URL || '';
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* 图片地址拼接函数
|
|
399
|
-
* @param baseUrl 基础图片域名或路径
|
|
400
|
-
* @param imagePath 图片相对路径
|
|
401
|
-
* @returns 完整的图片URL
|
|
402
|
-
*/
|
|
403
|
-
export const joinImageUrl = (imagePath: string, baseUrl?: string): string => {
|
|
404
|
-
// 处理空值情况
|
|
405
|
-
if (!imagePath) return '';
|
|
406
|
-
|
|
407
|
-
// 如果imagePath已经是完整的URL(包含http或https),直接返回
|
|
408
|
-
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
|
|
409
|
-
return imagePath;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// 如果baseUrl未提供,使用默认值
|
|
413
|
-
const newBaseUrl = baseUrl || DEFAULT_IMAGE_BASE_URL;
|
|
414
|
-
|
|
415
|
-
// 处理baseUrl结尾的斜杠和imagePath开头的斜杠,避免重复
|
|
416
|
-
const normalizedBaseUrl = newBaseUrl.endsWith('/') ? newBaseUrl.slice(0, -1) : newBaseUrl;
|
|
417
|
-
let normalizedImagePath = imagePath.startsWith('/') ? imagePath.slice(1) : imagePath;
|
|
418
|
-
// 判断链接开头是否包含 '/api'前缀,包含则删除
|
|
419
|
-
if (normalizedImagePath.startsWith('api/')) {
|
|
420
|
-
normalizedImagePath = normalizedImagePath.slice(4);
|
|
421
|
-
}
|
|
422
|
-
// 拼接完整URL
|
|
423
|
-
return \`${normalizedBaseUrl}/${normalizedImagePath}\`;
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* 批量拼接图片URL
|
|
428
|
-
* @param baseUrl 基础图片域名或路径
|
|
429
|
-
* @param imagePaths 图片相对路径数组
|
|
430
|
-
* @returns 完整图片URL数组
|
|
431
|
-
*/
|
|
432
|
-
export const joinImageUrls = (imagePaths: string[], baseUrl?: string): string[] => {
|
|
433
|
-
return imagePaths.map(path => joinImageUrl(path, baseUrl));
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Vue组件:图片URL拼接工具
|
|
438
|
-
* 可以在模板中直接使用
|
|
439
|
-
*/
|
|
440
|
-
export const useImageUrl = () => {
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
// 单个图片URL拼接
|
|
444
|
-
getImageUrl: (imagePath: string, customBaseUrl?: string) => {
|
|
445
|
-
return joinImageUrl(imagePath, customBaseUrl);
|
|
446
|
-
},
|
|
447
|
-
|
|
448
|
-
// 批量图片URL拼接
|
|
449
|
-
getImageUrls: (imagePaths: string[], customBaseUrl?: string) => {
|
|
450
|
-
return joinImageUrls(imagePaths, customBaseUrl);
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
};
|
|
454
|
-
`
|
|
106
|
+
content: readTemplateFile('plugins/axios/files/imageUtils.ts')
|
|
455
107
|
},
|
|
456
108
|
{
|
|
457
109
|
path: 'src/utils/README.md',
|
|
458
|
-
content:
|
|
459
|
-
# Axios 二次封装说明
|
|
460
|
-
|
|
461
|
-
## 项目结构
|
|
462
|
-
|
|
463
|
-
\`\`\`
|
|
464
|
-
/src/utils/
|
|
465
|
-
├── axiosConfig.ts # axios 配置文件
|
|
466
|
-
├── axiosInstance.ts # axios 实例封装
|
|
467
|
-
├── errorHandler.ts # 错误处理工具
|
|
468
|
-
├── axiosExample.ts # 使用示例
|
|
469
|
-
└── README.md # 说明文档
|
|
470
|
-
|
|
471
|
-
/src/api/
|
|
472
|
-
├── index.ts # API管理入口
|
|
473
|
-
├── userApi.ts # 用户相关API
|
|
474
|
-
└── commonApi.ts # 通用API
|
|
475
|
-
\`\`\`
|
|
476
|
-
|
|
477
|
-
## 功能特性
|
|
478
|
-
|
|
479
|
-
1. **统一配置管理**:通过\`axiosConfig.ts\`集中管理API基础URL、超时时间等配置
|
|
480
|
-
2. **请求拦截器**:自动添加token认证、时间戳防缓存
|
|
481
|
-
3. **响应拦截器**:统一处理响应数据格式和错误
|
|
482
|
-
4. **错误处理机制**:HTTP错误、业务错误统一处理,支持错误日志上报
|
|
483
|
-
5. **API模块化管理**:按业务模块划分API接口,方便维护
|
|
484
|
-
6. **TypeScript支持**:完整的类型定义,提供良好的开发体验
|
|
485
|
-
7. **文件上传支持**:简化文件上传操作
|
|
486
|
-
8. **取消请求支持**:支持取消正在进行的请求
|
|
487
|
-
|
|
488
|
-
## 安装和配置
|
|
489
|
-
|
|
490
|
-
项目已安装axios依赖,如需手动安装:
|
|
491
|
-
|
|
492
|
-
\`\`\`bash
|
|
493
|
-
pnpm install axios
|
|
494
|
-
\`\`\`
|
|
495
|
-
|
|
496
|
-
### 环境变量配置
|
|
497
|
-
|
|
498
|
-
在项目根目录创建`.env`文件,配置API基础URL:
|
|
499
|
-
|
|
500
|
-
\`\`\`
|
|
501
|
-
VITE_API_BASE_URL=https://your-api-domain.com/api
|
|
502
|
-
\`\`\`
|
|
503
|
-
|
|
504
|
-
## 使用方法
|
|
505
|
-
|
|
506
|
-
### 1. 导入方式
|
|
507
|
-
|
|
508
|
-
\`\`\`typescript
|
|
509
|
-
// 方式1:导入API模块(推荐)
|
|
510
|
-
import { api } from '@/api';
|
|
511
|
-
|
|
512
|
-
// 方式2:直接使用request工具函数
|
|
513
|
-
import { request } from '@/api';
|
|
514
|
-
|
|
515
|
-
// 方式3:导入原始axios实例
|
|
516
|
-
import { http } from '@/api';
|
|
517
|
-
\`\`\`
|
|
518
|
-
|
|
519
|
-
### 2. 基本使用示例
|
|
520
|
-
|
|
521
|
-
#### 用户登录
|
|
522
|
-
|
|
523
|
-
\`\`\`typescript
|
|
524
|
-
async function login() {
|
|
525
|
-
try {
|
|
526
|
-
const result = await api.user.login({
|
|
527
|
-
username: 'admin',
|
|
528
|
-
password: '123456'
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// 保存token
|
|
532
|
-
localStorage.setItem('token', result.token);
|
|
533
|
-
console.log('登录成功');
|
|
534
|
-
} catch (error) {
|
|
535
|
-
console.error('登录失败:', error.message);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
\`\`\`
|
|
539
|
-
|
|
540
|
-
#### 获取用户信息
|
|
541
|
-
|
|
542
|
-
\`\`\`typescript
|
|
543
|
-
async function getUserInfo() {
|
|
544
|
-
try {
|
|
545
|
-
const userInfo = await api.user.getUserInfo();
|
|
546
|
-
console.log('用户信息:', userInfo);
|
|
547
|
-
} catch (error) {
|
|
548
|
-
console.error('获取用户信息失败:', error);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
\`\`\`
|
|
552
|
-
|
|
553
|
-
#### 通用请求方法
|
|
554
|
-
|
|
555
|
-
\`\`\`typescript
|
|
556
|
-
// GET请求
|
|
557
|
-
const data = await request.get('/api/data', {
|
|
558
|
-
params: { id: 123 },
|
|
559
|
-
timeout: 5000
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
// POST请求
|
|
563
|
-
const result = await request.post('/api/data', {
|
|
564
|
-
name: 'test',
|
|
565
|
-
value: 'example'
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
// PUT请求
|
|
569
|
-
await request.put('/api/data/123', { name: 'updated' });
|
|
570
|
-
|
|
571
|
-
// DELETE请求
|
|
572
|
-
await request.delete('/api/data/123');
|
|
573
|
-
\`\`\`
|
|
574
|
-
|
|
575
|
-
### 3. 文件上传示例
|
|
576
|
-
|
|
577
|
-
\`\`\`typescript
|
|
578
|
-
async function uploadAvatar(file: File) {
|
|
579
|
-
try {
|
|
580
|
-
const result = await api.common.uploadFile(file, 'image');
|
|
581
|
-
console.log('上传成功,文件URL:', result.url);
|
|
582
|
-
return result.url;
|
|
583
|
-
} catch (error) {
|
|
584
|
-
console.error('上传失败:', error);
|
|
585
|
-
return null;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
\`\`\`
|
|
589
|
-
|
|
590
|
-
### 4. 错误处理
|
|
591
|
-
|
|
592
|
-
\`\`\`typescript
|
|
593
|
-
import { api, ApiError } from '@/api';
|
|
594
|
-
|
|
595
|
-
async function handleRequest() {
|
|
596
|
-
try {
|
|
597
|
-
await api.user.getUserInfo();
|
|
598
|
-
} catch (error) {
|
|
599
|
-
if (error instanceof ApiError) {
|
|
600
|
-
// 根据错误状态码处理
|
|
601
|
-
if (error.status === 401) {
|
|
602
|
-
// 未授权,跳转到登录页
|
|
603
|
-
console.log('请重新登录');
|
|
604
|
-
} else if (error.status === 403) {
|
|
605
|
-
console.log('没有权限访问');
|
|
606
|
-
} else {
|
|
607
|
-
console.log('其他错误:', error.message);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// 根据错误代码处理
|
|
611
|
-
if (error.code === 'USER_NOT_FOUND') {
|
|
612
|
-
console.log('用户不存在');
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
\`\`\`
|
|
618
|
-
|
|
619
|
-
### 5. 并发请求
|
|
620
|
-
|
|
621
|
-
\`\`\`typescript
|
|
622
|
-
async function fetchMultipleData() {
|
|
623
|
-
try {
|
|
624
|
-
const [userInfo, systemStatus] = await Promise.all([
|
|
625
|
-
api.user.getUserInfo(),
|
|
626
|
-
api.common.getSystemStatus()
|
|
627
|
-
]);
|
|
628
|
-
|
|
629
|
-
console.log('用户信息:', userInfo);
|
|
630
|
-
console.log('系统状态:', systemStatus);
|
|
631
|
-
} catch (error) {
|
|
632
|
-
console.error('请求失败:', error);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
\`\`\`
|
|
636
|
-
|
|
637
|
-
### 6. 取消请求
|
|
638
|
-
|
|
639
|
-
\`\`\`typescript
|
|
640
|
-
import { request } from '@/api';
|
|
641
|
-
|
|
642
|
-
// 创建取消令牌
|
|
643
|
-
const CancelToken = request.axios.CancelToken;
|
|
644
|
-
const source = CancelToken.source();
|
|
645
|
-
|
|
646
|
-
// 发送请求
|
|
647
|
-
request.get('/api/long-running', {
|
|
648
|
-
cancelToken: source.token
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
// 取消请求
|
|
652
|
-
function cancelRequest() {
|
|
653
|
-
source.cancel('用户取消了请求');
|
|
654
|
-
}
|
|
655
|
-
\`\`\`
|
|
656
|
-
|
|
657
|
-
## Vue 3组件中使用示例
|
|
658
|
-
|
|
659
|
-
\`\`\`vue
|
|
660
|
-
<script setup lang="ts">
|
|
661
|
-
import { ref, onMounted } from 'vue';
|
|
662
|
-
import { api } from '@/api';
|
|
663
|
-
|
|
664
|
-
const loading = ref(false);
|
|
665
|
-
const userList = ref([]);
|
|
666
|
-
const errorMsg = ref('');
|
|
667
|
-
|
|
668
|
-
const fetchUsers = async (page = 1) => {
|
|
669
|
-
loading.value = true;
|
|
670
|
-
errorMsg.value = '';
|
|
671
|
-
|
|
672
|
-
try {
|
|
673
|
-
const result = await api.user.getUserList({ page, pageSize: 10 });
|
|
674
|
-
userList.value = result.list;
|
|
675
|
-
} catch (error) {
|
|
676
|
-
errorMsg.value = error.message || '获取用户列表失败';
|
|
677
|
-
} finally {
|
|
678
|
-
loading.value = false;
|
|
679
|
-
}
|
|
680
|
-
};
|
|
681
|
-
|
|
682
|
-
onMounted(() => {
|
|
683
|
-
fetchUsers();
|
|
684
|
-
});
|
|
685
|
-
</script>
|
|
686
|
-
|
|
687
|
-
<template>
|
|
688
|
-
<div class="user-list">
|
|
689
|
-
<div v-if="loading">加载中...</div>
|
|
690
|
-
<div v-else-if="errorMsg" class="error">{{ errorMsg }}</div>
|
|
691
|
-
<div v-else>
|
|
692
|
-
<div v-for="user in userList" :key="user.id" class="user-item">
|
|
693
|
-
{{ user.name }} - {{ user.email }}
|
|
694
|
-
</div>
|
|
695
|
-
</div>
|
|
696
|
-
</div>
|
|
697
|
-
</template>
|
|
698
|
-
\`\`\`
|
|
699
|
-
|
|
700
|
-
## 扩展说明
|
|
701
|
-
|
|
702
|
-
### 1. 添加新的API模块
|
|
703
|
-
|
|
704
|
-
1. 在\`/src/api/\`目录下创建新的API文件,例如\`productApi.ts\`
|
|
705
|
-
2. 在文件中定义API接口和相关类型
|
|
706
|
-
3. 在`/ src / api / index.ts`中导入并导出新模块
|
|
707
|
-
|
|
708
|
-
### 2. 自定义配置
|
|
709
|
-
|
|
710
|
-
可以在\`axiosConfig.ts\`中修改配置:
|
|
711
|
-
- \`API_BASE_URL\`:修改API基础URL
|
|
712
|
-
- \`REQUEST_TIMEOUT\`:调整请求超时时间
|
|
713
|
-
- \`errorMessageMap\`:自定义错误消息 (可选)
|
|
714
|
-
|
|
715
|
-
### 3. 全局错误处理
|
|
716
|
-
|
|
717
|
-
可以在\`errorHandler.ts\`的\`globalErrorHandler\`函数中添加:
|
|
718
|
-
- 错误日志上报
|
|
719
|
-
- 统一错误提示
|
|
720
|
-
- 根据错误类型执行不同操作
|
|
721
|
-
|
|
722
|
-
## 注意事项
|
|
723
|
-
|
|
724
|
-
1. 所有API调用都应该使用\`try/catch\`进行错误处理
|
|
725
|
-
2. 登录成功后,token会自动保存到localStorage,并在后续请求中自动添加
|
|
726
|
-
3. 未授权错误(401)会自动清除token,可以在组件中监听此错误进行页面跳转
|
|
727
|
-
4. 文件上传时,需要使用FormData格式
|
|
728
|
-
5. 对于大型项目,可以根据业务模块创建更多API文件
|
|
729
|
-
|
|
730
|
-
**************************************************
|
|
731
|
-
# 工具函数库
|
|
732
|
-
|
|
733
|
-
## 图片地址处理工具
|
|
734
|
-
|
|
735
|
-
### 主要功能
|
|
736
|
-
- 拼接图片基础URL和相对路径
|
|
737
|
-
- 智能处理URL中的斜杠,避免重复
|
|
738
|
-
- 识别并保留完整URL(以http或https开头的路径)
|
|
739
|
-
- 支持批量图片URL处理
|
|
740
|
-
- 提供Vue组件和组合式API使用方式
|
|
741
|
-
|
|
742
|
-
### 使用方法
|
|
743
|
-
|
|
744
|
-
#### 1. 直接使用工具函数
|
|
745
|
-
|
|
746
|
-
\`\`\`typescript
|
|
747
|
-
import { joinImageUrl, joinImageUrls } from '@/utils/imageUtils';
|
|
748
|
-
|
|
749
|
-
// 单个图片URL拼接
|
|
750
|
-
const imageUrl = joinImageUrl('https://cdn.example.com', 'images/product.jpg');
|
|
751
|
-
// 结果: https://cdn.example.com/images/product.jpg
|
|
752
|
-
|
|
753
|
-
// 批量图片URL拼接
|
|
754
|
-
const imageUrls = joinImageUrls([
|
|
755
|
-
'images/1.jpg',
|
|
756
|
-
'images/2.jpg'
|
|
757
|
-
], 'https://cdn.example.com');
|
|
758
|
-
\`\`\`
|
|
759
|
-
|
|
760
|
-
#### 2. 使用组合式API(在Vue组件中)
|
|
761
|
-
|
|
762
|
-
\`\`\`typescript
|
|
763
|
-
import { useImageUrl } from '@/utils/imageUtils';
|
|
764
|
-
|
|
765
|
-
// 创建自定义基础URL的图片工具
|
|
766
|
-
const imageTools = useImageUrl('https://cdn.example.com');
|
|
767
|
-
|
|
768
|
-
// 单个图片URL
|
|
769
|
-
const imageUrl = imageTools.getImageUrl('images/product.jpg');
|
|
770
|
-
|
|
771
|
-
// 或使用自定义基础URL覆盖默认值
|
|
772
|
-
const imageUrlWithCustomBase = imageTools.getImageUrl('images/product.jpg', 'https://custom.example.com');
|
|
773
|
-
\`\`\`
|
|
774
|
-
|
|
775
|
-
#### 3. 使用Vue组件
|
|
776
|
-
|
|
777
|
-
\`\`\`vue
|
|
778
|
-
<template>
|
|
779
|
-
<!-- 简单使用 -->
|
|
780
|
-
<ImageUrl path="images/product.jpg" />
|
|
781
|
-
|
|
782
|
-
<!-- 使用自定义基础URL -->
|
|
783
|
-
<ImageUrl path="images/product.jpg" base-url="https://cdn.example.com" />
|
|
784
|
-
|
|
785
|
-
<!-- 使用插槽自定义渲染 -->
|
|
786
|
-
<ImageUrl path="images/product.jpg" #default="{ url }">
|
|
787
|
-
<img :src="url" alt="产品图片" />
|
|
788
|
-
</ImageUrl>
|
|
789
|
-
</template>
|
|
790
|
-
|
|
791
|
-
\`\`\`
|
|
792
|
-
|
|
793
|
-
### 配置说明
|
|
794
|
-
|
|
795
|
-
可以在环境变量文件(.env)中配置默认图片基础URL:
|
|
796
|
-
|
|
797
|
-
\`\`\`
|
|
798
|
-
VITE_IMAGE_BASE_URL=https://cdn.example.com
|
|
799
|
-
\`\`\`
|
|
800
|
-
|
|
801
|
-
如果未配置,默认值为'/'(网站根目录)。
|
|
802
|
-
|
|
803
|
-
### 注意事项
|
|
804
|
-
- 工具会自动处理URL中的斜杠,避免重复
|
|
805
|
-
- 如果提供的图片路径已经是完整URL,将直接返回该URL
|
|
806
|
-
- 对于空路径,函数会返回空字符串,避免生成无效URL
|
|
807
|
-
`
|
|
110
|
+
content: readTemplateFile('plugins/axios/files/README.md')
|
|
808
111
|
},
|
|
809
112
|
{
|
|
810
113
|
path: 'src/api/index.ts',
|
|
811
|
-
content:
|
|
812
|
-
// API接口管理入口文件
|
|
813
|
-
|
|
814
|
-
// 导出各个模块的API
|
|
815
|
-
import * as yApi from './app/yApi';
|
|
816
|
-
|
|
817
|
-
// 统一导出所有API
|
|
818
|
-
export {
|
|
819
|
-
yApi,
|
|
820
|
-
};
|
|
821
|
-
|
|
822
|
-
// 导出axios实例和请求方法,便于直接使用
|
|
823
|
-
import http, { request } from '../utils/axiosInstance';
|
|
824
|
-
export { http, request };
|
|
825
|
-
|
|
826
|
-
// 导出错误处理相关工具
|
|
827
|
-
export * from '../utils/errorHandler';
|
|
828
|
-
|
|
829
|
-
`
|
|
114
|
+
content: readTemplateFile('plugins/axios/files/index.ts')
|
|
830
115
|
},
|
|
831
116
|
]
|
|
832
117
|
}
|
|
@@ -902,7 +187,7 @@ function executeCommand(command, args, options = {}) {
|
|
|
902
187
|
if (code === 0) {
|
|
903
188
|
resolve();
|
|
904
189
|
} else {
|
|
905
|
-
reject(new Error(
|
|
190
|
+
reject(new Error('Command failed with exit code ' + code));
|
|
906
191
|
}
|
|
907
192
|
});
|
|
908
193
|
});
|
|
@@ -930,7 +215,7 @@ function updateProjectConfig(projectPath, selectedPlugins) {
|
|
|
930
215
|
// 添加导入语句
|
|
931
216
|
if (allImports.length > 0) {
|
|
932
217
|
const importStatements = allImports.join('\n');
|
|
933
|
-
content = content.replace(/import\s+\w+\s+from/g, match =>
|
|
218
|
+
content = content.replace(/import\s+\w+\s+from/g, match => importStatements + '\n' + match);
|
|
934
219
|
}
|
|
935
220
|
|
|
936
221
|
// 添加插件配置
|
|
@@ -938,14 +223,9 @@ function updateProjectConfig(projectPath, selectedPlugins) {
|
|
|
938
223
|
const pluginStatements = allPlugins.join(',\n ');
|
|
939
224
|
content = content.replace(/plugins:\s*\[([\s\S]*?)\]/g, (match, existingPlugins) => {
|
|
940
225
|
if (existingPlugins.trim()) {
|
|
941
|
-
return
|
|
942
|
-
${existingPlugins.trim()},
|
|
943
|
-
${pluginStatements}
|
|
944
|
-
]`;
|
|
226
|
+
return 'plugins: [\n ' + existingPlugins.trim() + ',\n ' + pluginStatements + '\n ]';
|
|
945
227
|
} else {
|
|
946
|
-
return
|
|
947
|
-
${pluginStatements}
|
|
948
|
-
]`;
|
|
228
|
+
return 'plugins: [\n ' + pluginStatements + '\n ]';
|
|
949
229
|
}
|
|
950
230
|
});
|
|
951
231
|
}
|
|
@@ -973,11 +253,11 @@ function updateProjectConfig(projectPath, selectedPlugins) {
|
|
|
973
253
|
// 追加内容
|
|
974
254
|
const existingContent = fs.readFileSync(filePath, 'utf8');
|
|
975
255
|
fs.writeFileSync(filePath, existingContent + fileConfig.content, 'utf8');
|
|
976
|
-
console.log(
|
|
256
|
+
console.log('成功追加到 ' + fileConfig.path);
|
|
977
257
|
} else {
|
|
978
258
|
// 创建或覆盖文件
|
|
979
259
|
fs.writeFileSync(filePath, fileConfig.content, 'utf8');
|
|
980
|
-
console.log(
|
|
260
|
+
console.log('成功创建 ' + fileConfig.path);
|
|
981
261
|
}
|
|
982
262
|
});
|
|
983
263
|
}
|
|
@@ -994,12 +274,12 @@ async function main(projectName, options) {
|
|
|
994
274
|
projectName: projectName || config.projectName,
|
|
995
275
|
...config,
|
|
996
276
|
...options
|
|
997
|
-
}
|
|
277
|
+
}
|
|
998
278
|
|
|
999
279
|
// 构建 pnpm create vue@latest 命令
|
|
1000
280
|
const args = ['create', 'vue@latest', ...parseConfigToArgs(finalConfig)];
|
|
1001
281
|
|
|
1002
|
-
console.log(
|
|
282
|
+
console.log('Running: pnpm ' + args.join(' '));
|
|
1003
283
|
|
|
1004
284
|
// 如果是 dry-run,只显示命令不执行
|
|
1005
285
|
if (finalConfig.dryRun) {
|
|
@@ -1025,22 +305,22 @@ async function main(projectName, options) {
|
|
|
1025
305
|
|
|
1026
306
|
if (!fs.existsSync(composablesPath)) {
|
|
1027
307
|
fs.mkdirSync(composablesPath, { recursive: true });
|
|
1028
|
-
console.log(
|
|
308
|
+
console.log('成功创建 ' + composablesPath);
|
|
1029
309
|
}
|
|
1030
310
|
|
|
1031
311
|
if (!fs.existsSync(utilsPath)) {
|
|
1032
312
|
fs.mkdirSync(utilsPath, { recursive: true });
|
|
1033
|
-
console.log(
|
|
313
|
+
console.log('成功创建 ' + utilsPath);
|
|
1034
314
|
}
|
|
1035
315
|
|
|
1036
316
|
if (!fs.existsSync(iconPath)) {
|
|
1037
317
|
fs.mkdirSync(iconPath, { recursive: true });
|
|
1038
|
-
console.log(
|
|
318
|
+
console.log('成功创建 ' + iconPath);
|
|
1039
319
|
}
|
|
1040
320
|
|
|
1041
321
|
if (!fs.existsSync(apiPath)) {
|
|
1042
322
|
fs.mkdirSync(apiPath, { recursive: true });
|
|
1043
|
-
console.log(
|
|
323
|
+
console.log('成功创建 ' + apiPath);
|
|
1044
324
|
}
|
|
1045
325
|
|
|
1046
326
|
const { selectedPlugins } = await inquirer.prompt([
|
|
@@ -1049,7 +329,7 @@ async function main(projectName, options) {
|
|
|
1049
329
|
name: 'selectedPlugins',
|
|
1050
330
|
message: '您想要添加哪些额外的插件?',
|
|
1051
331
|
choices: plugins.map(plugin => ({
|
|
1052
|
-
name:
|
|
332
|
+
name: plugin.name + ' - ' + plugin.description,
|
|
1053
333
|
value: plugin.name
|
|
1054
334
|
}))
|
|
1055
335
|
}
|
|
@@ -1079,15 +359,15 @@ async function main(projectName, options) {
|
|
|
1079
359
|
}
|
|
1080
360
|
}
|
|
1081
361
|
} catch (error) {
|
|
1082
|
-
console.error(
|
|
362
|
+
console.error('Error: ' + error.message);
|
|
1083
363
|
process.exit(1);
|
|
1084
364
|
}
|
|
1085
365
|
}
|
|
1086
366
|
|
|
1087
367
|
program
|
|
1088
|
-
.name('create-
|
|
368
|
+
.name('create-myvue')
|
|
1089
369
|
.description('pnpm create vue@latest 的二次封装,支持自定义默认选项')
|
|
1090
|
-
.version('
|
|
370
|
+
.version('0.0.1')
|
|
1091
371
|
.argument('[project-name]', '要创建的项目名称')
|
|
1092
372
|
.option('-d, --default', '使用默认选项')
|
|
1093
373
|
.option('--dry-run', '显示将要执行的命令,但不实际运行')
|
|
@@ -1111,4 +391,4 @@ program
|
|
|
1111
391
|
main(projectName, options);
|
|
1112
392
|
});
|
|
1113
393
|
|
|
1114
|
-
program.parse(process.argv);
|
|
394
|
+
program.parse(process.argv);
|