create-vite-vue 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
  import { execSync } from 'child_process'
3
3
  import fs from 'fs'
4
4
  import path from 'path'
@@ -8,204 +8,204 @@ import { fileURLToPath } from 'url'
8
8
  const __filename = fileURLToPath(import.meta.url)
9
9
  const __dirname = path.dirname(__filename)
10
10
 
11
- async function createProject () {
12
- // 1️⃣ 输入项目名
13
- let projectName
14
- while (true) {
15
- const res = await prompts({
16
- type: 'text',
17
- name: 'projectName',
18
- message: '📦 项目名称',
19
- validate: v => v ? true : '项目名不能为空'
20
- })
21
- projectName = res.projectName
22
- if (!projectName) process.exit(1)
23
-
24
- const targetDir = path.resolve(process.cwd(), projectName)
25
- if (fs.existsSync(targetDir)) {
26
- console.log('❌ 目录已存在,请重新输入')
27
- continue
11
+ ; (async () => {
12
+ // 1️⃣ 输入项目名
13
+ let projectName
14
+ while (true) {
15
+ const res = await prompts({
16
+ type: 'text',
17
+ name: 'projectName',
18
+ message: '📦 项目名称',
19
+ validate: v => v ? true : '项目名不能为空'
20
+ })
21
+ projectName = res.projectName
22
+ if (!projectName) process.exit(1)
23
+
24
+ const targetDir = path.resolve(process.cwd(), projectName)
25
+ if (fs.existsSync(targetDir)) {
26
+ console.log('❌ 目录已存在,请重新输入')
27
+ continue
28
+ }
29
+ break
28
30
  }
29
- break
30
- }
31
31
 
32
- const targetDir = path.resolve(process.cwd(), projectName)
32
+ const targetDir = path.resolve(process.cwd(), projectName)
33
33
 
34
- const { language } = await prompts({
35
- type: 'select',
36
- name: 'language',
37
- message: '请选择项目语言',
38
- choices: [
39
- { title: 'JavaScript', value: 'js' },
40
- { title: 'TypeScript', value: 'ts' }
41
- ]
42
- })
34
+ const { language } = await prompts({
35
+ type: 'select',
36
+ name: 'language',
37
+ message: '请选择项目语言',
38
+ choices: [
39
+ { title: 'JavaScript', value: 'js' },
40
+ { title: 'TypeScript', value: 'ts' }
41
+ ]
42
+ })
43
43
 
44
44
 
45
- // 2️⃣ 功能选择
46
- const features = await prompts([
47
- {
45
+ // 2️⃣ 功能选择
46
+ const features = await prompts([
47
+ {
48
+ type: 'select',
49
+ name: 'router',
50
+ message: '是否使用 vue-router?',
51
+ choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
52
+ },
53
+ {
54
+ type: 'select',
55
+ name: 'pinia',
56
+ message: '是否使用 Pinia(含持久化)?',
57
+ choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
58
+ },
59
+ {
60
+ type: 'select',
61
+ name: 'axios',
62
+ message: '是否使用 Axios?',
63
+ choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
64
+ },
65
+ {
66
+ type: 'select',
67
+ name: 'ui',
68
+ message: '请选择 UI 框架',
69
+ choices: [
70
+ { title: 'Element Plus(PC)', value: 'element' },
71
+ { title: 'Vant(Mobile)', value: 'vant' },
72
+ { title: '不使用 UI 框架', value: 'none' }
73
+ ]
74
+ }
75
+ ])
76
+
77
+ // 3️⃣ 是否立即运行 dev
78
+ const { runDev } = await prompts({
48
79
  type: 'select',
49
- name: 'router',
50
- message: '是否使用 vue-router?',
80
+ name: 'runDev',
81
+ message: '是否立即运行 npm run dev?',
51
82
  choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
52
- },
53
- {
54
- type: 'select',
55
- name: 'pinia',
56
- message: '是否使用 Pinia(含持久化)?',
57
- choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
58
- },
59
- {
60
- type: 'select',
61
- name: 'axios',
62
- message: '是否使用 Axios?',
63
- choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
64
- },
65
- {
66
- type: 'select',
67
- name: 'ui',
68
- message: '请选择 UI 框架',
69
- choices: [
70
- { title: 'Element Plus(PC)', value: 'element' },
71
- { title: 'Vant(Mobile)', value: 'vant' },
72
- { title: '不使用 UI 框架', value: 'none' }
73
- ]
74
- }
75
- ])
76
-
77
- // 3️⃣ 是否立即运行 dev
78
- const { runDev } = await prompts({
79
- type: 'select',
80
- name: 'runDev',
81
- message: '是否立即运行 npm run dev?',
82
- choices: [{ title: 'Yes', value: true }, { title: 'No', value: false }]
83
- })
84
-
85
- // 4️⃣ 拷贝 base 模板
86
- const baseTemplate = language === 'ts' ? 'base-ts' : 'base-js'
87
- fs.cpSync(
88
- path.resolve(__dirname, `../template/${baseTemplate}`),
89
- targetDir,
90
- { recursive: true }
91
- )
92
-
93
- // 4️⃣-1️⃣ 替换 index.html 的 title
94
- const indexPath = path.join(targetDir, 'index.html')
95
- if (fs.existsSync(indexPath)) {
96
- const indexContent = fs.readFileSync(indexPath, 'utf-8')
97
- fs.writeFileSync(
98
- indexPath,
99
- indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`)
83
+ })
84
+
85
+ // 4️⃣ 拷贝 base 模板
86
+ const baseTemplate = language === 'ts' ? 'base-ts' : 'base-js'
87
+ fs.cpSync(
88
+ path.resolve(__dirname, `../template/${baseTemplate}`),
89
+ targetDir,
90
+ { recursive: true }
100
91
  )
101
- }
102
-
103
- // 5️⃣ 拷贝可选模板
104
- const copy = name => {
105
- fs.cpSync(path.resolve(__dirname, `../template/${name}`), targetDir, { recursive: true })
106
- }
107
- features.router && copy(language === 'ts' ? 'router-ts' : 'router-js')
108
- features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
109
- features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
110
-
111
- // 6️⃣ 生成 main.js
112
- const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
113
- const mainTplPath = path.join(targetDir, `src/${mainFile}.tpl`)
114
- let main = fs.readFileSync(mainTplPath, 'utf-8')
115
-
116
- const replacements = {
117
- '/* __ROUTER_IMPORT__ */': features.router ? "import router from './router'" : '',
118
- '/* __PINIA_IMPORT__ */': features.pinia
119
- ? "import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'"
120
- : '',
121
- '/* __ELEMENT_IMPORT__ */': features.ui === 'element'
122
- ? `import ElementPlus from 'element-plus'
92
+
93
+ // 4️⃣-1️⃣ 替换 index.html 的 title
94
+ const indexPath = path.join(targetDir, 'index.html')
95
+ if (fs.existsSync(indexPath)) {
96
+ const indexContent = fs.readFileSync(indexPath, 'utf-8')
97
+ fs.writeFileSync(
98
+ indexPath,
99
+ indexContent.replace(/<title>.*<\/title>/, `<title>${projectName}</title>`)
100
+ )
101
+ }
102
+
103
+ // 5️⃣ 拷贝可选模板
104
+ const copy = name => {
105
+ fs.cpSync(path.resolve(__dirname, `../template/${name}`), targetDir, { recursive: true })
106
+ }
107
+ features.router && copy(language === 'ts' ? 'router-ts' : 'router-js')
108
+ features.pinia && copy(language === 'ts' ? 'pinia-ts' : 'pinia-js')
109
+ features.axios && copy(language === 'ts' ? 'axios-ts' : 'axios-js')
110
+
111
+ // 6️⃣ 生成 main.js
112
+ const mainFile = language === 'ts' ? 'main.ts' : 'main.js'
113
+ const mainTplPath = path.join(targetDir, `src/${mainFile}.tpl`)
114
+ let main = fs.readFileSync(mainTplPath, 'utf-8')
115
+
116
+ const replacements = {
117
+ '/* __ROUTER_IMPORT__ */': features.router ? "import router from './router'" : '',
118
+ '/* __PINIA_IMPORT__ */': features.pinia
119
+ ? "import { createPinia } from 'pinia'\nimport persistedstate from 'pinia-plugin-persistedstate'"
120
+ : '',
121
+ '/* __ELEMENT_IMPORT__ */': features.ui === 'element'
122
+ ? `import ElementPlus from 'element-plus'
123
123
  import 'element-plus/dist/index.css'
124
124
  import * as ElementPlusIconsVue from '@element-plus/icons-vue'`
125
- : '',
126
- '/* __VANT_IMPORT__ */': features.ui === 'vant'
127
- ? `import Vant from 'vant'
125
+ : '',
126
+ '/* __VANT_IMPORT__ */': features.ui === 'vant'
127
+ ? `import Vant from 'vant'
128
128
  import 'vant/lib/index.css'`
129
- : '',
130
- '/* __ROUTER_USE__ */': features.router ? 'app.use(router)' : '',
131
- '/* __PINIA_USE__ */': features.pinia
132
- ? 'app.use(createPinia().use(persistedstate))'
133
- : '',
134
- '/* __ELEMENT_USE__ */': features.ui === 'element'
135
- ? `app.use(ElementPlus)
129
+ : '',
130
+ '/* __ROUTER_USE__ */': features.router ? 'app.use(router)' : '',
131
+ '/* __PINIA_USE__ */': features.pinia
132
+ ? 'app.use(createPinia().use(persistedstate))'
133
+ : '',
134
+ '/* __ELEMENT_USE__ */': features.ui === 'element'
135
+ ? `app.use(ElementPlus)
136
136
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
137
137
  app.component(key, component)
138
138
  }`
139
- : '',
140
- '/* __VANT_USE__ */': features.ui === 'vant'
141
- ? 'app.use(Vant)'
142
- : ''
143
- }
144
-
145
- function escapeRegExp (str) {
146
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
147
- }
148
-
149
- for (const [placeholder, content] of Object.entries(replacements)) {
150
- if (content) {
151
- main = main.replace(placeholder, content)
152
- } else {
153
- const re = new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm')
154
- main = main.replace(re, '')
139
+ : '',
140
+ '/* __VANT_USE__ */': features.ui === 'vant'
141
+ ? 'app.use(Vant)'
142
+ : ''
155
143
  }
156
- }
157
-
158
- fs.writeFileSync(
159
- path.join(targetDir, `src/${mainFile}`),
160
- main
161
- )
162
- fs.unlinkSync(mainTplPath)
163
-
164
- // 7️⃣ 生成 package.json
165
- const pkgTpl = path.join(targetDir, 'package.json.tpl')
166
- if (fs.existsSync(pkgTpl)) {
167
- let pkg = fs.readFileSync(pkgTpl, 'utf-8')
168
-
169
- const optionalDeps = {}
170
- if (features.router) optionalDeps['vue-router'] = '^4.4.0'
171
- if (features.pinia) {
172
- optionalDeps['pinia'] = '^2.2.2'
173
- optionalDeps['pinia-plugin-persistedstate'] = '^3.2.1'
174
- }
175
- if (features.axios) optionalDeps['axios'] = '^1.7.7'
176
- if (features.ui === 'element') {
177
- optionalDeps['element-plus'] = '^2.8.8'
178
- optionalDeps['@element-plus/icons-vue'] = '^2.3.1'
179
- }
180
- if (features.ui === 'vant') {
181
- optionalDeps['vant'] = '^4.9.22'
144
+
145
+ function escapeRegExp (str) {
146
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
182
147
  }
183
148
 
184
- let depsStr = ''
185
- const keys = Object.keys(optionalDeps)
186
- if (keys.length > 0) {
187
- depsStr = ',\n' + keys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
149
+ for (const [placeholder, content] of Object.entries(replacements)) {
150
+ if (content) {
151
+ main = main.replace(placeholder, content)
152
+ } else {
153
+ const re = new RegExp(`^\\s*${escapeRegExp(placeholder)}\\s*$\\n?`, 'gm')
154
+ main = main.replace(re, '')
155
+ }
188
156
  }
189
157
 
190
- pkg = pkg
191
- .replace('__PROJECT_NAME__', projectName)
192
- .replace('__OPTIONAL_DEP__', depsStr)
158
+ fs.writeFileSync(
159
+ path.join(targetDir, `src/${mainFile}`),
160
+ main
161
+ )
162
+ fs.unlinkSync(mainTplPath)
163
+
164
+ // 7️⃣ 生成 package.json
165
+ const pkgTpl = path.join(targetDir, 'package.json.tpl')
166
+ if (fs.existsSync(pkgTpl)) {
167
+ let pkg = fs.readFileSync(pkgTpl, 'utf-8')
168
+
169
+ const optionalDeps = {}
170
+ if (features.router) optionalDeps['vue-router'] = '^4.4.0'
171
+ if (features.pinia) {
172
+ optionalDeps['pinia'] = '^2.2.2'
173
+ optionalDeps['pinia-plugin-persistedstate'] = '^3.2.1'
174
+ }
175
+ if (features.axios) optionalDeps['axios'] = '^1.7.7'
176
+ if (features.ui === 'element') {
177
+ optionalDeps['element-plus'] = '^2.8.8'
178
+ optionalDeps['@element-plus/icons-vue'] = '^2.3.1'
179
+ }
180
+ if (features.ui === 'vant') {
181
+ optionalDeps['vant'] = '^4.9.22'
182
+ }
183
+
184
+ let depsStr = ''
185
+ const keys = Object.keys(optionalDeps)
186
+ if (keys.length > 0) {
187
+ depsStr = ',\n' + keys.map(k => ` "${k}": "${optionalDeps[k]}"`).join(',\n')
188
+ }
189
+
190
+ pkg = pkg
191
+ .replace('__PROJECT_NAME__', projectName)
192
+ .replace('__OPTIONAL_DEP__', depsStr)
193
+
194
+ fs.writeFileSync(path.join(targetDir, 'package.json'), pkg)
195
+ fs.unlinkSync(pkgTpl)
196
+ }
193
197
 
194
- fs.writeFileSync(path.join(targetDir, 'package.json'), pkg)
195
- fs.unlinkSync(pkgTpl)
196
- }
198
+ // 8️⃣ 安装依赖
199
+ console.log('📦 安装依赖中...')
200
+ execSync('npm install', { cwd: targetDir, stdio: 'inherit' })
197
201
 
198
- // 8️⃣ 安装依赖
199
- console.log('📦 安装依赖中...')
200
- execSync('npm install', { cwd: targetDir, stdio: 'inherit' })
202
+ // 9️⃣ 运行 dev
203
+ if (runDev) {
204
+ console.log('🚀 启动开发服务器...')
205
+ execSync('npm run dev', { cwd: targetDir, stdio: 'inherit' })
206
+ } else {
207
+ console.log(`\n✅ 项目创建完成\n👉 cd ${projectName}\n👉 npm run dev\n`)
208
+ }
209
+ })()
201
210
 
202
- // 9️⃣ 运行 dev
203
- if (runDev) {
204
- console.log('🚀 启动开发服务器...')
205
- execSync('npm run dev', { cwd: targetDir, stdio: 'inherit' })
206
- } else {
207
- console.log(`\n✅ 项目创建完成\n👉 cd ${projectName}\n👉 npm run dev\n`)
208
- }
209
- }
210
211
 
211
- createProject()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-vite-vue",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "基于Vite+Vue3创建基础项目模板",
5
5
  "main": "index.js",
6
6
  "author": "YwaiX",
@@ -24,4 +24,4 @@
24
24
  "dependencies": {
25
25
  "prompts": "^2.4.2"
26
26
  }
27
- }
27
+ }
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios'
2
+ import { addPendingRequest, removePendingRequest } from './requestCache'
2
3
 
3
4
  const service = axios.create({
4
5
  baseURL: '/api',
@@ -6,13 +7,44 @@ const service = axios.create({
6
7
  })
7
8
 
8
9
  service.interceptors.request.use(
9
- config => config,
10
+ config => {
11
+ // 在发送请求之前做些什么
12
+
13
+ try {
14
+ addPendingRequest(config)
15
+ } catch (error) {
16
+ // 如果是取消请求,直接reject
17
+ if (error.isCancel) {
18
+ return Promise.reject({ canceled: true, message: '请求取消' })
19
+ }
20
+ throw error
21
+ }
22
+ return config
23
+ },
24
+ // 对请求错误做些什么
10
25
  error => Promise.reject(error)
11
26
  )
12
27
 
13
28
  service.interceptors.response.use(
14
- response => response.data,
15
- error => Promise.reject(error)
29
+ response => {
30
+ removePendingRequest(response.config)
31
+
32
+ // 对响应数据做点什么
33
+ return response.data
34
+ },
35
+ error => {
36
+ // 即使是错误(包括网络错误)也要清理
37
+ if (error.config) {
38
+ removePendingRequest(error.config)
39
+ }
40
+ // 判断是否是重复请求被取消
41
+ if (error.canceled) {
42
+ return Promise.reject(error)
43
+ }
44
+
45
+ // 对响应错误做点什么
46
+ return Promise.reject(error)
47
+ }
16
48
  )
17
49
 
18
50
  export default service
@@ -0,0 +1,41 @@
1
+ const pendingRequests = new Map()
2
+
3
+ /**
4
+ * 生成请求唯一kaey
5
+ * @param {RequestConfig} config 请求配置对象
6
+ * 注意:只适用于可序列化的 params 和 data(如普通对象)
7
+ */
8
+ function generateReqKey (config) {
9
+ const { method, url, params, data } = config
10
+ return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
11
+ }
12
+
13
+ /**
14
+ * 添加pending请求,并自动取消请求
15
+ * @param {RequestConfig} config 请求配置对象
16
+ */
17
+ export function addPendingRequest (config) {
18
+ // 获取请求key
19
+ const requestKey = generateReqKey(config)
20
+ // 如果存在相同请求,取消当前请求
21
+ if (pendingRequests.has(requestKey)) {
22
+ const error = new Error('重复请求')
23
+ error.isCancel = true
24
+ throw error
25
+ }
26
+
27
+ //否则为本次请求创建新的AbortController
28
+ const controller = new AbortController()
29
+ // 绑定 signal 到 axios 配置
30
+ config.signal = controller.signal
31
+ pendingRequests.set(requestKey, controller)
32
+ }
33
+
34
+ /**
35
+ * 移除已完成/失败/取消的请求
36
+ * @param {RequestConfig} config 请求配置对象
37
+ */
38
+ export function removePendingRequest (config) {
39
+ const requestKey = generateReqKey(config)
40
+ pendingRequests.delete(requestKey)
41
+ }
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios'
2
+ import { addPendingRequest, removePendingRequest } from './requestCache'
2
3
 
3
4
  const service = axios.create({
4
5
  baseURL: '/api',
@@ -6,13 +7,44 @@ const service = axios.create({
6
7
  })
7
8
 
8
9
  service.interceptors.request.use(
9
- config => config,
10
+ config => {
11
+ // 在发送请求之前做些什么
12
+
13
+ try {
14
+ addPendingRequest(config)
15
+ } catch (error) {
16
+ // 如果是取消请求,直接reject
17
+ if (error.isCancel) {
18
+ return Promise.reject({ canceled: true, message: '请求取消' })
19
+ }
20
+ throw error
21
+ }
22
+ return config
23
+ },
24
+ // 对请求错误做些什么
10
25
  error => Promise.reject(error)
11
26
  )
12
27
 
13
28
  service.interceptors.response.use(
14
- response => response.data,
15
- error => Promise.reject(error)
29
+ response => {
30
+ removePendingRequest(response.config)
31
+
32
+ // 对响应数据做点什么
33
+ return response.data
34
+ },
35
+ error => {
36
+ // 即使是错误(包括网络错误)也要清理
37
+ if (error.config) {
38
+ removePendingRequest(error.config)
39
+ }
40
+ // 判断是否是重复请求被取消
41
+ if (error.canceled) {
42
+ return Promise.reject(error)
43
+ }
44
+
45
+ // 对响应错误做点什么
46
+ return Promise.reject(error)
47
+ }
16
48
  )
17
49
 
18
50
  export default service
@@ -0,0 +1,41 @@
1
+ const pendingRequests = new Map()
2
+
3
+ /**
4
+ * 生成请求唯一kaey
5
+ * @param {RequestConfig} config 请求配置对象
6
+ * 注意:只适用于可序列化的 params 和 data(如普通对象)
7
+ */
8
+ function generateReqKey (config) {
9
+ const { method, url, params, data } = config
10
+ return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
11
+ }
12
+
13
+ /**
14
+ * 添加pending请求,并自动取消请求
15
+ * @param {RequestConfig} config 请求配置对象
16
+ */
17
+ export function addPendingRequest (config) {
18
+ // 获取请求key
19
+ const requestKey = generateReqKey(config)
20
+ // 如果存在相同请求,取消当前请求
21
+ if (pendingRequests.has(requestKey)) {
22
+ const error = new Error('重复请求') as Error & { isCancel: boolean }
23
+ error.isCancel = true
24
+ throw error
25
+ }
26
+
27
+ //否则为本次请求创建新的AbortController
28
+ const controller = new AbortController()
29
+ // 绑定 signal 到 axios 配置
30
+ config.signal = controller.signal
31
+ pendingRequests.set(requestKey, controller)
32
+ }
33
+
34
+ /**
35
+ * 移除已完成/失败/取消的请求
36
+ * @param {RequestConfig} config 请求配置对象
37
+ */
38
+ export function removePendingRequest (config) {
39
+ const requestKey = generateReqKey(config)
40
+ pendingRequests.delete(requestKey)
41
+ }
@@ -0,0 +1,148 @@
1
+ // request.d.ts
2
+
3
+ declare global {
4
+ /**
5
+ * HTTP 请求方法类型
6
+ */
7
+ type HttpMethod =
8
+ | 'GET'
9
+ | 'POST'
10
+ | 'PUT'
11
+ | 'DELETE'
12
+ | 'PATCH'
13
+ | 'HEAD'
14
+ | 'OPTIONS'
15
+ | 'CONNECT'
16
+ | 'TRACE'
17
+
18
+ /**
19
+ * 请求配置对象(Axios 风格)
20
+ */
21
+ interface RequestConfig<T = any> {
22
+ /** 请求方法,默认 GET */
23
+ method?: HttpMethod
24
+ /** 请求 URL */
25
+ url: string
26
+ /** 基础 URL,会自动拼接到 url 前面 */
27
+ baseURL?: string
28
+ /** URL 查询参数(GET 请求的参数) */
29
+ params?: Record<string, any>
30
+ /** 请求体数据(POST、PUT、PATCH 等请求的参数) */
31
+ data?: T
32
+ /** 请求超时时间(毫秒),0 表示不超时 */
33
+ timeout?: number
34
+ /** 超时错误消息 */
35
+ timeoutErrorMessage?: string
36
+ /** 是否携带跨域凭证(cookies) */
37
+ withCredentials?: boolean
38
+ /** 响应数据类型 */
39
+ responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
40
+ /** 响应编码 */
41
+ responseEncoding?: string
42
+ /** 自定义请求头 */
43
+ headers?: Record<string, string>
44
+ /** HTTP 基础认证 */
45
+ auth?: {
46
+ username: string
47
+ password: string
48
+ }
49
+ /** 代理配置 */
50
+ proxy?: {
51
+ host: string
52
+ port: number
53
+ protocol?: string
54
+ auth?: string
55
+ }
56
+ /** 响应内容最大长度 */
57
+ maxContentLength?: number
58
+ /** 请求体最大长度 */
59
+ maxBodyLength?: number
60
+ /** 最大重定向次数 */
61
+ maxRedirects?: number
62
+ /** Unix Socket 路径 */
63
+ socketPath?: string
64
+ /** Node.js HTTP Agent */
65
+ httpAgent?: any
66
+ /** Node.js HTTPS Agent */
67
+ httpsAgent?: any
68
+ /** 是否自动解压响应 */
69
+ decompress?: boolean
70
+ /** 自定义状态码验证函数 */
71
+ validateStatus?: (status: number) => boolean
72
+ /** 参数序列化函数 */
73
+ paramsSerializer?: (params: any) => string
74
+ /** 请求数据转换函数 */
75
+ transformRequest?: ((data: any, headers: any) => any) | Array<(data: any, headers: any) => any>
76
+ /** 响应数据转换函数 */
77
+ transformResponse?: ((data: any, headers: any, status: number) => any) | Array<(data: any, headers: any, status: number) => any>
78
+ /** 上传进度事件回调 */
79
+ onUploadProgress?: (progressEvent: any) => void
80
+ /** 下载进度事件回调 */
81
+ onDownloadProgress?: (progressEvent: any) => void
82
+ /** 取消令牌 */
83
+ cancelToken?: any
84
+ /** CSRF token cookie 名称 */
85
+ xsrfCookieName?: string
86
+ /** CSRF token header 名称 */
87
+ xsrfHeaderName?: string
88
+ /** 请求适配器 */
89
+ adapter?: (config: RequestConfig) => any
90
+ /** AbortSignal(用于取消请求) */
91
+ signal?: AbortSignal
92
+ /** 是否使用不安全的 HTTP 解析器 */
93
+ insecureHTTPParser?: boolean
94
+ /** 过渡性配置 */
95
+ transitional?: {
96
+ /** 是否静默 JSON 解析错误 */
97
+ silentJSONParsing?: boolean
98
+ /** 是否强制 JSON 解析 */
99
+ forcedJSONParsing?: boolean
100
+ /** 是否澄清超时错误 */
101
+ clarifyTimeoutError?: boolean
102
+ }
103
+ /** 代理连接回调 */
104
+ onProxyConnect?: (proxyReq: any, req: any, res: any) => void
105
+ /** 代理错误回调 */
106
+ onProxyError?: (err: Error, req: any, res: any) => void
107
+ /** 代理响应回调 */
108
+ onProxyResponse?: (proxyRes: any, req: any, res: any) => void
109
+ /** 方法名称(用于拦截器) */
110
+ methodName?: string
111
+ }
112
+
113
+ /**
114
+ * Axios 响应对象
115
+ */
116
+ interface AxiosResponse<T = any> {
117
+ /** 响应数据 */
118
+ data: T
119
+ /** HTTP 状态码 */
120
+ status: number
121
+ /** HTTP 状态文本 */
122
+ statusText: string
123
+ /** 响应头 */
124
+ headers: Record<string, string>
125
+ /** 请求配置 */
126
+ config: RequestConfig<T>
127
+ /** 原始请求对象 */
128
+ request?: XMLHttpRequest
129
+ }
130
+
131
+ /**
132
+ * Axios 错误对象
133
+ */
134
+ interface AxiosError<T = any> extends Error {
135
+ /** 错误代码 */
136
+ code?: string
137
+ /** 响应对象(如果收到响应) */
138
+ response?: AxiosResponse<T>
139
+ /** 请求配置 */
140
+ config?: RequestConfig<T>
141
+ /** 原始请求对象 */
142
+ request?: XMLHttpRequest
143
+ /** 是否为 Axios 错误 */
144
+ isAxiosError: boolean
145
+ }
146
+ }
147
+ export { }
148
+
@@ -0,0 +1,148 @@
1
+ // request.d.ts
2
+
3
+ declare global {
4
+ /**
5
+ * HTTP 请求方法类型
6
+ */
7
+ type HttpMethod =
8
+ | 'GET'
9
+ | 'POST'
10
+ | 'PUT'
11
+ | 'DELETE'
12
+ | 'PATCH'
13
+ | 'HEAD'
14
+ | 'OPTIONS'
15
+ | 'CONNECT'
16
+ | 'TRACE'
17
+
18
+ /**
19
+ * 请求配置对象(Axios 风格)
20
+ */
21
+ interface RequestConfig<T = any> {
22
+ /** 请求方法,默认 GET */
23
+ method?: HttpMethod
24
+ /** 请求 URL */
25
+ url: string
26
+ /** 基础 URL,会自动拼接到 url 前面 */
27
+ baseURL?: string
28
+ /** URL 查询参数(GET 请求的参数) */
29
+ params?: Record<string, any>
30
+ /** 请求体数据(POST、PUT、PATCH 等请求的参数) */
31
+ data?: T
32
+ /** 请求超时时间(毫秒),0 表示不超时 */
33
+ timeout?: number
34
+ /** 超时错误消息 */
35
+ timeoutErrorMessage?: string
36
+ /** 是否携带跨域凭证(cookies) */
37
+ withCredentials?: boolean
38
+ /** 响应数据类型 */
39
+ responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
40
+ /** 响应编码 */
41
+ responseEncoding?: string
42
+ /** 自定义请求头 */
43
+ headers?: Record<string, string>
44
+ /** HTTP 基础认证 */
45
+ auth?: {
46
+ username: string
47
+ password: string
48
+ }
49
+ /** 代理配置 */
50
+ proxy?: {
51
+ host: string
52
+ port: number
53
+ protocol?: string
54
+ auth?: string
55
+ }
56
+ /** 响应内容最大长度 */
57
+ maxContentLength?: number
58
+ /** 请求体最大长度 */
59
+ maxBodyLength?: number
60
+ /** 最大重定向次数 */
61
+ maxRedirects?: number
62
+ /** Unix Socket 路径 */
63
+ socketPath?: string
64
+ /** Node.js HTTP Agent */
65
+ httpAgent?: any
66
+ /** Node.js HTTPS Agent */
67
+ httpsAgent?: any
68
+ /** 是否自动解压响应 */
69
+ decompress?: boolean
70
+ /** 自定义状态码验证函数 */
71
+ validateStatus?: (status: number) => boolean
72
+ /** 参数序列化函数 */
73
+ paramsSerializer?: (params: any) => string
74
+ /** 请求数据转换函数 */
75
+ transformRequest?: ((data: any, headers: any) => any) | Array<(data: any, headers: any) => any>
76
+ /** 响应数据转换函数 */
77
+ transformResponse?: ((data: any, headers: any, status: number) => any) | Array<(data: any, headers: any, status: number) => any>
78
+ /** 上传进度事件回调 */
79
+ onUploadProgress?: (progressEvent: any) => void
80
+ /** 下载进度事件回调 */
81
+ onDownloadProgress?: (progressEvent: any) => void
82
+ /** 取消令牌 */
83
+ cancelToken?: any
84
+ /** CSRF token cookie 名称 */
85
+ xsrfCookieName?: string
86
+ /** CSRF token header 名称 */
87
+ xsrfHeaderName?: string
88
+ /** 请求适配器 */
89
+ adapter?: (config: RequestConfig) => any
90
+ /** AbortSignal(用于取消请求) */
91
+ signal?: AbortSignal
92
+ /** 是否使用不安全的 HTTP 解析器 */
93
+ insecureHTTPParser?: boolean
94
+ /** 过渡性配置 */
95
+ transitional?: {
96
+ /** 是否静默 JSON 解析错误 */
97
+ silentJSONParsing?: boolean
98
+ /** 是否强制 JSON 解析 */
99
+ forcedJSONParsing?: boolean
100
+ /** 是否澄清超时错误 */
101
+ clarifyTimeoutError?: boolean
102
+ }
103
+ /** 代理连接回调 */
104
+ onProxyConnect?: (proxyReq: any, req: any, res: any) => void
105
+ /** 代理错误回调 */
106
+ onProxyError?: (err: Error, req: any, res: any) => void
107
+ /** 代理响应回调 */
108
+ onProxyResponse?: (proxyRes: any, req: any, res: any) => void
109
+ /** 方法名称(用于拦截器) */
110
+ methodName?: string
111
+ }
112
+
113
+ /**
114
+ * Axios 响应对象
115
+ */
116
+ interface AxiosResponse<T = any> {
117
+ /** 响应数据 */
118
+ data: T
119
+ /** HTTP 状态码 */
120
+ status: number
121
+ /** HTTP 状态文本 */
122
+ statusText: string
123
+ /** 响应头 */
124
+ headers: Record<string, string>
125
+ /** 请求配置 */
126
+ config: RequestConfig<T>
127
+ /** 原始请求对象 */
128
+ request?: XMLHttpRequest
129
+ }
130
+
131
+ /**
132
+ * Axios 错误对象
133
+ */
134
+ interface AxiosError<T = any> extends Error {
135
+ /** 错误代码 */
136
+ code?: string
137
+ /** 响应对象(如果收到响应) */
138
+ response?: AxiosResponse<T>
139
+ /** 请求配置 */
140
+ config?: RequestConfig<T>
141
+ /** 原始请求对象 */
142
+ request?: XMLHttpRequest
143
+ /** 是否为 Axios 错误 */
144
+ isAxiosError: boolean
145
+ }
146
+ }
147
+ export { }
148
+