cmpt-huitu-cli 1.0.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.
Files changed (60) hide show
  1. package/README.md +169 -0
  2. package/bin/huitu.js +30 -0
  3. package/package.json +38 -0
  4. package/src/create.js +127 -0
  5. package/src/versions.js +23 -0
  6. package/templates/default/README.md +127 -0
  7. package/templates/default/eslint.config.js +187 -0
  8. package/templates/default/index.html +38 -0
  9. package/templates/default/jsconfig.json +29 -0
  10. package/templates/default/package.json +56 -0
  11. package/templates/default/public/.DS_Store +0 -0
  12. package/templates/default/public/config/envCfg.js +68 -0
  13. package/templates/default/public/images/favicon.ico +0 -0
  14. package/templates/default/src/.DS_Store +0 -0
  15. package/templates/default/src/App.vue +49 -0
  16. package/templates/default/src/assets/.DS_Store +0 -0
  17. package/templates/default/src/assets/empty.png +0 -0
  18. package/templates/default/src/assets/images/.DS_Store +0 -0
  19. package/templates/default/src/assets/images/404/403_1.png +0 -0
  20. package/templates/default/src/assets/images/404/404.png +0 -0
  21. package/templates/default/src/assets/images/404/404_2.png +0 -0
  22. package/templates/default/src/assets/images/404/404_cloud.png +0 -0
  23. package/templates/default/src/assets/images/login/img.png +0 -0
  24. package/templates/default/src/assets/images/login/logo.png +0 -0
  25. package/templates/default/src/assets/images/login/person.png +0 -0
  26. package/templates/default/src/components/.DS_Store +0 -0
  27. package/templates/default/src/components/error/Error.vue +259 -0
  28. package/templates/default/src/components.d.ts +18 -0
  29. package/templates/default/src/imports.d.ts +90 -0
  30. package/templates/default/src/main.js +27 -0
  31. package/templates/default/src/routers/base.js +41 -0
  32. package/templates/default/src/routers/guard.js +43 -0
  33. package/templates/default/src/routers/index.js +36 -0
  34. package/templates/default/src/routers/modules/example.js +17 -0
  35. package/templates/default/src/services/.DS_Store +0 -0
  36. package/templates/default/src/services/login.js +32 -0
  37. package/templates/default/src/stores/README.md +317 -0
  38. package/templates/default/src/stores/index/global.js +49 -0
  39. package/templates/default/src/stores/index/template.js +31 -0
  40. package/templates/default/src/stores/index.js +14 -0
  41. package/templates/default/src/stores//344/275/277/347/224/250/347/244/272/344/276/213.vue +94 -0
  42. package/templates/default/src/styles/index.scss +23 -0
  43. package/templates/default/src/styles/theme/README.md +52 -0
  44. package/templates/default/src/styles/theme/variables.scss +62 -0
  45. package/templates/default/src/utils/RequestCache.js +198 -0
  46. package/templates/default/src/utils/auth.js +51 -0
  47. package/templates/default/src/utils/errorCode.js +16 -0
  48. package/templates/default/src/utils/index.js +519 -0
  49. package/templates/default/src/utils/requestAxios.js +148 -0
  50. package/templates/default/src/utils/theme.js +20 -0
  51. package/templates/default/src/views/About.vue +6 -0
  52. package/templates/default/src/views/Home.vue +111 -0
  53. package/templates/default/src/views/login/index.vue +285 -0
  54. package/templates/default/vite/plugins/autoComponents.js +18 -0
  55. package/templates/default/vite/plugins/autoImport.js +13 -0
  56. package/templates/default/vite/plugins/compression.js +24 -0
  57. package/templates/default/vite/plugins/externals.js +8 -0
  58. package/templates/default/vite/plugins/index.js +18 -0
  59. package/templates/default/vite/plugins/visualizer.js +11 -0
  60. package/templates/default/vite.config.js +185 -0
