15 files modified
4 files deleted
1 files added
| | |
| | | # 掌控智飞 |
| | | |
| | | ``` |
| | | npm install pnpm -g |
| | | pnpm install |
| | | pnpm run dev |
| | | ``` |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | # uniapp 团队协作开发实践模板(Vue3) |
| | | |
| | | [](https://github.com/oyjt/uniapp-vue3-template) |
| | |
| | | 5. `shadcn-ui` 分支采用最新的 `tailwindcss v4.1` 版本,因为现阶段的 `unocss` 对于最新版 `tailwindcss` 支持还不够完善。 |
| | | `shadcn-ui`并不太适合移动端使用,如果不喜欢可以移除,只保留纯净的框架。 |
| | | |
| | | ### 捐赠 |
| | | |
| | | 如果你觉得这个项目对你有帮助,你可以请作者喝饮料🍹 |
| | | |
| | | <p align='center'> |
| | | <img alt="微信收款码" src="./src/static/images/pay.png" height="330" style="display:inline-block; height:330px;"> |
| | | </p> |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="en"> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="UTF-8" /> |
| | | <script> |
| | |
| | | "name": "uniapp-vue3-project", |
| | | "type": "module", |
| | | "version": "1.5.0", |
| | | "description": "uniapp 团队协作开发实践模板(Vue3)", |
| | | "description": "掌控智飞", |
| | | "author": { |
| | | "name": "江阳小道", |
| | | "email": "oyjt001@gmail.com", |
| | | "github": "https://github.com/oyjt" |
| | | "name": "ztzf" |
| | | }, |
| | | "license": "MIT", |
| | | "homepage": "https://github.com/oyjt/uniapp-vue3-template", |
| | |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2025-09-28 10:07:55 |
| | | * @FilePath : \src\App.vue |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-09-28 09:31:16 |
| | | --> |
| | | <script setup> |
| | | import { onHide, onLaunch, onShow } from "@dcloudio/uni-app"; |
| | | import { useAppStore } from "@/store"; |
| | | import { mpUpdate } from "@/utils/index"; |
| | | import {mpUpdate} from "@/utils/index"; |
| | | |
| | | const appStore = useAppStore(); |
| | | |
| | | onLaunch(() => { |
| | | console.log("App Launch"); |
| | | |
| New file |
| | |
| | | import {request} from "@/utils/request" |
| | | |
| | | export const getDeviceRegionApi = data => { |
| | | return request({ |
| | | url: '/drone-device-core/manage/api/v1/devices/getDeviceRegion', |
| | | method: 'post', |
| | | data: { hidden_flag: 0, ...data }, |
| | | }) |
| | | } |
| | |
| | | |
| | | import website from '@/config/website' |
| | | |
| | | /** 获取用户信息 */ |
| | | export const profile = params => get("/user/profile", { |
| | | params |
| | | }) |
| | | |
| | | /** 登录 */ |
| | | export const login = data => |
| | | post("/user/login", { |
| | |
| | | export const logout = () => post("/user/logout") |
| | | |
| | | // 用户登录接口 |
| | | export const loginByUsername = (tenantId, deptId, roleId, username, password, type, key, code) => |
| | | request({ |
| | | export const loginByUsername = (tenantId, deptId, roleId, username, password, type, key, code) => { |
| | | return request({ |
| | | url: '/blade-auth/oauth/token', |
| | | method: 'post', |
| | | header: { |
| | |
| | | type, |
| | | }, |
| | | }) |
| | | } |
| | |
| | | <template> |
| | | <view> |
| | | <view class="login-form-wrap"> |
| | | <view class="title"> 欢迎登录 </view> |
| | | <input v-model="tel" class="u-border-bottom" type="number" placeholder="请输入手机号" /> |
| | | <view class="u-border-bottom my-40rpx flex"> |
| | | <input v-model="code" class="flex-1" type="number" placeholder="请输入验证码" /> |
| | | <view> |
| | | <u-code ref="uCodeRef" @change="codeChange" /> |
| | | <u-button :text="tips" type="success" size="mini" @click="getCode" /> |
| | | </view> |
| | | </view> |
| | | <view class="title"> 掌控智飞</view> |
| | | <input v-model="loginForm.username" class="u-border-bottom" placeholder="请输入用户名"/> |
| | | <input v-model="loginForm.password" class="u-border-bottom" type="password" placeholder="请输入密码"/> |
| | | <button class="login-btn" :style="[inputStyle]" @tap="submit"> |
| | | 登录 <text class="i-mdi-login" /> |
| | | 登录 |
| | | <text class="i-mdi-login"/> |
| | | </button> |
| | | |
| | | <view class="alternative"> |
| | | <view class="password"> 密码登录 </view> |
| | | <view class="issue flex items-center"> |
| | | 遇到问题 <text class="i-mdi-help" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view class="login-type-wrap"> |
| | | <view class="item wechat"> |
| | | <view class="icon"> |
| | | <u-icon size="35" name="weixin-fill" color="rgb(83,194,64)" /> |
| | | </view> |
| | | 微信 |
| | | </view> |
| | | <view class="item QQ"> |
| | | <view class="icon"> |
| | | <u-icon size="35" name="qq-fill" color="rgb(17,183,233)" /> |
| | | </view> |
| | | QQ |
| | | </view> |
| | | </view> |
| | | <view class="hint"> |
| | | 登录代表同意 |
| | | <text class="link"> 用户协议、隐私政策, </text> |
| | | 并授权使用您的账号信息(如昵称、头像、收获地址)以便您统一管理 |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import md5 from 'js-md5' |
| | | import { |
| | | loginByUsername |
| | | } from '@/api/user/index.js' |
| | | import { |
| | | HOME_PATH, |
| | | isTabBarPath, |
| | | LOGIN_PATH, |
| | | removeQueryString, |
| | | } from "@/router"; |
| | | import { |
| | | setToken |
| | | } from "@/utils/auth"; |
| | | // import { useUserStore } from '@/store'; |
| | | |
| | | // const userStore = useUserStore(); |
| | | const tel = ref("18502811111"); |
| | | const code = ref("1234"); |
| | | const tips = ref(); |
| | | const uCodeRef = ref(null); |
| | | let redirect = HOME_PATH; |
| | | |
| | | const inputStyle = computed(() => { |
| | | const style = {}; |
| | | if (tel.value && code.value) { |
| | | style.color = "#fff"; |
| | | style.backgroundColor = uni.$u.color.warning; |
| | | } |
| | | return style; |
| | | }); |
| | | |
| | | function codeChange(text) { |
| | | tips.value = text; |
| | | import md5 from 'js-md5' |
| | | import {loginByUsername} from '@/api/user/index.js' |
| | | import {useUserStore} from "@/store/index.js"; |
| | | import { |
| | | HOME_PATH, LOGIN_PATH, removeQueryString, |
| | | } from "@/router"; |
| | | const userStore = useUserStore(); |
| | | const loginForm = ref({ |
| | | username: "shuishen", |
| | | password: "Dashabi...." |
| | | }) |
| | | const inputStyle = computed(() => { |
| | | const style = {}; |
| | | if (loginForm.value.username && loginForm.value.password) { |
| | | style.color = "#fff"; |
| | | style.backgroundColor = uni.$u.color.warning; |
| | | } |
| | | return style; |
| | | }); |
| | | let redirect = HOME_PATH; |
| | | |
| | | function getCode() { |
| | | if (uCodeRef.value?.canGetCode) { |
| | | // 模拟向后端请求验证码 |
| | | uni.showLoading({ |
| | | title: "正在获取验证码", |
| | | }); |
| | | setTimeout(() => { |
| | | uni.hideLoading(); |
| | | uni.$u.toast("验证码已发送"); |
| | | // 通知验证码组件内部开始倒计时 |
| | | uCodeRef.value?.start(); |
| | | }, 1000); |
| | | } else { |
| | | uni.$u.toast("倒计时结束后再发送"); |
| | | } |
| | | async function submit() { |
| | | let userInfo = { |
| | | "tenantId": "000000", |
| | | "deptId": "", |
| | | "roleId": "", |
| | | "username": loginForm.value.username, |
| | | "password": loginForm.value.password, |
| | | "type": "account", |
| | | "code": "", |
| | | "key": "", |
| | | } |
| | | async function submit() { |
| | | if (!uni.$u.test.mobile(Number(tel.value))) { |
| | | uni.$u.toast("请输入正确的手机号"); |
| | | return; |
| | | } |
| | | if (!code.value) { |
| | | uni.$u.toast("请输入验证码"); |
| | | return; |
| | | } |
| | | // 登录请求 |
| | | // const res = await userStore.login({ phone: tel.value, code: code.value }).catch(() => { |
| | | // uni.$u.toast('登录失败'); |
| | | // }); |
| | | // if (!res) return; |
| | | setToken("1234567890"); |
| | | setTimeout(() => { |
| | | uni.$u.route({ |
| | | type: isTabBarPath(redirect) ? "switchTab" : "redirectTo", |
| | | url: redirect, |
| | | }); |
| | | }, 800); |
| | | |
| | | let userInfo = { |
| | | "tenantId": "000000", |
| | | "deptId": "", |
| | | "roleId": "", |
| | | "username": "shuishen", |
| | | "password": "Dashabi....", |
| | | "type": "account", |
| | | "code": "", |
| | | "key": "", |
| | | "image": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" |
| | | } |
| | | |
| | | loginByUsername( |
| | | userInfo.tenantId, |
| | | userInfo.deptId, |
| | | userInfo.roleId, |
| | | userInfo.username, |
| | | md5(userInfo.password), |
| | | userInfo.type, |
| | | userInfo.key, |
| | | userInfo.code |
| | | ).then(res => { |
| | | uni.switchTab({ |
| | | url: '/pages/map/index' |
| | | }) |
| | | loginByUsername( |
| | | userInfo.tenantId, |
| | | userInfo.deptId, |
| | | userInfo.roleId, |
| | | userInfo.username, |
| | | md5(userInfo.password), |
| | | userInfo.type, |
| | | userInfo.key, |
| | | userInfo.code |
| | | ).then(res => { |
| | | userStore.setUserInfo(res.data) |
| | | uni.switchTab({ |
| | | url: '/pages/user/index' |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | onLoad((options) => { |
| | | if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) { |
| | | redirect = decodeURIComponent(options.redirect); |
| | | } |
| | | }); |
| | | onLoad((options) => { |
| | | if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) { |
| | | redirect = decodeURIComponent(options.redirect); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .login-form-wrap { |
| | | @apply mt-80rpx mx-auto mb-0 w-600rpx; |
| | | .login-form-wrap { |
| | | @apply mt-80rpx mx-auto mb-0 w-600rpx; |
| | | |
| | | .title { |
| | | @apply mb-100rpx text-60rpx text-left font-500; |
| | | } |
| | | |
| | | input { |
| | | @apply pb-6rpx mb-10rpx text-left; |
| | | } |
| | | |
| | | .tips { |
| | | @apply mt-8rpx mb-60rpx; |
| | | |
| | | color: $u-info; |
| | | } |
| | | |
| | | .login-btn { |
| | | @apply flex items-center justify-center py-12rpx px-0 text-30rpx bg-#fdf3d0 border-none; |
| | | |
| | | color: $u-tips-color; |
| | | |
| | | &::after { |
| | | @apply border-none; |
| | | } |
| | | } |
| | | |
| | | .alternative { |
| | | @apply flex justify-between mt-30rpx; |
| | | |
| | | color: $u-tips-color; |
| | | } |
| | | .title { |
| | | @apply mb-100rpx text-60rpx text-left font-500; |
| | | } |
| | | |
| | | .login-type-wrap { |
| | | @apply flex justify-between pt-350rpx px-150rpx pb-150rpx; |
| | | |
| | | .item { |
| | | @apply flex items-center flex-col text-28rpx; |
| | | |
| | | color: $u-content-color; |
| | | } |
| | | input { |
| | | @apply pb-6rpx mb-10rpx text-left; |
| | | } |
| | | |
| | | .hint { |
| | | @apply px-40rpx py-20rpx text-24rpx; |
| | | .tips { |
| | | @apply mt-8rpx mb-60rpx; |
| | | |
| | | color: $u-info; |
| | | } |
| | | |
| | | .login-btn { |
| | | @apply flex items-center justify-center py-12rpx px-0 text-30rpx bg-#fdf3d0 border-none; |
| | | |
| | | color: $u-tips-color; |
| | | |
| | | .link { |
| | | color: $u-warning; |
| | | &::after { |
| | | @apply border-none; |
| | | } |
| | | } |
| | | |
| | | .alternative { |
| | | @apply flex justify-between mt-30rpx; |
| | | |
| | | color: $u-tips-color; |
| | | } |
| | | } |
| | | |
| | | .login-type-wrap { |
| | | @apply flex justify-between pt-350rpx px-150rpx pb-150rpx; |
| | | |
| | | .item { |
| | | @apply flex items-center flex-col text-28rpx; |
| | | |
| | | color: $u-content-color; |
| | | } |
| | | } |
| | | |
| | | .hint { |
| | | @apply px-40rpx py-20rpx text-24rpx; |
| | | |
| | | color: $u-tips-color; |
| | | |
| | | .link { |
| | | color: $u-warning; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | |
| | | <script setup> |
| | | onShow(() => { |
| | | console.log(222); |
| | | // #ifdef APP-PLUS |
| | | |
| | | console.log(111); |
| | | plus.screen.lockOrientation("landscape-primary"); |
| | | // plus.screen.lockOrientation("landscape-primary"); |
| | | // #endif |
| | | }); |
| | | |
| | | onHide(() => { |
| | | console.log(123123); |
| | | // #ifdef APP-PLUS |
| | | plus.screen.lockOrientation("portrait-primary"); |
| | | // plus.screen.lockOrientation("portrait-primary"); |
| | | // #endif |
| | | }); |
| | | </script> |
| | |
| | | <!-- 我的 --> |
| | | <template> |
| | | <view class="page-wrap"> |
| | | <u-navbar title="" placeholder left-icon="" right-icon="camera-fill" /> |
| | | <u-navbar title="" placeholder left-icon="" right-icon="camera-fill"/> |
| | | <view class="flex items-center pb-30rpx pl-30rpx pr-20rpx"> |
| | | <view class="mr-10rpx"> |
| | | <u-avatar src="/static/images/logo.png" size="70" /> |
| | | <u-avatar src="/static/images/logo.png" size="70"/> |
| | | </view> |
| | | <view class="flex-1"> |
| | | <view class="pb-20rpx text-36rpx"> uni-app </view> |
| | | <view class="pb-20rpx text-36rpx">{{ userStore.$state?.userInfo?.nick_name }}</view> |
| | | <view class="u-tips-color text-28rpx" @click="toCopy"> |
| | | 微信号:uni-app |
| | | ID:{{ userStore.$state?.userInfo?.user_id }} |
| | | </view> |
| | | </view> |
| | | <view class="ml-10rpx p-10rpx"> |
| | | <u-icon name="scan" color="#969799" /> |
| | | <u-icon name="scan" color="#969799"/> |
| | | </view> |
| | | <view class="ml-10rpx p-10rpx"> |
| | | <u-icon name="arrow-right" color="#969799" /> |
| | | <u-icon name="arrow-right" color="#969799"/> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="mt-20rpx"> |
| | | <u-cell-group> |
| | | <u-cell icon="rmb-circle" title="支付" is-link /> |
| | | </u-cell-group> |
| | | </view> |
| | | |
| | | <view class="mt-20rpx"> |
| | | <u-cell-group> |
| | | <u-cell icon="star" title="收藏" is-link /> |
| | | <u-cell icon="photo" title="相册" is-link /> |
| | | <u-cell icon="coupon" title="卡券" is-link /> |
| | | <u-cell icon="heart" title="关注" is-link /> |
| | | </u-cell-group> |
| | | </view> |
| | | |
| | | <view class="mt-20rpx"> |
| | | <u-cell-group> |
| | | <u-cell icon="setting" title="设置" is-link /> |
| | | <u-cell icon="setting" title="退出登录" is-link @click="logOut"/> |
| | | </u-cell-group> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { useClipboard, usePermission } from "@/hooks"; |
| | | import {useClipboard} from "@/hooks"; |
| | | import {useUserStore} from "@/store/index.js"; |
| | | import {onShow} from "@dcloudio/uni-app"; |
| | | import {getDeviceRegionApi} from "@/api/map.js"; |
| | | |
| | | const { setClipboardData, getClipboardData } = useClipboard(); |
| | | |
| | | const {setClipboardData, getClipboardData} = useClipboard(); |
| | | const userStore = useUserStore(); |
| | | // 复制 |
| | | const toCopy = async () => { |
| | | await setClipboardData({ data: "1234567890" }); |
| | | await setClipboardData({data: "1234567890"}); |
| | | const data = await getClipboardData(); |
| | | console.log("[ data ] >", data); |
| | | }; |
| | | |
| | | function logOut() { |
| | | userStore.setUserInfo(null) |
| | | uni.redirectTo({ |
| | | url: '/pages/login/index' |
| | | }) |
| | | } |
| | | |
| | | function getDeviceRegion() { |
| | | getDeviceRegionApi().then(res =>{ |
| | | console.log(res.data.data,'用户设备') |
| | | }) |
| | | } |
| | | |
| | | // 登录鉴权,微信小程序端点击tabbar的底层逻辑不触发uni.switchTab,需要在页面onShow生命周期中校验权限 |
| | | onShow(async () => { |
| | | const hasPermission = await usePermission(); |
| | | console.log(hasPermission ? "已登录" : "未登录,拦截跳转"); |
| | | getDeviceRegion() |
| | | }); |
| | | </script> |
| | |
| | | // 分享使用示例 |
| | | const { onShareAppMessage, onShareTimeline } = useShare({ |
| | | title: "首页", |
| | | path: "pages/tab/home/index", |
| | | path: "pages/tab/map/index", |
| | | imageUrl: "", |
| | | }); |
| | | onShareAppMessage(); |
| | |
| | | removeQueryString, |
| | | routes |
| | | } from "@/router" |
| | | import { isLogin } from "@/utils/auth" |
| | | import {useUserStore} from "@/store/index.js"; |
| | | |
| | | // 白名单路由 |
| | | const whiteList = ["/"] |
| | |
| | | return false |
| | | } |
| | | // 在白名单中或有token,直接放行 |
| | | const hasPermission = whiteList.includes(removeQueryString(path)) || isLogin() |
| | | const hasPermission = whiteList.includes(removeQueryString(path)) || useUserStore().$state.userInfo.access_token |
| | | if (!hasPermission) { |
| | | // 将用户的目标路径传递过去,这样可以实现用户登录之后,直接跳转到目标页面 |
| | | uni.redirectTo({ |
| | |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2025-09-28 09:55:05 |
| | | * @FilePath : \src\router\index.js |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-09-28 09:31:16 |
| | | */ |
| | | import pagesJson from "@/pages.json" |
| | | |
| | | // 路径常量 |
| | | export const HOME_PATH = "/pages/tab/home/index" |
| | | export const LOGIN_PATH = "/pages/common/login/index" |
| | | export const HOME_PATH = "/pages/map/index" |
| | | export const LOGIN_PATH = "/pages/login/index" |
| | | export const ERROR404_PATH = "/pages/common/404/index" |
| | | |
| | | /** |
| | | * 解析路由地址 |
| | | * @param {object} pagesJson |
| | | * @returns [{"path": "/pages/tab/home/index","needLogin": false},...] |
| | | * @returns [{"path": "/pages/tab/map/index","needLogin": false},...] |
| | | */ |
| | | function parseRoutes (pagesJson = {}) { |
| | | if (!pagesJson.pages) { |
| | |
| | | /* |
| | | * @Author : yuan |
| | | * @Date : 2025-09-28 09:31:16 |
| | | * @LastEditors : yuan |
| | | * @LastEditTime : 2025-09-28 10:02:26 |
| | | * @FilePath : \src\store\modules\user\index.ts |
| | | * @Description : |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-09-28 09:31:16 |
| | | */ |
| | | import { defineStore } from 'pinia' |
| | | import { UserApi } from '@/api' |
| | | import { clearToken, setToken } from '@/utils/auth' |
| | | import {defineStore} from 'pinia' |
| | | |
| | | const useUserStore = defineStore('user', { |
| | | state: () => ({ |
| | | user_id: '', |
| | | user_name: '江阳小道', |
| | | avatar: '', |
| | | token: '' |
| | | userInfo: null // 用户信息 |
| | | }), |
| | | getters: { |
| | | userInfo (state) { |
| | | return { ...state } |
| | | } |
| | | }, |
| | | actions: { |
| | | // 设置用户的信息 |
| | | setInfo (partial) { |
| | | this.$patch(partial) |
| | | setUserInfo(userInfo) { |
| | | this.userInfo = userInfo |
| | | }, |
| | | // 重置用户信息 |
| | | resetInfo () { |
| | | // 重置 |
| | | resetUserStore() { |
| | | this.$reset() |
| | | }, |
| | | // 获取用户信息 |
| | | async info () { |
| | | const result = await UserApi.profile() |
| | | this.setInfo(result) |
| | | // 合并 |
| | | mergeUserStore(data) { |
| | | this.$patch(data) |
| | | }, |
| | | // 异步登录并存储token |
| | | login (loginForm) { |
| | | return new Promise((resolve, reject) => { |
| | | UserApi.login(loginForm) |
| | | .then((res) => { |
| | | const token = res.token |
| | | if (token) { |
| | | setToken(token) |
| | | } |
| | | resolve(res) |
| | | }) |
| | | .catch((error) => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | // Logout |
| | | async logout () { |
| | | await UserApi.logout() |
| | | this.resetInfo() |
| | | clearToken() |
| | | }, |
| | | // 小程序授权登录 |
| | | authLogin (provider = 'weixin') { |
| | | return new Promise((resolve, reject) => { |
| | | uni.login({ |
| | | provider, |
| | | success: async (result) => { |
| | | if (result.code) { |
| | | const res = await UserApi.loginByCode({ code: result.code }) |
| | | resolve(res) |
| | | } else { |
| | | reject(new Error(result.errMsg)) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | console.error(`login error: ${err}`) |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }, |
| | | persist: true |
| | | persist: true //持久化 |
| | | }) |
| | | |
| | | export default useUserStore |
| | |
| | | * Copyright 2025 OBKoro1, All Rights Reserved. |
| | | * 2025-09-28 09:31:16 |
| | | */ |
| | | export * from "./auth" |
| | | export * from "./common" |
| | | export * from "./modals" |
| | | export * from "./request" |
| | |
| | | |
| | | const http = new Request() |
| | | |
| | | export function request (config) { |
| | | return new Promise((resolve, reject) => { |
| | | http |
| | | .request(config) |
| | | .then(res => { |
| | | resolve(res) |
| | | }) |
| | | .catch(err => { |
| | | reject(err) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | |
| | | // 引入拦截器配置 |
| | | export function setupRequest () { |
| | | http.setConfig(defaultConfig => { |
| | |
| | | responseInterceptors(http) |
| | | } |
| | | |
| | | export function request (config) { |
| | | return new Promise((resolve, reject) => { |
| | | http |
| | | .request(config) |
| | | .then(res => { |
| | | console.log("[ res ] >", res) |
| | | const { result } = res.data |
| | | resolve(result) |
| | | }) |
| | | .catch(err => { |
| | | console.error("[ err ] >", err) |
| | | reject(err) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | |
| | | |
| | | export function get (url, config) { |
| | | return request({ ...config, url, method: "GET" }) |
| | |
| | | import { |
| | | useUserStore |
| | | } from "@/store" |
| | | import { |
| | | getToken |
| | | } from "@/utils/auth" |
| | | import storage from "@/utils/storage" |
| | | import { |
| | | showMessage |
| | |
| | | import { |
| | | Base64 |
| | | } from 'js-base64' |
| | | // 重试队列,每一项将是一个待执行的函数形式 |
| | | let requestQueue = [] |
| | | |
| | | // 防止重复提交 |
| | | const repeatSubmit = config => { |
| | |
| | | } |
| | | } |
| | | |
| | | // 是否正在刷新token的标记 |
| | | let isRefreshing = false |
| | | |
| | | // 刷新token |
| | | const refreshToken = async (http, config) => { |
| | | // 是否在获取token中,防止重复获取 |
| | | if (!isRefreshing) { |
| | | // 修改登录状态为true |
| | | isRefreshing = true |
| | | // 等待登录完成 |
| | | await useUserStore().authLogin() |
| | | // 登录完成之后,开始执行队列请求 |
| | | requestQueue.forEach(cb => cb()) |
| | | // 重试完了清空这个队列 |
| | | requestQueue = [] |
| | | isRefreshing = false |
| | | // 重新执行本次请求 |
| | | return http.request(config) |
| | | } |
| | | |
| | | return new Promise(resolve => { |
| | | // 将resolve放进队列,用一个函数形式来保存,等登录后直接执行 |
| | | requestQueue.push(() => { |
| | | resolve(http.request(config)) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // 请求拦截器 |
| | | function requestInterceptors(http) { |
| | | http.interceptors.request.use((config) => { // 可使用async await 做异步操作 |
| | | |
| | | http.interceptors.request.use((config) => { |
| | | const {detail} = useUserStore().$state?.userInfo || {} |
| | | // 假设有token值需要在头部需要携带 |
| | | let accessToken = uni.getStorageSync('accessToken'); |
| | | let accessToken = useUserStore()?.$state?.userInfo?.access_token; |
| | | if (accessToken) { |
| | | config.header['Blade-Auth'] = 'bearer ' + accessToken; |
| | | } |
| | | |
| | | if (detail?.areaCode) { |
| | | config.header['areaCode'] = detail.areaCode |
| | | } |
| | | // 安全请求header |
| | | config.header['Blade-Requested-With'] = 'BladeHttpRequest'; |
| | | // 客户端认证参数 |
| | | config.header['Authorization'] = 'Basic ' + Base64.encode(website.clientId + ':' + website.clientSecret); |
| | | |
| | | // #ifndef H5 |
| | | let url = config.url |
| | | if (process.env.NODE_ENV == 'development' && !url.startsWith("http")) { |
| | | url = url.substring(url.indexOf('/api') + 4) |
| | | config.url = url |
| | | } |
| | | // #endif |
| | | |
| | | // 额外参数 |
| | | // config.data = config.data || {}; |
| | | // config.data.pf = uni.getSystemInfoSync().platform; |
| | | // config.data.sys = uni.getSystemInfoSync().system; |
| | | |
| | | // 演示custom 用处 |
| | | // if (config.custom.auth) { |
| | | // config.header.token = 'token' |
| | | // } |
| | | // if (config.custom.loading) { |
| | | // uni.showLoading() |
| | | // } |
| | | /** |
| | | /* 演示 |
| | | if (!token) { // 如果token不存在,return Promise.reject(config) 会取消本次请求 |
| | | return Promise.reject(config) |
| | | } |
| | | **/ |
| | | return config |
| | | }, config => { // 可使用async await 做异步操作 |
| | | return Promise.reject(config) |
| | | }) |
| | | } |
| | | |
| | | // 响应拦截器 |
| | | function responseInterceptors(http) { |
| | | http.interceptors.response.use((response) => { |
| | | console.log(response, 2222) |
| | | // 若有数据返回则通过 |
| | | if (response.data.access_token || response.data.key) { |
| | | return response.data |
| | | } |
| | | // 服务端返回的状态码不等于200,则reject() |
| | | if (response.data.code !== 200) { |
| | | uni.showToast({ |
| | | title: response.data.msg, |
| | | icon: 'none' |
| | | }); |
| | | let res = response |
| | | const status = res.data.error_code || res.data.code || res.statusCode |
| | | const message = res?.data?.msg || res?.data?.error_description || res?.data?.message || '系统错误' |
| | | if (status !== 200) { |
| | | uni.showToast({title: message, icon: 'none'}); |
| | | return Promise.reject(response); |
| | | } |
| | | return response.data; |
| | | return response; |
| | | }, (response) => { |
| | | console.log(response, 111) |
| | | /* 对响应错误做点什么 (statusCode !== 200)*/ |
| | | uni.showToast({ |
| | | title: response.data.msg, |
| | | icon: 'none' |
| | | }); |
| | | if (response.statusCode == 401) { |
| | | uni.showToast({title: response?.data?.msg, icon: 'none'}); |
| | | if (response.statusCode === 401) { |
| | | const pages = getCurrentPages() |
| | | const currentPage = pages[pages.length - 1] |
| | | uni.redirectTo({ |
| | | url: `/pages/login/login-account?redirect=/${currentPage.route}` |
| | | url: `/pages/login/index?redirect=/${currentPage.route}` |
| | | }) |
| | | } |
| | | return Promise.reject(response) |