소스 검색

Merge branch 'base' into tabbar

菲鸽 2 년 전
부모
커밋
5eadda62de

+ 3 - 1
.eslintrc-auto-import.json

@@ -87,6 +87,8 @@
     "watchEffect": true,
     "watchPostEffect": true,
     "watchSyncEffect": true,
-    "useRequest": true
+    "useRequest": true,
+    "useUpload": true,
+    "useUpload2": true
   }
 }

+ 2 - 0
env/.env

@@ -10,3 +10,5 @@ VITE_WX_APPID = 'wxa2abb91f64032a2b'
 # 非h5端只能使用完整的baseurl,否则无法请求,本地proxy只支持h5端
 # VITE_SERVER_BASEURL = '/api'
 VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
+
+VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'

+ 1 - 3
package.json

@@ -96,7 +96,7 @@
     "dayjs": "1.11.10",
     "pinia": "2.0.36",
     "pinia-plugin-persistedstate": "3.2.1",
-    "qs": "^6.11.2",
+    "qs": "6.5.3",
     "vue": "3.3.11",
     "wot-design-uni": "^1.2.13"
   },
@@ -150,8 +150,6 @@
     "unplugin-auto-import": "^0.17.2",
     "vite": "4.3.5",
     "vite-plugin-restart": "^0.4.0",
-    "vite-plugin-svg-icons": "^2.0.1",
-    "vite-svg-loader": "^5.1.0",
     "vue-tsc": "^1.8.25"
   }
 }

+ 6 - 0
src/env.d.ts

@@ -9,9 +9,15 @@ declare module '*.vue' {
 }
 
 interface ImportMetaEnv {
+  /** 网站标题,应用名称 */
   readonly VITE_APP_TITLE: string
+  /** 服务端口号 */
   readonly VITE_SERVER_PORT: string
+  /** 后台接口地址 */
   readonly VITE_SERVER_BASEURL: string
+  /** 上传图片地址 */
+  readonly VITE_UPLOAD_BASEURL: string
+  /** 是否清除console */
   readonly VITE_DELETE_CONSOLE: string
   // 更多环境变量...
 }

+ 80 - 0
src/hooks/useUpload.ts

@@ -0,0 +1,80 @@
+/**
+ * useUpload 是一个定制化的请求钩子,用于处理上传图片。
+ * @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
+ * @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
+ */
+export default function useUpload<T>(formData: Record<string, any> = {}) {
+  const loading = ref(false)
+  const error = ref(false)
+  const data = ref<T>()
+  const url = import.meta.env.VITE_UPLOAD_BASEURL
+  const run = () => {
+    // #ifdef MP-WEIXIN
+    // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
+    // 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
+    uni.chooseMedia({
+      count: 1,
+      mediaType: ['image'],
+      success: (res) => {
+        console.log(res)
+        loading.value = true
+        const tempFilePath = res.tempFiles[0].tempFilePath
+        uni.uploadFile({
+          url,
+          filePath: tempFilePath,
+          name: 'file',
+          formData,
+          success: (uploadFileRes) => {
+            console.log(uploadFileRes.data)
+            data.value = uploadFileRes.data as T
+          },
+          fail: (err) => {
+            console.log('uni.uploadFile err->', err)
+            error.value = true
+          },
+          complete: () => {
+            loading.value = false
+          },
+        })
+      },
+      fail: (err) => {
+        console.log('uni.chooseMedia err->', err)
+        error.value = true
+      },
+    })
+    // #endif
+    // #ifndef MP-WEIXIN
+    uni.chooseImage({
+      count: 1,
+      success: (res) => {
+        console.log(res)
+        loading.value = true
+        const tempFilePath = res.tempFilePaths[0]
+        uni.uploadFile({
+          url,
+          filePath: tempFilePath,
+          name: 'file',
+          formData,
+          success: (uploadFileRes) => {
+            console.log(uploadFileRes.data)
+            data.value = uploadFileRes.data as T
+          },
+          fail: (err) => {
+            console.log('uni.uploadFile err->', err)
+            error.value = true
+          },
+          complete: () => {
+            loading.value = false
+          },
+        })
+      },
+      fail: (err) => {
+        console.log('uni.chooseImage err->', err)
+        error.value = true
+      },
+    })
+    // #endif
+  }
+
+  return { loading, error, data, run }
+}