@@ -0,0 +1,519 @@
1
+ /**
2
+ * @FileDescription: 系统通用工具类
3
+ */
4
+
5
+ export function isProd() {
6
+ // 推荐:使用import.meta.env.DEV 和 import.meta.env.PROD进行环境判断,而非直接检查MODE,因为MODE可能被环境变量文件或者命令改变
7
+ if (import.meta.env.PROD) {
8
+ return true
9
+ }
10
+ return false
11
+ }
12
+
13
+ export function isDev() {
14
+ if (import.meta.env.DEV) {
15
+ return true
16
+ }
17
+ return false
18
+ }
19
+
20
+ // 获取上传后图片的地址
21
+ export function getFileFullPathNoBucket(fileName, filePath) {
22
+ const uploadUrl = window.HT_EnvConfig?.uploadUrl || '/upload'
23
+ return uploadUrl + '/api/oss/minio/objects/file?filename=' + fileName + '&filepath=' + filePath
24
+ }
25
+
26
+ // 获取上传后图片的地址(带 bucket)
27
+ export function getFileFullPathByBucket(fileName, filePath, bucketname = 'default') {
28
+ const uploadUrl = window.HT_EnvConfig?.uploadUrl || '/upload'
29
+ return uploadUrl + '/api/oss/minio/objects/file-by-bucketname?bucketname=' + bucketname + '&filename=' + fileName + '&filepath=' + filePath
30
+ }
31
+
32
+ /**
33
+ * Parse the time to string
34
+ * @param {(Object|string|number)} time
35
+ * @param {string} cFormat
36
+ * @returns {string}
37
+ */
38
+ export function parseTimeNative(time, cFormat) {
39
+ if (arguments.length === 0) {
40
+ return null
41
+ }
42
+ const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
43
+ let date
44
+
45
+ if (typeof time === 'object') {
46
+ date = time
47
+ } else {
48
+ if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
49
+ time = Number.parseInt(time)
50
+ }
51
+ if (typeof time === 'number' && time.toString().length === 10) {
52
+ time = time * 1000
53
+ }
54
+ date = new Date(time)
55
+ }
56
+ const formatObj = {
57
+ y: date.getFullYear(),
58
+ m: date.getMonth() + 1,
59
+ d: date.getDate(),
60
+ h: date.getHours(),
61
+ i: date.getMinutes(),
62
+ s: date.getSeconds(),
63
+ a: date.getDay(),
64
+ }
65
+ const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
66
+ let value = formatObj[key]
67
+ // Note: getDay() returns 0 on Sunday
68
+ if (key === 'a') {
69
+ return ['日', '一', '二', '三', '四', '五', '六'][value]
70
+ }
71
+ if (result.length > 0 && value < 10) {
72
+ value = '0' + value
73
+ }
74
+ return value || 0
75
+ })
76
+ return timeStr
77
+ }
78
+
79
+ /**
80
+ * @param {number} time
81
+ * @param {string} option
82
+ * @returns {string}
83
+ */
84
+ export function formatTime(time, option) {
85
+ if (('' + time).length === 10) {
86
+ time = Number.parseInt(time) * 1000
87
+ } else {
88
+ time = +time
89
+ }
90
+ const d = new Date(time)
91
+ const now = Date.now()
92
+
93
+ const diff = (now - d) / 1000
94
+
95
+ if (diff < 30) {
96
+ return '刚刚'
97
+ } else if (diff < 3600) {
98
+ // less 1 hour
99
+ return Math.ceil(diff / 60) + '分钟前'
100
+ } else if (diff < 3600 * 24) {
101
+ return Math.ceil(diff / 3600) + '小时前'
102
+ } else if (diff < 3600 * 24 * 2) {
103
+ return '1天前'
104
+ }
105
+ if (option) {
106
+ return parseTimeNative(time, option)
107
+ } else {
108
+ return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @param {string} url
114
+ * @returns {Object}
115
+ */
116
+ export function getQueryParameters(url) {
117
+ const search = url.split('?')[1]
118
+ if (!search) {
119
+ return {}
120
+ }
121
+ return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"').replace(/\+/g, ' ') + '"}')
122
+ }
123
+ // 移除地址栏?参数
124
+ export function removeQueryParameters(str, name) {
125
+ var reg = new RegExp(name + '=([^&]*)(&?)', 'i')
126
+ var r = str.match(reg)
127
+ if (r != null) {
128
+ str = str.replace(reg, '')
129
+ // console.log(str)
130
+ // alert()
131
+ }
132
+ return str
133
+ }
134
+ export function hasClass(elem, cls) {
135
+ cls = cls || ''
136
+ if (cls.replace(/\s/g, '').length === 0) return false // 当cls没有参数时,返回false
137
+ return new RegExp(' ' + cls + ' ').test(' ' + elem.className + ' ')
138
+ }
139
+
140
+ export function addClass(elem, cls) {
141
+ if (!hasClass(elem, cls)) {
142
+ elem.className = elem.className === '' ? cls : elem.className + ' ' + cls
143
+ }
144
+ }
145
+
146
+ export function removeClass(elem, cls) {
147
+ if (hasClass(elem, cls)) {
148
+ let newClass = ' ' + elem.className.replace(/[\t\r\n]/g, '') + ' '
149
+ while (newClass.indexOf(' ' + cls + ' ') >= 0) {
150
+ newClass = newClass.replace(' ' + cls + ' ', ' ')
151
+ }
152
+ elem.className = newClass.replace(/^\s+|\s+$/g, '')
153
+ }
154
+ }
155
+
156
+ export function tansParamsString(obj) {
157
+ if (!obj) return ''
158
+ const pairs = []
159
+ for (const key in obj) {
160
+ const value = obj[key]
161
+
162
+ if (value instanceof Array) {
163
+ for (let i = 0; i < value.length; ++i) {
164
+ pairs.push(encodeURIComponent(key + '[' + i + ']') + '=' + encodeURIComponent(value[i]))
165
+ }
166
+ continue
167
+ }
168
+
169
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
170
+ }
171
+
172
+ return pairs.join('&')
173
+ }
174
+
175
+ /**
176
+ * [strToNull 处理提交JSON后台不接受""空字符串]
177
+ * @author: cm
178
+ * @param {对象} data
179
+ */
180
+ export const strToNull = (data) => {
181
+ for (const x in data) {
182
+ if (data[x] === '') {
183
+ // 如果是空字符串 把直接内容转为 null
184
+ data[x] = ''
185
+ } else {
186
+ if (Array.isArray(data[x])) {
187
+ // 是数组遍历数组 递归继续处理
188
+ data[x] = data[x].map((z) => {
189
+ return strToNull(z)
190
+ })
191
+ }
192
+ if (typeof data[x] === 'object') {
193
+ // 是json 递归继续处理
194
+ data[x] = strToNull(data[x])
195
+ }
196
+ }
197
+ }
198
+ return data
199
+ }
200
+
201
+ // 防抖函数
202
+ // 用法示例: methods:{ testFun: debounce(function(params){}, 500)}
203
+ export const debounce = (fn, t) => {
204
+ const delay = t || 500
205
+ let timer
206
+ return function () {
207
+ const args = arguments
208
+ if (timer) {
209
+ clearTimeout(timer)
210
+ }
211
+ timer = setTimeout(() => {
212
+ timer = null
213
+ fn.apply(this, args)
214
+ }, delay)
215
+ }
216
+ }
217
+
218
+ /**
219
+ * 函数节流
220
+ * @param fn
221
+ * @param interval
222
+ * @returns {Function}
223
+ * @constructor
224
+ */
225
+ export const throttle = (fn, t) => {
226
+ let last
227
+ let timer
228
+ const interval = t || 500
229
+ return function () {
230
+ const args = arguments
231
+ const now = +new Date()
232
+ if (last && now - last < interval) {
233
+ clearTimeout(timer)
234
+ timer = setTimeout(() => {
235
+ last = now
236
+ fn.apply(this, args)
237
+ }, interval)
238
+ } else {
239
+ last = now
240
+ fn.apply(this, args)
241
+ }
242
+ }
243
+ }
244
+
245
+ /**
246
+ * 获取数值指定小数点位数
247
+ * @param num 数值
248
+ * @param digit 小数点位数
249
+ * @returns {null}
250
+ */
251
+ export function getNumberByDigit(num, digit) {
252
+ if (!isNum(num)) return ''
253
+ if (digit === 3 && num % 1 === 0) return num
254
+ const retNum = Number.parseFloat(num).toFixed(digit)
255
+ return retNum == 0 ? 0 : Number.parseFloat(num).toFixed(digit) * 1 // 此方法会触发四舍五入 - 此处万不能使用绝对等于号
256
+ // return Math.ceil(0.007 * 100) / 100
257
+ }
258
+
259
+ /**
260
+ * 对比对象差异并返回差异属性
261
+ * @param obj1
262
+ * @param obj2
263
+ * @return {*}
264
+ */
265
+ export function diffObjProps(obj1, obj2) {
266
+ return Object.fromEntries([...Object.entries(obj1), ...Object.entries(obj2)].filter(([key]) => obj1[key] !== obj2[key]))
267
+ }
268
+
269
+ /**
270
+ * 判断字符是否为中文汉字
271
+ * @param str
272
+ * @return {boolean}
273
+ */
274
+ export function isChinese(str) {
275
+ const reg = /^[\u4E00-\u9FA5]+$/
276
+ return reg.test(str)
277
+ }
278
+
279
+ /**
280
+ * 经度验证,最长2位,小数点后最长7位数
281
+ * @param val
282
+ * @return {*}
283
+ * @constructor
284
+ */
285
+ export function lgtdIsValid(val) {
286
+ const regex = /^[-+]?[0-9]{1,3}(\.[0-9]{1,7})?$/
287
+ return regex.test(val)
288
+ }
289
+
290
+ /**
291
+ * 纬度验证,最长2位,小数点后最长7位数
292
+ * @param val
293
+ * @return {*}
294
+ * @constructor
295
+ */
296
+ export function lttdIsValid(val) {
297
+ const regex = /^[-+]?[0-9]{1,2}(\.[0-9]{1,7})?$/
298
+ return regex.test(val)
299
+ }
300
+
301
+ /**
302
+ * 深拷贝
303
+ *
304
+ */
305
+ export function deepCopy(obj) {
306
+ if (typeof obj !== 'object' || obj === null) {
307
+ return obj // 如果不是对象类型或者是null,直接返回
308
+ }
309
+
310
+ let copy
311
+ if (Array.isArray(obj)) {
312
+ copy = [] // 如果是数组,创建一个空数组
313
+ for (let i = 0; i < obj.length; i++) {
314
+ copy[i] = deepCopy(obj[i]) // 递归拷贝数组中的每个元素
315
+ }
316
+ } else {
317
+ copy = {} // 如果是对象,创建一个空对象
318
+ for (const key in obj) {
319
+ if (obj.hasOwnProperty(key)) {
320
+ copy[key] = deepCopy(obj[key]) // 递归拷贝对象中的每个属性值
321
+ }
322
+ }
323
+ }
324
+ return copy // 返回深拷贝后的对象或数组
325
+ }
326
+
327
+ /**
328
+ * 导出前的HTML元素图片转换
329
+ * @param dataurl
330
+ * @return {Blob}
331
+ */
332
+ export function dataURLToBlob(dataurl) {
333
+ const arr = dataurl.split(',')
334
+ const mime = arr[0].match(/:(.*?);/)[1]
335
+ const bstr = atob(arr[1])
336
+ let n = bstr.length
337
+ const u8arr = new Uint8Array(n)
338
+ while (n--) {
339
+ u8arr[n] = bstr.charCodeAt(n)
340
+ }
341
+ return new Blob([u8arr], { type: mime })
342
+ }
343
+
344
+ /**
345
+ * 将相对路径转换为绝对URL地址
346
+ * @param {string} url - 需要转换的相对路径
347
+ * @returns {string|null} 返回转换后的绝对URL地址,如果转换失败则返回null
348
+ */
349
+ export const requireFile = (url) => {
350
+ // 方式一:容易错误
351
+ // try {
352
+ // // 如果是以 / 开头的绝对路径,直接返回
353
+ // if (url.startsWith('/')) {
354
+ // return url
355
+ // }
356
+ // // 使用new URL时路径需基于当前文件位置计算相对路径,@别名可能失效需改用../
357
+ // return new URL(url, import.meta.url).href
358
+ // } catch (error) {
359
+ // console.error(error)
360
+ // return null
361
+ // }
362
+
363
+ // 方式二:推荐
364
+ // 静态扫描 src/assets 下所有图片资源,生成路径映射(构建时执行)
365
+ // 匹配规则:递归扫描所有子目录,支持 png/jpg/jpeg/gif/svg 格式
366
+ const imageResourceMap = import.meta.glob(
367
+ '/src/assets/**/*.{png,jpg,jpeg,gif,svg}',
368
+ { eager: true, import: 'default' } // 立即加载并获取资源路径
369
+ )
370
+ try {
371
+ // 拼接完整的资源路径(基于 src 目录)
372
+ const fullResourcePath = `/src${url}`
373
+ // 从映射表中获取对应资源的处理后路径(开发/生产环境自适应)
374
+ return imageResourceMap[fullResourcePath] || null
375
+ } catch (error) {
376
+ console.error('图片资源路径解析失败:', error)
377
+ return null
378
+ }
379
+ }
380
+
381
+ // 判断是否为数值 true 数值类型 false 其他
382
+ function isNum(val) {
383
+ // 先判定是否为number
384
+ if (typeof val !== 'number') {
385
+ return false
386
+ }
387
+ if (!isNaN(val)) {
388
+ return true
389
+ } else {
390
+ return false
391
+ }
392
+ }
393
+ // 截断到指定位数(不四舍五入)
394
+ const truncateToDecimal = (num, digit) => {
395
+ const multiplier = Math.pow(10, digit)
396
+ // 正数用Math.floor,负数用Math.ceil(避免-0.25截断为-0.3)
397
+ return num >= 0 ? Math.floor(num * multiplier) / multiplier : Math.ceil(num * multiplier) / multiplier
398
+ }
399
+ // 判断是否为数值 true 数值类型 false 其他
400
+ export const formatNum = (val, digit) => {
401
+ // 异常值判断:无效输入返回 '-'
402
+ if (val === null || isNaN(Number(val)) || val === undefined || val === '' || val === ' ') return '-'
403
+
404
+ // 1. 转换为数值并截断到指定位数(不四舍五入)
405
+ const num = Number(val)
406
+ const truncatedNum = truncateToDecimal(num, digit) // 核心:截断逻辑
407
+
408
+ // 2. 转为带指定位数的字符串(确保至少有digit位小数,补0)
409
+ // 用toFixed临时转换,再处理截断后的结果(避免浮点数精度问题)
410
+ const fixedStr = truncatedNum.toFixed(digit)
411
+
412
+ // 3. 分割整数和小数部分
413
+ const [integerPart, decimalPart] = fixedStr.split('.')
414
+
415
+ // 4. 判断是否为整数(小数部分全为0)
416
+ const isInteger = decimalPart.split('').every((char) => char === '0')
417
+
418
+ if (isInteger) {
419
+ // 整数:强制保留digit位小数(补0)
420
+ return `${integerPart}.${decimalPart}`
421
+ } else {
422
+ // 非整数:去掉小数部分尾部的0
423
+ const trimmedDecimal = decimalPart.replace(/0+$/, '')
424
+ return `${integerPart}.${trimmedDecimal}`
425
+ }
426
+ // // 异常值判断
427
+ // if (val === null || isNaN(val) || val === undefined || val === '' || val === ' ') return '--'
428
+ // // 数值 或 字符串数值
429
+ // val = Number.parseFloat(val).toFixed(digit)
430
+
431
+ // // 删除尾部的‘0’
432
+ // if (val.endsWith('0')) {
433
+ // val = val.substring(0, val.lastIndexOf('0'))
434
+ // }
435
+
436
+ // // 删除尾部的‘.’
437
+ // if (val.endsWith('.')) {
438
+ // val = val.substring(0, val.lastIndexOf('.'))
439
+ // }
440
+ // return val
441
+ }
442
+ /**
443
+ * @description: 数据深拷贝
444
+ * @param {*} obj 对象
445
+ * @return {*}
446
+ */
447
+ export const deepClone = (obj) => {
448
+ const isObj = (v) => v && typeof v === 'object'
449
+ if (!isObj(obj)) return obj
450
+ const newObj = Array.isArray(obj) ? [] : {}
451
+ for (const key in obj) {
452
+ const item = obj[key]
453
+ newObj[key] = isObj(item) ? deepClone(item) : item
454
+ }
455
+ return newObj
456
+ }
457
+
458
+ /**
459
+ * @description: 文件下载
460
+ * @param {*} data 文件流
461
+ * @return {String} fileName 下载显示的文件名称
462
+ */
463
+ export function downloadFileXls(data, fileName) {
464
+ // application/vnd.ms-excel,采用后台响应的content-type值
465
+ downloadFile(data, fileName, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
466
+ }
467
+ export function downloadFileDoc(data, fileName) {
468
+ downloadFile(data, fileName, 'application/msword')
469
+ }
470
+ export function downloadFilePpt(data, fileName) {
471
+ downloadFile(data, fileName, 'application/vnd.ms-powerpoint')
472
+ }
473
+ export function downloadFileZip(data, fileName) {
474
+ downloadFile(data, fileName, 'application/zip')
475
+ }
476
+ export function downloadFileTxt(data, fileName) {
477
+ downloadFile(data, fileName, 'application/plain')
478
+ }
479
+ export function downloadFile(data, fileName, type) {
480
+ // 生成blob对象
481
+ //const blob = new Blob([data], { type: `${type}` })
482
+ const blob = new Blob([data], { type: `${type};charset=utf-8` })
483
+
484
+ if (window.navigator.msSaveOrOpenBlob) {
485
+ window.navigator.msSaveBlob(blob, fileName)
486
+ return
487
+ }
488
+
489
+ // 生成下载链接
490
+ const downElement = document.createElement('a')
491
+ downElement.style.display = 'none'
492
+ const href = window.URL.createObjectURL(blob)
493
+ downElement.href = href
494
+ downElement.download = fileName
495
+ document.body.appendChild(downElement)
496
+ downElement.click()
497
+ window.URL.revokeObjectURL(href)
498
+ downElement.remove()
499
+ }
500
+
501
+ /**
502
+ * 预加载图片资源
503
+ *
504
+ * @param {string[]} arrUrl - 图片URL数组
505
+ * @returns {Promise} 返回一个Promise,当所有图片加载完成时resolve,任意一张图片加载失败时reject
506
+ */
507
+ export function preloadImages(arrUrl) {
508
+ return Promise.all(
509
+ arrUrl.map(
510
+ (url) =>
511
+ new Promise((resolve, reject) => {
512
+ const img = new Image()
513
+ img.onload = () => resolve(img)
514
+ img.onerror = reject
515
+ img.src = url
516
+ })
517
+ )
518
+ )
519
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @FileDescription: 系统ajax请求封装库
3
+ * @Author: yans
4
+ * @Date: 2025-12-26
5
+ * @LastEditors: yans
6
+ * @LastEditTime: 2025-12-30
7
+ */
8
+ import axios from 'axios'
9
+ import { getToken, clearAuth } from '@utils/auth'
10
+ import { tansParamsString } from '@huitu/utils'
11
+ import errorCode from '@utils/errorCode'
12
+
13
+ // 全局默认基础配置
14
+ const _BaseURL = window.HT_EnvConfig.apiUrlNew
15
+ // 请求超时设置
16
+ const _TimeOut = 30000
17
+ // 请求数据大小限制
18
+ const _LimitSize = 5 * 1024 * 1024
19
+
20
+ // 默认请求头参数设置
21
+ axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
22
+
23
+ /**
24
+ * 创建Axios实例的函数
25
+ * @param {Object} config - 配置对象,包含baseURL、timeOut、limitSize等配置项
26
+ * @param {string} [config.baseURL] - 请求的基础URL
27
+ * @param {number} [config.timeOut] - 请求超时时间
28
+ * @param {number} [config.limitSize] - 请求数据大小限制
29
+ * @returns {Object} 返回配置好的Axios实例
30
+ */
31
+ export function createAxiosInstance(config) {
32
+ // 0、基础配置
33
+ const baseURL = config.baseURL || _BaseURL
34
+ // 请求超时设置
35
+ const timeOut = config.timeOut || _TimeOut
36
+ // 请求数据大小限制
37
+ const limitSize = config.limitSize || _LimitSize
38
+
39
+ // 1、创建axios实例
40
+ const service = axios.create({
41
+ // axios中请求配置有baseURL选项,表示请求URL公共部分
42
+ baseURL: baseURL,
43
+ // 超时
44
+ timeout: timeOut,
45
+ })
46
+
47
+ // 2、请求拦截器
48
+ service.interceptors.request.use(
49
+ (config) => {
50
+ console.log('请求参数:', config)
51
+ // 是否需要设置 token
52
+ const isToken = (config.headers || {}).isToken === false
53
+ if (getToken() && !isToken) {
54
+ const token = getToken()
55
+ // 自动添加 Token 到请求头
56
+ // 根据后端要求选择以下方式之一:
57
+ config.headers['Authorization'] = `Bearer ${token}`
58
+
59
+ // 如果需要添加其他认证信息,请在此处添加
60
+ // 例如:config.headers['tenantId'] = 'your-tenant-id'
61
+ }
62
+
63
+ // 处理特殊代理路径(如果需要)
64
+ // 如果请求路径匹配特殊代理规则,则不使用 baseURL
65
+ // 示例:config.url?.startsWith('/special-api') && (config.baseURL = null)
66
+ // 默认使用 baseURL
67
+ if (!config.baseURL) {
68
+ config.baseURL = baseURL
69
+ }
70
+
71
+ // get请求映射params参数
72
+ if (config.method === 'get' && config.params) {
73
+ const url = config.url + '?' + tansParamsString(config.params)
74
+ config.params = {}
75
+ config.url = url
76
+ }
77
+
78
+ // post等请求处理
79
+ if (config.method === 'post' || config.method === 'put') {
80
+ const requestObj = typeof config.data === 'object' ? JSON.stringify(config.data) : config.data // 请求数据
81
+ const requestSize = (requestObj || '').length // 请求数据大小
82
+ if (requestSize >= limitSize) {
83
+ return Promise.reject(new Error('请求数据大小超出允许的5M限制。'))
84
+ }
85
+ }
86
+
87
+ return config
88
+ },
89
+ (error) => {
90
+ console.log('request error:', error)
91
+ return Promise.reject(error)
92
+ }
93
+ )
94
+
95
+ // 3、响应拦截器
96
+ service.interceptors.response.use(
97
+ (res) => {
98
+ // 未设置状态码则默认成功状态
99
+ const code = res.data?.code || 200
100
+ // 获取错误信息
101
+ const message = errorCode[code] || res.data?.msg || errorCode['default']
102
+ // 二进制数据则直接返回
103
+ if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
104
+ // 访问成功
105
+ return res.data
106
+ }
107
+ // debugger
108
+ if (code === 401) {
109
+ // 非法访问
110
+ clearAuth()
111
+ window.location.href = import.meta.env.BASE_URL + '/login'
112
+ return Promise.reject('无效的会话或会话已过期,请重新登录。')
113
+ } else if (code !== 200 && code !== 0 && code !== '0' && code !== 1000) {
114
+ // 后台报错
115
+ console.log('response error:', message)
116
+ return Promise.reject(new Error(message))
117
+ } else {
118
+ // 访问成功
119
+ return res.data
120
+ }
121
+ },
122
+ (error) => {
123
+ console.log('response error:', error)
124
+ let { message } = error
125
+ if (message == 'Network Error') {
126
+ message = '后端接口连接异常'
127
+ } else if (message.includes('timeout')) {
128
+ message = '系统接口请求超时'
129
+ } else if (message.includes('Request failed with status code')) {
130
+ message = '系统接口' + message.substr(message.length - 3) + '异常'
131
+ } else if (message == 'canceled') {
132
+ message = '系统接口canceled异常'
133
+ }
134
+ console.log('response error message:', message)
135
+ return Promise.reject(error)
136
+ }
137
+ )
138
+
139
+ // 返回ajax实例
140
+ return service
141
+ }
142
+
143
+ // ***********创建ajax请求实例*********** //
144
+ export const axiosInstance = createAxiosInstance({
145
+ baseURL: _BaseURL,
146
+ timeOut: _TimeOut,
147
+ limitSize: _LimitSize,
148
+ })
@@ -0,0 +1,20 @@
1
+ /*
2
+ * @Description: '主题切换函数 data-theme属性: light | dark'
3
+ */
4
+
5
+ // 设置主题
6
+ import { cache } from '@huitu/utils'
7
+ const { local } = cache
8
+ export const setTheme = (theme) => {
9
+ local.set('data-theme', theme)
10
+ document.documentElement.setAttribute('data-theme', theme)
11
+ }
12
+ // 获取当前主题
13
+ export const getTheme = () => {
14
+ return local.get('data-theme') || 'light'
15
+ }
16
+ // 初始化主题 默认浅色系
17
+ export const initTheme = () => {
18
+ const theme = getTheme()
19
+ setTheme(theme)
20
+ }
@@ -0,0 +1,6 @@
1
+ <template>
2
+ <div class="about">
3
+ <h1>关于模版</h1>
4
+ <p>此模版旨在快速启动新的前端项目,复用现有的 UI 组件库和工具函数库。</p>
5
+ </div>
6
+ </template>