create-bubbles 0.1.7 → 0.1.9
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/dist/index.js +4 -3
- package/package.json +1 -1
- package/template-taro-vue-eslint/.editorconfig +12 -0
- package/template-taro-vue-eslint/.env +3 -0
- package/template-taro-vue-eslint/.env.development +5 -0
- package/template-taro-vue-eslint/.env.production +5 -0
- package/template-taro-vue-eslint/.vscode/settings.json +57 -0
- package/template-taro-vue-eslint/babel.config.js +12 -0
- package/template-taro-vue-eslint/commitlint.config.mjs +1 -0
- package/template-taro-vue-eslint/config/dev.ts +27 -0
- package/template-taro-vue-eslint/config/index.ts +129 -0
- package/template-taro-vue-eslint/config/output-root.ts +11 -0
- package/template-taro-vue-eslint/config/prod.ts +39 -0
- package/template-taro-vue-eslint/config/release.ts +113 -0
- package/template-taro-vue-eslint/eslint.config.mjs +22 -0
- package/template-taro-vue-eslint/lefthook.yaml +13 -0
- package/template-taro-vue-eslint/package.json +115 -0
- package/template-taro-vue-eslint/patches/@tarojs__plugin-mini-ci.patch +13 -0
- package/template-taro-vue-eslint/pnpm-workspace.yaml +2 -0
- package/template-taro-vue-eslint/project.config.json +16 -0
- package/template-taro-vue-eslint/src/api/common/upload.ts +53 -0
- package/template-taro-vue-eslint/src/app.config.ts +19 -0
- package/template-taro-vue-eslint/src/app.ts +14 -0
- package/template-taro-vue-eslint/src/assets/image/.gitkeep +0 -0
- package/template-taro-vue-eslint/src/index.html +17 -0
- package/template-taro-vue-eslint/src/pages/example/upload/index.config.ts +3 -0
- package/template-taro-vue-eslint/src/pages/example/upload/index.module.scss +4 -0
- package/template-taro-vue-eslint/src/pages/example/upload/index.vue +71 -0
- package/template-taro-vue-eslint/src/pages/index/index.config.ts +3 -0
- package/template-taro-vue-eslint/src/pages/index/index.module.scss +4 -0
- package/template-taro-vue-eslint/src/pages/index/index.vue +71 -0
- package/template-taro-vue-eslint/src/store/index.ts +10 -0
- package/template-taro-vue-eslint/src/store/modules/user.ts +15 -0
- package/template-taro-vue-eslint/src/store/taroStorage.ts +7 -0
- package/template-taro-vue-eslint/src/styles/index.css +1 -0
- package/template-taro-vue-eslint/src/styles/nut-theme.css +4 -0
- package/template-taro-vue-eslint/src/utils/env.ts +13 -0
- package/template-taro-vue-eslint/src/utils/index.ts +40 -0
- package/template-taro-vue-eslint/src/utils/request/core/index.ts +193 -0
- package/template-taro-vue-eslint/src/utils/request/core/utils.ts +30 -0
- package/template-taro-vue-eslint/src/utils/request/index.ts +73 -0
- package/template-taro-vue-eslint/tsconfig.json +30 -0
- package/template-taro-vue-eslint/types/components.d.ts +12 -0
- package/template-taro-vue-eslint/types/global.d.ts +31 -0
- package/template-taro-vue-eslint/types/vue.d.ts +10 -0
- package/template-taro-vue-eslint/unocss.config.ts +38 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isH5, uploadApiAffix } from '@/utils/env'
|
|
2
|
+
import { alovaUploadRequest } from '@/utils/request'
|
|
3
|
+
|
|
4
|
+
interface UploadBaseParams {
|
|
5
|
+
fileMd5: string
|
|
6
|
+
fileSize: number
|
|
7
|
+
filename: string
|
|
8
|
+
companyId: string
|
|
9
|
+
projectId: string
|
|
10
|
+
indexDbId: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface H5UploadParams extends UploadBaseParams {
|
|
14
|
+
file: Blob
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MpUploadParams extends UploadBaseParams {
|
|
18
|
+
filePath: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type UploadParams = H5UploadParams | MpUploadParams
|
|
22
|
+
|
|
23
|
+
export function uploadFile(data: any) {
|
|
24
|
+
if (isH5) {
|
|
25
|
+
// H5 端用 fetch 发送 FormData,绕过 Taro 适配器的序列化问题
|
|
26
|
+
return fetch(`/${uploadApiAffix}/files/uploadFileAppend`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: data,
|
|
29
|
+
// headers: {
|
|
30
|
+
// 'Content-Type': 'multipart/form-data',
|
|
31
|
+
// authorization: 'Bearer 9a2d60a8-d9a6-40a3-9b13-4288225d855d',
|
|
32
|
+
// },
|
|
33
|
+
}).then(res => res.json())
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// 小程序端:用 Taro uploadFile 适配器
|
|
37
|
+
const { filePath, fileMd5, fileSize, filename, companyId, projectId, indexDbId } = data
|
|
38
|
+
return alovaUploadRequest.Post(`/files/uploadFileAppend`, {
|
|
39
|
+
name: 'file',
|
|
40
|
+
filePath,
|
|
41
|
+
fileMd5,
|
|
42
|
+
fileSize,
|
|
43
|
+
filename,
|
|
44
|
+
companyId,
|
|
45
|
+
projectId,
|
|
46
|
+
indexDbId,
|
|
47
|
+
}, {
|
|
48
|
+
requestType: 'upload',
|
|
49
|
+
fileName: filename,
|
|
50
|
+
meta: { isWrapped: false },
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export default defineAppConfig({
|
|
2
|
+
pages: [
|
|
3
|
+
'pages/index/index',
|
|
4
|
+
],
|
|
5
|
+
subPackages: [
|
|
6
|
+
{
|
|
7
|
+
root: 'pages/example',
|
|
8
|
+
pages: [
|
|
9
|
+
'upload/index',
|
|
10
|
+
],
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
window: {
|
|
14
|
+
backgroundTextStyle: 'light',
|
|
15
|
+
navigationBarBackgroundColor: '#fff',
|
|
16
|
+
navigationBarTitleText: 'WeChat',
|
|
17
|
+
navigationBarTextStyle: 'black',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createApp } from 'vue'
|
|
2
|
+
|
|
3
|
+
import { setupStore } from './store'
|
|
4
|
+
import '@nutui/touch-emulator'
|
|
5
|
+
import 'uno.css'
|
|
6
|
+
import '@/styles/index.css'
|
|
7
|
+
|
|
8
|
+
const App = createApp({
|
|
9
|
+
onShow(_options) {},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
setupStore(App)
|
|
13
|
+
|
|
14
|
+
export default App
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
5
|
+
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
|
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="apple-touch-fullscreen" content="yes">
|
|
8
|
+
<meta name="format-detection" content="telephone=no,address=no">
|
|
9
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="white">
|
|
10
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
|
|
11
|
+
<title>myApp</title>
|
|
12
|
+
<script><%= htmlWebpackPlugin.options.script %></script>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="app"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import Taro from '@tarojs/taro'
|
|
3
|
+
import { uploadFile } from '@/api/common/upload'
|
|
4
|
+
import { canvasToFile, file2Md5 } from '@/utils'
|
|
5
|
+
import { isH5 } from '@/utils/env'
|
|
6
|
+
|
|
7
|
+
async function handleConfirm(canvas: any, data: string) {
|
|
8
|
+
if (isH5) {
|
|
9
|
+
const file = await canvasToFile(canvas)
|
|
10
|
+
const fileMd5 = await file2Md5(file)
|
|
11
|
+
const data = new FormData()
|
|
12
|
+
data.append('file', file)
|
|
13
|
+
data.append('companyId', 'c35bd5e0d5834eccb1cfbf4dd538eb61')
|
|
14
|
+
data.append('projectId', 'eb42a2b7e680a4124e78951ccf7f1268')
|
|
15
|
+
data.append('indexDbId', '0')
|
|
16
|
+
data.append('fileMd5', fileMd5)
|
|
17
|
+
data.append('fileSize', `${file.size}`)
|
|
18
|
+
data.append('filename', file.name)
|
|
19
|
+
try {
|
|
20
|
+
const res = await uploadFile(data)
|
|
21
|
+
if (res.code === 200) {
|
|
22
|
+
// console.log('💦res.data', res.data)
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
Taro.showToast({
|
|
26
|
+
title: res.message,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
Taro.showToast({
|
|
32
|
+
title: '服务异常',
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// 小程序端:用 Taro uploadFile 适配器
|
|
38
|
+
Taro.getFileSystemManager().getFileInfo({
|
|
39
|
+
filePath: data,
|
|
40
|
+
success: async (res) => {
|
|
41
|
+
const params = {
|
|
42
|
+
filePath: data,
|
|
43
|
+
companyId: 'c35bd5e0d5834eccb1cfbf4dd538eb61',
|
|
44
|
+
projectId: 'eb42a2b7e680a4124e78951ccf7f1268',
|
|
45
|
+
indexDbId: '0',
|
|
46
|
+
fileMd5: res.digest,
|
|
47
|
+
fileSize: res.size,
|
|
48
|
+
filename: `${res.digest}.png`,
|
|
49
|
+
}
|
|
50
|
+
const uploadResStr = await uploadFile(params)
|
|
51
|
+
const uploadRes = JSON.parse(uploadResStr)
|
|
52
|
+
if (uploadRes.code === 200) {
|
|
53
|
+
// console.log('💦uploadRes.data', uploadRes.data)
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
Taro.showToast({
|
|
57
|
+
title: uploadRes.message,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<div class="text-red">
|
|
68
|
+
1112
|
|
69
|
+
<nut-signature @confirm="handleConfirm" />
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import Taro from '@tarojs/taro'
|
|
3
|
+
import { uploadFile } from '@/api/common/upload'
|
|
4
|
+
import { canvasToFile, file2Md5 } from '@/utils'
|
|
5
|
+
import { isH5 } from '@/utils/env'
|
|
6
|
+
|
|
7
|
+
async function handleConfirm(canvas: any, data: string) {
|
|
8
|
+
if (isH5) {
|
|
9
|
+
const file = await canvasToFile(canvas)
|
|
10
|
+
const fileMd5 = await file2Md5(file)
|
|
11
|
+
const data = new FormData()
|
|
12
|
+
data.append('file', file)
|
|
13
|
+
data.append('companyId', 'c35bd5e0d5834eccb1cfbf4dd538eb61')
|
|
14
|
+
data.append('projectId', 'eb42a2b7e680a4124e78951ccf7f1268')
|
|
15
|
+
data.append('indexDbId', '0')
|
|
16
|
+
data.append('fileMd5', fileMd5)
|
|
17
|
+
data.append('fileSize', `${file.size}`)
|
|
18
|
+
data.append('filename', file.name)
|
|
19
|
+
try {
|
|
20
|
+
const res = await uploadFile(data)
|
|
21
|
+
if (res.code === 200) {
|
|
22
|
+
// console.log('💦res.data', res.data)
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
Taro.showToast({
|
|
26
|
+
title: res.message,
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
Taro.showToast({
|
|
32
|
+
title: '服务异常',
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// 小程序端:用 Taro uploadFile 适配器
|
|
38
|
+
Taro.getFileSystemManager().getFileInfo({
|
|
39
|
+
filePath: data,
|
|
40
|
+
success: async (res) => {
|
|
41
|
+
const params = {
|
|
42
|
+
filePath: data,
|
|
43
|
+
companyId: 'c35bd5e0d5834eccb1cfbf4dd538eb61',
|
|
44
|
+
projectId: 'eb42a2b7e680a4124e78951ccf7f1268',
|
|
45
|
+
indexDbId: '0',
|
|
46
|
+
fileMd5: res.digest,
|
|
47
|
+
fileSize: res.size,
|
|
48
|
+
filename: `${res.digest}.png`,
|
|
49
|
+
}
|
|
50
|
+
const uploadResStr = await uploadFile(params)
|
|
51
|
+
const uploadRes = JSON.parse(uploadResStr)
|
|
52
|
+
if (uploadRes.code === 200) {
|
|
53
|
+
// console.log('💦uploadRes.data', uploadRes.data)
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
Taro.showToast({
|
|
57
|
+
title: uploadRes.message,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<div class="text-red">
|
|
68
|
+
1112
|
|
69
|
+
<nut-signature @confirm="handleConfirm" />
|
|
70
|
+
</div>
|
|
71
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { App } from 'vue'
|
|
2
|
+
import { createPinia } from 'pinia'
|
|
3
|
+
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
|
4
|
+
|
|
5
|
+
export const store = createPinia()
|
|
6
|
+
store.use(piniaPluginPersistedstate)
|
|
7
|
+
|
|
8
|
+
export function setupStore(app: App) {
|
|
9
|
+
app.use(store)
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { TaroStorage } from '../taroStorage'
|
|
4
|
+
|
|
5
|
+
export const useUserStore = defineStore('user', () => {
|
|
6
|
+
const user = ref('1')
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
user,
|
|
10
|
+
}
|
|
11
|
+
}, {
|
|
12
|
+
persist: {
|
|
13
|
+
storage: TaroStorage,
|
|
14
|
+
},
|
|
15
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import './nut-theme.css';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const apiUrl = process.env.TARO_APP_API_URL
|
|
2
|
+
const apiAffix = process.env.TARO_APP_API_AFFIX
|
|
3
|
+
const uploadApiAffix = process.env.TARO_APP_UPLOAD_API_AFFIX
|
|
4
|
+
const weappId = process.env.TARO_APP_ID
|
|
5
|
+
const isH5 = process.env.TARO_ENV === 'h5'
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
apiAffix,
|
|
9
|
+
apiUrl,
|
|
10
|
+
isH5,
|
|
11
|
+
uploadApiAffix,
|
|
12
|
+
weappId,
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Taro from '@tarojs/taro'
|
|
2
|
+
import SparkMD5 from 'spark-md5'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* H5 端:canvas 元素转 File
|
|
6
|
+
*/
|
|
7
|
+
export function canvasToFile(canvas, filename = 'image.png', mimeType = 'image/png', quality = 1): Promise<File> {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
canvas.toBlob((blob) => {
|
|
10
|
+
const file = new File([blob], filename, { type: mimeType })
|
|
11
|
+
resolve(file)
|
|
12
|
+
}, mimeType, quality)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 获取本地文件信息(小程序端)
|
|
18
|
+
*/
|
|
19
|
+
export function getFileInfo(filePath: string): Promise<Taro.getFileInfo.SuccessCallbackResult> {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
Taro.getFileInfo({
|
|
22
|
+
filePath,
|
|
23
|
+
success: resolve,
|
|
24
|
+
fail: reject,
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function file2Md5(file): Promise<string> {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const reader = new FileReader()
|
|
32
|
+
reader.onload = (e: ProgressEvent<FileReader>) => {
|
|
33
|
+
const spark = new SparkMD5.ArrayBuffer()
|
|
34
|
+
spark.append(e.target?.result)
|
|
35
|
+
resolve(spark.end())
|
|
36
|
+
}
|
|
37
|
+
reader.onerror = reject
|
|
38
|
+
reader.readAsArrayBuffer(file)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import type { TaroConfig } from '@alova/adapter-taro'
|
|
2
|
+
import type Taro from '@tarojs/taro'
|
|
3
|
+
import type {
|
|
4
|
+
AlovaGlobalCacheAdapter,
|
|
5
|
+
AlovaOptions,
|
|
6
|
+
AlovaRequestAdapter,
|
|
7
|
+
GlobalCacheConfig,
|
|
8
|
+
StatesExport,
|
|
9
|
+
StatesHook,
|
|
10
|
+
} from 'alova'
|
|
11
|
+
import type { FetchRequestInit } from 'alova/fetch'
|
|
12
|
+
import { createAlova } from 'alova'
|
|
13
|
+
import adapterFetch from 'alova/fetch'
|
|
14
|
+
import { deepMergeObject, isReadableStream } from './utils'
|
|
15
|
+
|
|
16
|
+
// ---- Taro 适配器的响应类型 ----
|
|
17
|
+
type TaroResponse
|
|
18
|
+
= | Taro.request.SuccessCallbackResult<any>
|
|
19
|
+
| Taro.uploadFile.SuccessCallbackResult
|
|
20
|
+
| Taro.downloadFile.FileSuccessCallbackResult
|
|
21
|
+
|
|
22
|
+
type TaroResponseHeader = Taro.request.SuccessCallbackResult<any>['header']
|
|
23
|
+
|
|
24
|
+
// ---- 支持的适配器联合类型 ----
|
|
25
|
+
type SupportedRequestConfig = TaroConfig | FetchRequestInit
|
|
26
|
+
type SupportedResponse = TaroResponse | Response
|
|
27
|
+
type SupportedResponseHeader = TaroResponseHeader | Headers
|
|
28
|
+
|
|
29
|
+
interface StatusMap {
|
|
30
|
+
success?: number
|
|
31
|
+
unAuthorized?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface CodeMap {
|
|
35
|
+
success?: number[]
|
|
36
|
+
unAuthorized?: number[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface BaseRequestOption<
|
|
40
|
+
RC extends SupportedRequestConfig = SupportedRequestConfig,
|
|
41
|
+
RE extends SupportedResponse = SupportedResponse,
|
|
42
|
+
RH extends SupportedResponseHeader = SupportedResponseHeader,
|
|
43
|
+
SE extends StatesExport<any> = StatesExport<any>,
|
|
44
|
+
> {
|
|
45
|
+
baseUrl?: string
|
|
46
|
+
timeout?: number
|
|
47
|
+
commonHeaders?: Record<string, string | (() => string)>
|
|
48
|
+
statusMap?: StatusMap
|
|
49
|
+
isWrapped?: boolean
|
|
50
|
+
cacheFor?: GlobalCacheConfig<any>
|
|
51
|
+
cacheLogger?: boolean
|
|
52
|
+
codeMap?: CodeMap
|
|
53
|
+
responseDataKey?: string
|
|
54
|
+
responseMessageKey?: string
|
|
55
|
+
isTransformResponse?: boolean
|
|
56
|
+
isShowSuccessMessage?: boolean
|
|
57
|
+
successDefaultMessage?: string
|
|
58
|
+
isShowErrorMessage?: boolean
|
|
59
|
+
errorDefaultMessage?: string
|
|
60
|
+
statesHook?: StatesHook<SE>
|
|
61
|
+
successMessageFunc?: (message: string) => void
|
|
62
|
+
errorMessageFunc?: (message: string) => void
|
|
63
|
+
unAuthorizedResponseFunc?: () => void
|
|
64
|
+
requestAdapter?: AlovaRequestAdapter<RC, RE, RH>
|
|
65
|
+
storageAdapter?: AlovaGlobalCacheAdapter
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** method.meta 中可按请求覆盖的字段 */
|
|
69
|
+
export interface RequestMeta {
|
|
70
|
+
isTransformResponse?: boolean
|
|
71
|
+
isShowSuccessMessage?: boolean
|
|
72
|
+
isShowErrorMessage?: boolean
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getMetaFlag(meta: Record<string, any> | undefined, key: string, fallback: boolean): boolean {
|
|
76
|
+
if (meta && typeof meta[key] === 'boolean')
|
|
77
|
+
return meta[key]
|
|
78
|
+
return fallback
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function createInstance<
|
|
82
|
+
RC extends SupportedRequestConfig = SupportedRequestConfig,
|
|
83
|
+
RE extends SupportedResponse = SupportedResponse,
|
|
84
|
+
RH extends SupportedResponseHeader = SupportedResponseHeader,
|
|
85
|
+
SE extends StatesExport<any> = StatesExport<any>,
|
|
86
|
+
>(option: BaseRequestOption<RC, RE, RH, SE>) {
|
|
87
|
+
const defaultOption: BaseRequestOption = {
|
|
88
|
+
baseUrl: '/',
|
|
89
|
+
timeout: 0,
|
|
90
|
+
statusMap: { success: 200, unAuthorized: 401 },
|
|
91
|
+
isWrapped: true,
|
|
92
|
+
cacheFor: null,
|
|
93
|
+
cacheLogger: true,
|
|
94
|
+
codeMap: { success: [200], unAuthorized: [401] },
|
|
95
|
+
responseDataKey: 'data',
|
|
96
|
+
responseMessageKey: 'message',
|
|
97
|
+
isTransformResponse: true,
|
|
98
|
+
isShowSuccessMessage: false,
|
|
99
|
+
successDefaultMessage: '操作成功',
|
|
100
|
+
isShowErrorMessage: true,
|
|
101
|
+
errorDefaultMessage: '服务异常',
|
|
102
|
+
requestAdapter: adapterFetch() as AlovaRequestAdapter<any, any, any>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const config = deepMergeObject(defaultOption, option) as Required<
|
|
106
|
+
Pick<BaseRequestOption, 'statusMap' | 'codeMap' | 'responseDataKey' | 'responseMessageKey'>
|
|
107
|
+
> & BaseRequestOption<RC, RE, RH, SE>
|
|
108
|
+
|
|
109
|
+
const alovaOptions: AlovaOptions<{
|
|
110
|
+
Responded: any
|
|
111
|
+
Transformed: any
|
|
112
|
+
RequestConfig: RC
|
|
113
|
+
Response: RE
|
|
114
|
+
ResponseHeader: RH
|
|
115
|
+
L1Cache: AlovaGlobalCacheAdapter
|
|
116
|
+
L2Cache: AlovaGlobalCacheAdapter
|
|
117
|
+
StatesExport: SE
|
|
118
|
+
}> = {
|
|
119
|
+
baseURL: config.baseUrl,
|
|
120
|
+
timeout: config.timeout,
|
|
121
|
+
cacheFor: config.cacheFor as any,
|
|
122
|
+
cacheLogger: config.cacheLogger,
|
|
123
|
+
statesHook: config.statesHook,
|
|
124
|
+
l2Cache: config.storageAdapter,
|
|
125
|
+
requestAdapter: config.requestAdapter!,
|
|
126
|
+
beforeRequest: async (method) => {
|
|
127
|
+
for (const [key, value] of Object.entries(config.commonHeaders ?? {})) {
|
|
128
|
+
method.config.headers[key] = typeof value === 'function' ? value() : value
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
responded: {
|
|
132
|
+
onSuccess: async (response, method) => {
|
|
133
|
+
const meta = method.meta as Record<string, any> | undefined
|
|
134
|
+
const shouldTransform = getMetaFlag(meta, 'isTransformResponse', config.isTransformResponse ?? true)
|
|
135
|
+
const showSuccess = getMetaFlag(meta, 'isShowSuccessMessage', config.isShowSuccessMessage ?? false)
|
|
136
|
+
const showError = getMetaFlag(meta, 'isShowErrorMessage', config.isShowErrorMessage ?? true)
|
|
137
|
+
const isWrapped = getMetaFlag(meta, 'isWrapped', config.isWrapped ?? true)
|
|
138
|
+
|
|
139
|
+
if (!shouldTransform)
|
|
140
|
+
return response
|
|
141
|
+
|
|
142
|
+
// 兼容 fetch (status) 和 Taro (statusCode)
|
|
143
|
+
const status = (response as any).statusCode ?? (response as any).status
|
|
144
|
+
// 兼容 fetch (body 是 ReadableStream) 和 Taro (data 直接可用)
|
|
145
|
+
const data
|
|
146
|
+
= (response as any)?.body && isReadableStream((response as any).body)
|
|
147
|
+
? await (response as Response).json()
|
|
148
|
+
: (response as any).data ?? response
|
|
149
|
+
|
|
150
|
+
if (status !== config.statusMap.success) {
|
|
151
|
+
if (config.statusMap.unAuthorized === status)
|
|
152
|
+
config.unAuthorizedResponseFunc?.()
|
|
153
|
+
if (showError)
|
|
154
|
+
config.errorMessageFunc?.(config.errorDefaultMessage ?? '服务异常')
|
|
155
|
+
return Promise.reject(response)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!isWrapped || (isWrapped === undefined || !config.isWrapped)) {
|
|
159
|
+
if (showSuccess)
|
|
160
|
+
config.successMessageFunc?.(config.successDefaultMessage ?? '操作成功')
|
|
161
|
+
return data
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const code = data?.code
|
|
165
|
+
const responseData = data?.[config.responseDataKey]
|
|
166
|
+
const responseMessage = data?.[config.responseMessageKey]
|
|
167
|
+
|
|
168
|
+
if (!config.codeMap.success?.includes(+code)) {
|
|
169
|
+
if (config.codeMap.unAuthorized?.includes(+code)) {
|
|
170
|
+
config.unAuthorizedResponseFunc?.()
|
|
171
|
+
return Promise.reject(response)
|
|
172
|
+
}
|
|
173
|
+
if (showError)
|
|
174
|
+
config.errorMessageFunc?.(responseMessage ?? config.errorDefaultMessage ?? '服务异常')
|
|
175
|
+
return Promise.reject(response)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (showSuccess)
|
|
179
|
+
config.successMessageFunc?.(responseMessage ?? config.successDefaultMessage)
|
|
180
|
+
return responseData
|
|
181
|
+
},
|
|
182
|
+
onError: (error, method) => {
|
|
183
|
+
const meta = method.meta as Record<string, any> | undefined
|
|
184
|
+
const showError = getMetaFlag(meta, 'isShowErrorMessage', config.isShowErrorMessage ?? true)
|
|
185
|
+
if (showError)
|
|
186
|
+
config.errorMessageFunc?.(config.errorDefaultMessage ?? error.message)
|
|
187
|
+
return Promise.reject(error)
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return createAlova(alovaOptions)
|
|
193
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function deepMergeObject<T = any>(source: T, target: Partial<T>): T {
|
|
2
|
+
const isObject = (obj: any): obj is Record<string, any> =>
|
|
3
|
+
obj && typeof obj === 'object' && !Array.isArray(obj)
|
|
4
|
+
|
|
5
|
+
const merge = (src: any, tgt: any): any => {
|
|
6
|
+
const result = { ...src }
|
|
7
|
+
if (isObject(result) && isObject(tgt)) {
|
|
8
|
+
Object.keys(tgt).forEach((key) => {
|
|
9
|
+
if (isObject(tgt[key])) {
|
|
10
|
+
result[key] = merge(result[key] || {}, tgt[key])
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
result[key] = tgt[key]
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
return result
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return merge(source, target)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 判断一个变量是不是可读流
|
|
25
|
+
*/
|
|
26
|
+
export function isReadableStream(data: unknown): boolean {
|
|
27
|
+
if (typeof ReadableStream === 'undefined')
|
|
28
|
+
return false
|
|
29
|
+
return data instanceof ReadableStream
|
|
30
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { TaroConfig } from '@alova/adapter-taro'
|
|
2
|
+
import type { VueHookExportType } from 'alova/vue'
|
|
3
|
+
import AdapterTaroVue from '@alova/adapter-taro/vue'
|
|
4
|
+
import Taro from '@tarojs/taro'
|
|
5
|
+
import { apiAffix, apiUrl, isH5, uploadApiAffix } from '@/utils/env'
|
|
6
|
+
import { createInstance } from './core'
|
|
7
|
+
|
|
8
|
+
type TaroResponse
|
|
9
|
+
= | Taro.request.SuccessCallbackResult<any>
|
|
10
|
+
| Taro.uploadFile.SuccessCallbackResult
|
|
11
|
+
| Taro.downloadFile.FileSuccessCallbackResult
|
|
12
|
+
|
|
13
|
+
type TaroResponseHeader = Taro.request.SuccessCallbackResult<any>['header']
|
|
14
|
+
|
|
15
|
+
const taroAdapter = AdapterTaroVue()
|
|
16
|
+
|
|
17
|
+
const alovaRequest = createInstance<
|
|
18
|
+
TaroConfig,
|
|
19
|
+
TaroResponse,
|
|
20
|
+
TaroResponseHeader,
|
|
21
|
+
VueHookExportType<unknown>
|
|
22
|
+
>({
|
|
23
|
+
baseUrl: isH5 ? `/${apiAffix}` : `${apiUrl}/${apiAffix}`,
|
|
24
|
+
statusMap: { success: 200, unAuthorized: 401 },
|
|
25
|
+
codeMap: { success: [200] },
|
|
26
|
+
responseDataKey: 'data',
|
|
27
|
+
responseMessageKey: 'msg',
|
|
28
|
+
commonHeaders: {},
|
|
29
|
+
successMessageFunc: (msg) => {
|
|
30
|
+
Taro.showToast({ title: msg })
|
|
31
|
+
},
|
|
32
|
+
errorMessageFunc: (msg) => {
|
|
33
|
+
Taro.showToast({ title: msg, icon: 'error' })
|
|
34
|
+
},
|
|
35
|
+
unAuthorizedResponseFunc: () => {
|
|
36
|
+
Taro.showToast({ title: '登录过期或未登录' })
|
|
37
|
+
Taro.navigateTo({ url: '/pages/login/index' })
|
|
38
|
+
},
|
|
39
|
+
statesHook: taroAdapter.statesHook,
|
|
40
|
+
requestAdapter: taroAdapter.requestAdapter,
|
|
41
|
+
storageAdapter: taroAdapter.storageAdapter,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
export default alovaRequest
|
|
45
|
+
|
|
46
|
+
const alovaUploadRequest = createInstance<
|
|
47
|
+
TaroConfig,
|
|
48
|
+
TaroResponse,
|
|
49
|
+
TaroResponseHeader,
|
|
50
|
+
VueHookExportType<unknown>
|
|
51
|
+
>({
|
|
52
|
+
baseUrl: isH5 ? `/${uploadApiAffix}` : `${apiUrl}/${uploadApiAffix}`,
|
|
53
|
+
statusMap: { success: 200, unAuthorized: 401 },
|
|
54
|
+
codeMap: { success: [200] },
|
|
55
|
+
responseDataKey: 'data',
|
|
56
|
+
responseMessageKey: 'msg',
|
|
57
|
+
commonHeaders: {},
|
|
58
|
+
successMessageFunc: (msg) => {
|
|
59
|
+
Taro.showToast({ title: msg })
|
|
60
|
+
},
|
|
61
|
+
errorMessageFunc: (msg) => {
|
|
62
|
+
Taro.showToast({ title: msg, icon: 'error' })
|
|
63
|
+
},
|
|
64
|
+
unAuthorizedResponseFunc: () => {
|
|
65
|
+
Taro.showToast({ title: '登录过期或未登录' })
|
|
66
|
+
Taro.navigateTo({ url: '/pages/login/index' })
|
|
67
|
+
},
|
|
68
|
+
statesHook: taroAdapter.statesHook,
|
|
69
|
+
requestAdapter: taroAdapter.requestAdapter,
|
|
70
|
+
storageAdapter: taroAdapter.storageAdapter,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export { alovaUploadRequest }
|