+ 82 - 0
src/hooks/useUpload2.ts

@@ -0,0 +1,82 @@
+/**
+ * useUpload 是一个定制化的请求钩子,用于处理上传图片。
+ * @param url 上传图片的后台地址,如 https://ukw0y1.laf.run/upload。
+ * 如果上传地址是固定的,那就可以配置到 .env 里面,函数里面不需要再传了。
+ * @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
+ * @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
+ */
+export default function useUpload<T>(url: string, formData: Record<string, any> = {}) {
+  const loading = ref(false)
+  const error = ref(false)
+  const data = ref<T>()
+
+  const run = () => {
+    // #ifdef MP-WEIXIN
+    // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
+    // 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
+    uni.chooseMedia({
+      count: 1,
+      mediaType: ['image'],
+      success: (res) => {
+        console.log(res)
+        loading.value = true
+        const tempFilePath = res.tempFiles[0].tempFilePath
+        uni.uploadFile({
+          url,
+          filePath: tempFilePath,
+          name: 'file',
+          formData,
+          success: (uploadFileRes) => {
+            console.log(uploadFileRes.data)
+            data.value = uploadFileRes.data as T
+          },
+          fail: (err) => {
+            console.log('uni.uploadFile err->', err)
+            error.value = true
+          },
+          complete: () => {
+            loading.value = false
+          },
+        })
+      },
+      fail: (err) => {
+        console.log('uni.chooseMedia err->', err)
+        error.value = true
+      },
+    })
+    // #endif
+    // #ifndef MP-WEIXIN
+    uni.chooseImage({
+      count: 1,
+      success: (res) => {
+        console.log(res)
+        loading.value = true
+        const tempFilePath = res.tempFilePaths[0]
+        uni.uploadFile({
+          url,
+          filePath: tempFilePath,
+          name: 'file',
+          formData,
+          success: (uploadFileRes) => {
+            console.log(uploadFileRes.data)
+            data.value = uploadFileRes.data as T
+          },
+          fail: (err) => {
+            console.log('uni.uploadFile err->', err)
+            error.value = true
+          },
+          complete: () => {
+            loading.value = false
+          },
+        })
+      },
+      fail: (err) => {
+        console.log('uni.chooseImage err->', err)
+        error.value = true
+      },
+    })
+    // #endif
+  }
+
+  return { loading, error, data, run }
+}

+ 17 - 1
src/pages.json

@@ -66,7 +66,23 @@
       "type": "page",
       "layout": "demo",
       "style": {
-        "navigationBarTitleText": "请求"
+        "navigationBarTitleText": "请求-状态一体化"
+      }
+    },
+    {
+      "path": "pages/index/upload",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "上传"
+      }
+    },
+    {
+      "path": "pages/index/upload2",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationBarTitleText": "上传-状态一体化"
       }
     }
   ],

+ 4 - 0
src/pages/index/about.vue

@@ -22,6 +22,10 @@
         去请求页2 (请求状态一体化)
       </wd-button>
     </view>
+    <view class="text-center mt-8 text-#fff">
+      <wd-button type="primary" @click="gotoPage('upload')">上传demo</wd-button>
+      <wd-button type="primary" @click="gotoPage('upload2')">上传demo2(请求状态一体化)</wd-button>
+    </view>
   </view>
   <fg-tabbar />
 </template>

+ 8 - 3
src/pages/index/request2.vue

@@ -2,7 +2,7 @@
 {
   layout: 'demo',
   style: {
-    navigationBarTitleText: '请求',
+    navigationBarTitleText: '请求-状态一体化',
   },
 }
 </route>
@@ -13,15 +13,20 @@
     <button @click="getFoo" class="my-6">测试 GET 请求</button>
     <view class="text-xl">请求数据如下</view>
     <view v-if="loading" class="text-blue h-10">加载中...</view>
+    <view v-if="error" class="text-red h-10">请求错误</view>
     <view v-else class="text-green h-10">{{ JSON.stringify(data) }}</view>
     <button class="my-6" type="warn" @click="reset">一键清空数据</button>
   </view>
 </template>
 
 <script lang="ts" setup>
-import { getFooAPI, IFooItem } from '@/service/index/foo'
+import { getFooAPI2, IFooItem } from '@/service/index/foo'
+import { httpGet } from '@/utils/http'
 
-const { loading, data, run } = useRequest<IFooItem>(() => getFooAPI('菲鸽'), { immediate: true })
+// 适合少部分全局性的接口————多个页面都需要的请求接口,额外编写一个 Service 层
+const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI2('菲鸽'))
+// 再次简化,一行代码搞定,方便一次性的请求,适用大部分请求
+// const { loading, error, data, run } = useRequest<IFooItem>(() => httpGet('/foo', { name: '菲鸽' }))
 
 const getFoo = () => run()
 const reset = () => {

+ 75 - 0
src/pages/index/upload.vue

@@ -0,0 +1,75 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '上传',
+  },
+}
+</route>
+
+<template>
+  <view class="p-4 text-center">
+    <wd-button @click="chooseImage">选择图片并上传</wd-button>
+    <view class="m-2">上传后返回的图片地址:</view>
+    <view class="m-2">{{ imgStr }}</view>
+    <view class="h-80 w-full">
+      <image v-if="imgStr" :src="imgStr" mode="scaleToFill" />
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+const imgStr = ref('')
+
+const chooseImage = () => {
+  // #ifdef MP-WEIXIN
+  // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
+  // 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
+  uni.chooseMedia({
+    count: 1,
+    mediaType: ['image'],
+    success: (res) => {
+      console.log(res)
+      const tempFilePath = res.tempFiles[0].tempFilePath
+      uni.uploadFile({
+        url: 'https://ukw0y1.laf.run/upload',
+        filePath: tempFilePath,
+        name: 'file',
+        formData: {
+          user: '菲鸽',
+        },
+        success: (uploadFileRes) => {
+          console.log(uploadFileRes.data)
+          imgStr.value = uploadFileRes.data
+        },
+      })
+    },
+  })
+  // #endif
+  // #ifndef MP-WEIXIN
+  uni.chooseImage({
+    count: 1,
+    success: (res) => {
+      console.log(res)
+      const tempFilePath = res.tempFilePaths[0]
+      uni.uploadFile({
+        url: 'https://ukw0y1.laf.run/upload',
+        filePath: tempFilePath,
+        name: 'file',
+        formData: {
+          user: '菲鸽',
+        },
+        success: (uploadFileRes) => {
+          console.log(uploadFileRes.data)
+          imgStr.value = uploadFileRes.data
+        },
+      })
+    },
+  })
+  // #endif
+}
+</script>
+
+<style lang="scss" scoped>
+//
+</style>

+ 31 - 0
src/pages/index/upload2.vue

@@ -0,0 +1,31 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationBarTitleText: '上传-状态一体化',
+  },
+}
+</route>
+
+<template>
+  <view class="p-4 text-center">
+    <wd-button @click="run">选择图片并上传</wd-button>
+    <view v-if="loading" class="text-blue h-10">上传...</view>
+    <template v-else>
+      <view class="m-2">上传后返回的图片地址:</view>
+      <view class="m-2">{{ data }}</view>
+      <view class="h-80 w-full">
+        <image v-if="data" :src="data" mode="scaleToFill" />
+      </view>
+    </template>
+  </view>
+</template>
+
+<script lang="ts" setup>
+// const { loading, data, run } = useUpload2<string>('https://ukw0y1.laf.run/upload', { user: '菲鸽' })
+const { loading, data, run } = useUpload<string>({ user: '菲鸽' })
+</script>
+
+<style lang="scss" scoped>
+//
+</style>

+ 0 - 4
src/service/index/foo.d.ts

@@ -1,4 +0,0 @@
-export interface IFooItem {
-  id: string
-  name: string
-}

+ 12 - 6
src/service/index/foo.ts

@@ -1,9 +1,10 @@
-import { http } from '@/utils/http'
-import type { IFooItem } from './foo.d'
-
-export { IFooItem }
+import { http, httpGet } from '@/utils/http'
+export interface IFooItem {
+  id: string
+  name: string
+}
 
-/** get 请求 */
+/** GET 请求 */
 export const getFooAPI = (name: string) => {
   return http<IFooItem>({
     url: `/foo`,
@@ -12,7 +13,12 @@ export const getFooAPI = (name: string) => {
   })
 }
 
-/** get 请求 */
+/** GET 请求 - 再次简化,看大家是否喜欢这种简化 */
+export const getFooAPI2 = (name: string) => {
+  return httpGet<IFooItem>('/foo', { name })
+}
+
+/** POST 请求 */
 export const postFooAPI = (name: string) => {
   return http<IFooItem>({
     url: `/foo`,

+ 30 - 36
src/utils/http.ts

@@ -41,42 +41,36 @@ export const http = <T>(options: CustomRequestOptions) => {
     })
   })
 }
+/**
+ * GET 请求
+ * @param url 后台地址
+ * @param query 请求query参数
+ * @returns
+ */
+export const httpGet = <T>(url: string, query?: Record<string, any>) => {
+  return http<T>({
+    url,
+    query,
+    method: 'GET',
+  })
+}
 
-// uni.uploadFile封装
-export const uniFileUpload = <T>(options: CustomRequestOptions) => {
-  // 1. 返回 Promise 对象
-  return new Promise<IResData<T>>((resolve, reject) => {
-    uni.uploadFile({
-      ...options,
-      // 响应成功
-      success(res) {
-        // 状态码 2xx,参考 axios 的设计
-        if (res.statusCode >= 200 && res.statusCode < 300) {
-          // 文件上传接口的rea.data的类型为string,这里转一下
-          const resData = JSON.parse(res.data) as IResData<T>
-          resolve(resData)
-        } else if (res.statusCode === 401) {
-          // 401错误  -> 清理用户信息,跳转到登录页
-          // userStore.clearUserInfo()
-          // uni.navigateTo({ url: '/pages/login/login' })
-          reject(res)
-        } else {
-          // 其他错误 -> 根据后端错误信息轻提示
-          uni.showToast({
-            icon: 'none',
-            title: '文件上传错误',
-          })
-          reject(res)
-        }
-      },
-      // 响应失败
-      fail(err) {
-        uni.showToast({
-          icon: 'none',
-          title: '网络错误,换个网络试试',
-        })
-        reject(err)
-      },
-    })
+/**
+ * POST 请求
+ * @param url 后台地址
+ * @param data 请求body参数
+ * @param query 请求query参数,post请求也支持query,很多微信接口都需要
+ * @returns
+ */
+export const httpPost = <T>(
+  url: string,
+  data?: Record<string, any>,
+  query?: Record<string, any>,
+) => {
+  return http<T>({
+    url,
+    query,
+    data,
+    method: 'POST',
   })
 }

+ 0 - 6
vite.config.ts

@@ -13,8 +13,6 @@ import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
 import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
 // @see https://unocss.dev/
 import UnoCSS from 'unocss/vite'
-// @see https://github.com/jpkleemans/vite-svg-loader
-import svgLoader from 'vite-svg-loader'
 import AutoImport from 'unplugin-auto-import/vite'
 import { visualizer } from 'rollup-plugin-visualizer'
 import ViteRestart from 'vite-plugin-restart'
@@ -58,10 +56,6 @@ export default ({ command, mode }) => {
       // UniXXX 需要在 Uni 之前引入
       Uni(),
       UnoCSS(),
-      // svg 可以当做组件来使用(Vite plugin to load SVG files as Vue components, using SVGO for optimization.)
-      svgLoader({
-        defaultImport: 'url', // or 'raw'
-      }),
       AutoImport({
         imports: ['vue', 'uni-app'],
         dts: 'src/types/auto-import.d.ts',