feat(auth): 重构授权逻辑并添加静默授权功能
重构应用授权流程,将授权逻辑抽离到单独的工具模块 添加静默授权功能,优化用户体验 完善请求拦截器中的授权检查和跳转逻辑 新增页面路径保存和返回功能
Showing
6 changed files
with
277 additions
and
96 deletions
| 1 | // ESLint 检查 .vue 文件需要单独配置编辑器: | 1 | // ESLint 检查 .vue 文件需要单独配置编辑器: |
| 2 | // https://eslint.vuejs.org/user-guide/#editor-integrations | 2 | // https://eslint.vuejs.org/user-guide/#editor-integrations |
| 3 | { | 3 | { |
| 4 | - "extends": ["taro/vue3"] | 4 | + "extends": ["taro/vue3"], |
| 5 | + "parserOptions": { | ||
| 6 | + "requireConfigFile": false | ||
| 7 | + } | ||
| 5 | } | 8 | } |
| 6 | - | ... | ... |
| ... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
| 7 | */ | 7 | */ |
| 8 | import axios from '@/utils/request'; | 8 | import axios from '@/utils/request'; |
| 9 | import Taro from '@tarojs/taro' | 9 | import Taro from '@tarojs/taro' |
| 10 | -// import qs from 'qs' | 10 | +import qs from 'qs' |
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| 13 | * 网络请求功能函数 | 13 | * 网络请求功能函数 |
| ... | @@ -80,9 +80,13 @@ export const fetch = { | ... | @@ -80,9 +80,13 @@ export const fetch = { |
| 80 | post: function (api, params) { | 80 | post: function (api, params) { |
| 81 | return axios.post(api, params) | 81 | return axios.post(api, params) |
| 82 | }, | 82 | }, |
| 83 | - // stringifyPost: function (api, params) { | 83 | + stringifyPost: function (api, params) { |
| 84 | - // return axios.post(api, qs.stringify(params)) | 84 | + return axios.post(api, qs.stringify(params), { |
| 85 | - // }, | 85 | + headers: { |
| 86 | + 'content-type': 'application/x-www-form-urlencoded' | ||
| 87 | + } | ||
| 88 | + }) | ||
| 89 | + }, | ||
| 86 | basePost: function (url, data, config) { | 90 | basePost: function (url, data, config) { |
| 87 | return axios.post(url, data, config) | 91 | return axios.post(url, data, config) |
| 88 | } | 92 | } | ... | ... |
| ... | @@ -8,33 +8,34 @@ | ... | @@ -8,33 +8,34 @@ |
| 8 | import { createApp } from 'vue' | 8 | import { createApp } from 'vue' |
| 9 | import { createPinia } from 'pinia' | 9 | import { createPinia } from 'pinia' |
| 10 | import './app.less' | 10 | import './app.less' |
| 11 | -import { routerStore } from '@/stores/router' | 11 | +import { saveCurrentPagePath, needAuth, silentAuth, navigateToAuth } from '@/utils/authRedirect' |
| 12 | -import Taro from '@tarojs/taro' | ||
| 13 | 12 | ||
| 14 | const App = createApp({ | 13 | const App = createApp({ |
| 15 | // 对应 onLaunch | 14 | // 对应 onLaunch |
| 16 | - onLaunch(options) { | 15 | + async onLaunch(options) { |
| 17 | - // 未授权状态跳转授权页面,首页不需要权限 | 16 | + const path = options?.path || '' |
| 18 | - const path = options.path; | 17 | + const query = options?.query || {} |
| 19 | - const query = options.query; | 18 | + |
| 20 | - // 缓存没有权限的地址 | 19 | + const query_string = Object.keys(query) |
| 21 | - const router = routerStore(); | 20 | + .map((key) => `${key}=${encodeURIComponent(query[key])}`) |
| 22 | - router.add(path); | 21 | + .join('&') |
| 23 | - // if (path !== 'pages/index/index' && !wx.getStorageSync("sessionid")) { | 22 | + const full_path = query_string ? `${path}?${query_string}` : path |
| 24 | - if (!wx.getStorageSync("sessionid")) { | 23 | + |
| 25 | - console.warn("没有权限"); | 24 | + if (full_path) { |
| 26 | - // if (path === 'pages/detail/index') { | 25 | + saveCurrentPagePath(full_path) |
| 27 | - // Taro.navigateTo({ | 26 | + } |
| 28 | - // url: `./pages/auth/index?url=${path}&id=${query.id}&start_date=${query.start_date}&end_date=${query.end_date}`, | 27 | + |
| 29 | - // }) | 28 | + if (!needAuth()) return |
| 30 | - // } else { | 29 | + |
| 31 | - // Taro.navigateTo({ | 30 | + if (path === 'pages/auth/index') return |
| 32 | - // url: './pages/auth/index?url=' + path, | 31 | + |
| 33 | - // }) | 32 | + try { |
| 34 | - // } | 33 | + await silentAuth() |
| 34 | + } catch (error) { | ||
| 35 | + navigateToAuth(full_path || undefined) | ||
| 35 | } | 36 | } |
| 36 | }, | 37 | }, |
| 37 | - onShow(options) { | 38 | + onShow() { |
| 38 | }, | 39 | }, |
| 39 | // 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖 | 40 | // 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖 |
| 40 | }); | 41 | }); | ... | ... |
| ... | @@ -15,62 +15,14 @@ | ... | @@ -15,62 +15,14 @@ |
| 15 | 15 | ||
| 16 | <script setup> | 16 | <script setup> |
| 17 | import Taro, { useDidShow } from '@tarojs/taro' | 17 | import Taro, { useDidShow } from '@tarojs/taro' |
| 18 | -import request from '@/utils/request'; | 18 | +import { silentAuth, returnToOriginalPage } from '@/utils/authRedirect' |
| 19 | 19 | ||
| 20 | useDidShow(() => { | 20 | useDidShow(() => { |
| 21 | - // 授权登陆 | 21 | + silentAuth() |
| 22 | - Taro.login({ | 22 | + .then(() => returnToOriginalPage('/pages/index/index')) |
| 23 | - success: function (res) { | 23 | + .catch((error) => { |
| 24 | - if (res.code) { | 24 | + Taro.showToast({ title: error?.message || '授权失败', icon: 'none' }) |
| 25 | - //发起网络请求 | 25 | + }) |
| 26 | - Taro.showLoading({ | ||
| 27 | - title: '授权中', | ||
| 28 | - }) | ||
| 29 | - request.post('/srv/?a=openid', { | ||
| 30 | - code: res.code | ||
| 31 | - }) | ||
| 32 | - .then(res => { | ||
| 33 | - if (res.data.code === 1) { // 成功 | ||
| 34 | - var cookie = res.cookies ? res.cookies[0] : (res.header['Set-Cookie'] || res.header['set-cookie']); | ||
| 35 | - // 有些时候 cookie 可能是一个数组,或者分号分隔的字符串 | ||
| 36 | - // axios-miniprogram 返回的 cookies 应该是一个数组 | ||
| 37 | - | ||
| 38 | - if (cookie) { | ||
| 39 | - // 处理 cookie 格式,如果包含分号,取第一个 | ||
| 40 | - if (Array.isArray(cookie)) cookie = cookie[0]; | ||
| 41 | - // 简单处理,通常服务器返回 PHPSESSID=xxx; path=/ | ||
| 42 | - // 我们存入 storage | ||
| 43 | - Taro.setStorageSync("sessionid", cookie); | ||
| 44 | - | ||
| 45 | - // 返回上一页 | ||
| 46 | - Taro.hideLoading(); | ||
| 47 | - Taro.navigateBack({ | ||
| 48 | - fail: () => { | ||
| 49 | - Taro.reLaunch({ url: '/pages/index/index' }) | ||
| 50 | - } | ||
| 51 | - }); | ||
| 52 | - } else { | ||
| 53 | - console.warn('No cookie received'); | ||
| 54 | - Taro.hideLoading(); | ||
| 55 | - Taro.showToast({ title: '授权失败: 无Cookie', icon: 'none' }); | ||
| 56 | - } | ||
| 57 | - } else { | ||
| 58 | - console.warn(res.data.msg); | ||
| 59 | - Taro.hideLoading(); | ||
| 60 | - Taro.showToast({ title: res.data.msg || '授权失败', icon: 'none' }); | ||
| 61 | - } | ||
| 62 | - }) | ||
| 63 | - .catch(err => { | ||
| 64 | - console.error(err); | ||
| 65 | - Taro.hideLoading(); | ||
| 66 | - Taro.showToast({ title: '网络错误', icon: 'none' }); | ||
| 67 | - }); | ||
| 68 | - } else { | ||
| 69 | - console.log('登录失败!' + res.errMsg) | ||
| 70 | - Taro.showToast({ title: '微信登录失败', icon: 'none' }); | ||
| 71 | - } | ||
| 72 | - } | ||
| 73 | - }) | ||
| 74 | }) | 26 | }) |
| 75 | </script> | 27 | </script> |
| 76 | 28 | ... | ... |
src/utils/authRedirect.js
0 → 100644
| 1 | +import Taro from '@tarojs/taro' | ||
| 2 | +import { routerStore } from '@/stores/router' | ||
| 3 | +import request from '@/utils/request' | ||
| 4 | + | ||
| 5 | +export const getCurrentPageFullPath = () => { | ||
| 6 | + const pages = getCurrentPages() | ||
| 7 | + if (!pages || pages.length === 0) return '' | ||
| 8 | + | ||
| 9 | + const current_page = pages[pages.length - 1] | ||
| 10 | + const route = current_page.route | ||
| 11 | + const options = current_page.options || {} | ||
| 12 | + | ||
| 13 | + const query_params = Object.keys(options) | ||
| 14 | + .map((key) => `${key}=${encodeURIComponent(options[key])}`) | ||
| 15 | + .join('&') | ||
| 16 | + | ||
| 17 | + return query_params ? `${route}?${query_params}` : route | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +export const saveCurrentPagePath = (custom_path) => { | ||
| 21 | + const router = routerStore() | ||
| 22 | + const path = custom_path || getCurrentPageFullPath() | ||
| 23 | + router.add(path) | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +export const needAuth = () => { | ||
| 27 | + try { | ||
| 28 | + const sessionid = Taro.getStorageSync('sessionid') | ||
| 29 | + return !sessionid || sessionid === '' | ||
| 30 | + } catch (error) { | ||
| 31 | + console.error('检查授权状态失败:', error) | ||
| 32 | + return true | ||
| 33 | + } | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +export const silentAuth = async (on_success, on_error) => { | ||
| 37 | + try { | ||
| 38 | + if (!needAuth()) { | ||
| 39 | + const result = { code: 1, msg: '已授权' } | ||
| 40 | + if (on_success) on_success(result) | ||
| 41 | + return result | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + Taro.showLoading({ | ||
| 45 | + title: '加载中...', | ||
| 46 | + mask: true, | ||
| 47 | + }) | ||
| 48 | + | ||
| 49 | + const login_result = await new Promise((resolve, reject) => { | ||
| 50 | + Taro.login({ | ||
| 51 | + success: resolve, | ||
| 52 | + fail: reject, | ||
| 53 | + }) | ||
| 54 | + }) | ||
| 55 | + | ||
| 56 | + if (!login_result || !login_result.code) { | ||
| 57 | + throw new Error('获取微信登录code失败') | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + const response = await request.post('/srv/?a=openid', { | ||
| 61 | + code: login_result.code, | ||
| 62 | + }) | ||
| 63 | + | ||
| 64 | + Taro.hideLoading() | ||
| 65 | + | ||
| 66 | + if (!response?.data || response.data.code !== 1) { | ||
| 67 | + const error_msg = response?.data?.msg || '授权失败' | ||
| 68 | + if (on_error) on_error(error_msg) | ||
| 69 | + throw new Error(error_msg) | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + let cookie = | ||
| 73 | + (response.cookies && response.cookies[0]) || | ||
| 74 | + response.header?.['Set-Cookie'] || | ||
| 75 | + response.header?.['set-cookie'] | ||
| 76 | + | ||
| 77 | + if (Array.isArray(cookie)) cookie = cookie[0] | ||
| 78 | + | ||
| 79 | + if (!cookie) { | ||
| 80 | + const error_msg = '授权失败:没有获取到有效的会话信息' | ||
| 81 | + if (on_error) on_error(error_msg) | ||
| 82 | + throw new Error(error_msg) | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + Taro.setStorageSync('sessionid', cookie) | ||
| 86 | + | ||
| 87 | + if (request?.defaults?.headers) { | ||
| 88 | + request.defaults.headers.cookie = cookie | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + if (on_success) on_success(response.data) | ||
| 92 | + return response.data | ||
| 93 | + } catch (error) { | ||
| 94 | + Taro.hideLoading() | ||
| 95 | + const error_msg = error?.message || '授权失败,请稍后重试' | ||
| 96 | + if (on_error) on_error(error_msg) | ||
| 97 | + throw error | ||
| 98 | + } | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +export const navigateToAuth = (return_path) => { | ||
| 102 | + if (return_path) { | ||
| 103 | + saveCurrentPagePath(return_path) | ||
| 104 | + } else { | ||
| 105 | + saveCurrentPagePath() | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + Taro.navigateTo({ | ||
| 109 | + url: '/pages/auth/index', | ||
| 110 | + }) | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +export const returnToOriginalPage = async (default_path = '/pages/index/index') => { | ||
| 114 | + const router = routerStore() | ||
| 115 | + const saved_path = router.url | ||
| 116 | + | ||
| 117 | + try { | ||
| 118 | + router.remove() | ||
| 119 | + | ||
| 120 | + const pages = Taro.getCurrentPages() | ||
| 121 | + const current_page = pages[pages.length - 1] | ||
| 122 | + const current_route = current_page?.route | ||
| 123 | + | ||
| 124 | + let target_path = default_path | ||
| 125 | + if (saved_path && saved_path !== '') { | ||
| 126 | + target_path = saved_path.startsWith('/') ? saved_path : `/${saved_path}` | ||
| 127 | + } | ||
| 128 | + | ||
| 129 | + const target_route = target_path.split('?')[0].replace(/^\//, '') | ||
| 130 | + | ||
| 131 | + if (current_route === target_route) { | ||
| 132 | + return | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + try { | ||
| 136 | + await Taro.redirectTo({ url: target_path }) | ||
| 137 | + } catch (error) { | ||
| 138 | + await Taro.reLaunch({ url: target_path }) | ||
| 139 | + } | ||
| 140 | + } catch (error) { | ||
| 141 | + console.error('returnToOriginalPage 执行出错:', error) | ||
| 142 | + try { | ||
| 143 | + await Taro.reLaunch({ url: default_path }) | ||
| 144 | + } catch (final_error) { | ||
| 145 | + console.error('最终降级方案也失败了:', final_error) | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +export const isFromShare = (options) => { | ||
| 151 | + return options && (options.from_share === '1' || options.scene) | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +export const handleSharePageAuth = async (options, callback) => { | ||
| 155 | + if (!needAuth()) { | ||
| 156 | + if (typeof callback === 'function') callback() | ||
| 157 | + return true | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + if (isFromShare(options)) { | ||
| 161 | + saveCurrentPagePath() | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + try { | ||
| 165 | + await silentAuth( | ||
| 166 | + () => { | ||
| 167 | + if (typeof callback === 'function') callback() | ||
| 168 | + }, | ||
| 169 | + () => { | ||
| 170 | + Taro.navigateTo({ url: '/pages/auth/index' }) | ||
| 171 | + } | ||
| 172 | + ) | ||
| 173 | + return true | ||
| 174 | + } catch (error) { | ||
| 175 | + Taro.navigateTo({ url: '/pages/auth/index' }) | ||
| 176 | + return false | ||
| 177 | + } | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +export const addShareFlag = (path) => { | ||
| 181 | + const separator = path.includes('?') ? '&' : '?' | ||
| 182 | + return `${path}${separator}from_share=1` | ||
| 183 | +} |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2022-09-19 14:11:06 | 2 | * @Date: 2022-09-19 14:11:06 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-01 11:17:49 | 4 | + * @LastEditTime: 2026-01-06 21:41:47 |
| 5 | - * @FilePath: /xyxBooking-weapp/src/utils/request.js | 5 | + * @FilePath: /itomix/git/xyxBooking-weapp/src/utils/request.js |
| 6 | * @Description: 简单axios封装,后续按实际处理 | 6 | * @Description: 简单axios封装,后续按实际处理 |
| 7 | */ | 7 | */ |
| 8 | // import axios from 'axios' | 8 | // import axios from 'axios' |
| 9 | import axios from 'axios-miniprogram'; | 9 | import axios from 'axios-miniprogram'; |
| 10 | import Taro from '@tarojs/taro' | 10 | import Taro from '@tarojs/taro' |
| 11 | -// import { strExist } from './tools' | 11 | +import qs from 'qs' |
| 12 | -// import qs from 'Qs' | 12 | +import { strExist } from './tools' |
| 13 | -import { mainStore } from '@/stores/main' | 13 | +import { routerStore } from '@/stores/router' |
| 14 | 14 | ||
| 15 | // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress'; | 15 | // import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress'; |
| 16 | // import store from '@/store' | 16 | // import store from '@/store' |
| ... | @@ -30,6 +30,30 @@ const getSessionId = () => { | ... | @@ -30,6 +30,30 @@ const getSessionId = () => { |
| 30 | } | 30 | } |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | +const getCurrentPageFullPath = () => { | ||
| 34 | + try { | ||
| 35 | + const pages = Taro.getCurrentPages() | ||
| 36 | + if (!pages || pages.length === 0) return '' | ||
| 37 | + | ||
| 38 | + const currentPage = pages[pages.length - 1] | ||
| 39 | + const route = currentPage.route | ||
| 40 | + const options = currentPage.options || {} | ||
| 41 | + | ||
| 42 | + const queryParams = Object.keys(options) | ||
| 43 | + .map(key => `${key}=${encodeURIComponent(options[key])}`) | ||
| 44 | + .join('&') | ||
| 45 | + | ||
| 46 | + return queryParams ? `${route}?${queryParams}` : route | ||
| 47 | + } catch (error) { | ||
| 48 | + return '' | ||
| 49 | + } | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +const isPlainObject = (value) => { | ||
| 53 | + if (value === null || typeof value !== 'object') return false | ||
| 54 | + return Object.prototype.toString.call(value) === '[object Object]' | ||
| 55 | +} | ||
| 56 | + | ||
| 33 | // create an axios instance | 57 | // create an axios instance |
| 34 | const service = axios.create({ | 58 | const service = axios.create({ |
| 35 | baseURL: BASE_URL, // url = base url + request url | 59 | baseURL: BASE_URL, // url = base url + request url |
| ... | @@ -56,17 +80,28 @@ service.interceptors.request.use( | ... | @@ -56,17 +80,28 @@ service.interceptors.request.use( |
| 56 | if (sessionid) { | 80 | if (sessionid) { |
| 57 | config.headers.cookie = sessionid; | 81 | config.headers.cookie = sessionid; |
| 58 | } | 82 | } |
| 59 | - | 83 | + |
| 60 | // 增加时间戳 | 84 | // 增加时间戳 |
| 61 | if (config.method === 'get') { | 85 | if (config.method === 'get') { |
| 62 | config.params = { ...config.params, timestamp: (new Date()).valueOf() } | 86 | config.params = { ...config.params, timestamp: (new Date()).valueOf() } |
| 63 | } | 87 | } |
| 64 | 88 | ||
| 65 | - /** | 89 | + if ((config.method || '').toLowerCase() === 'post') { |
| 66 | - * POST PHP需要修改数据格式 | 90 | + const url = config.url || '' |
| 67 | - * 序列化POST请求时需要屏蔽上传相关接口,上传相关接口序列化后报错 | 91 | + const headers = config.headers || {} |
| 68 | - */ | 92 | + const contentType = headers['content-type'] || headers['Content-Type'] |
| 69 | - // config.data = config.method === 'post' && !strExist(['a=upload', 'upload.qiniup.com'], config.url) ? qs.stringify(config.data) : config.data; | 93 | + const shouldUrlEncode = |
| 94 | + !contentType || String(contentType).includes('application/x-www-form-urlencoded') | ||
| 95 | + | ||
| 96 | + if (shouldUrlEncode && !strExist(['upload.qiniup.com'], url) && isPlainObject(config.data)) { | ||
| 97 | + config.headers = { | ||
| 98 | + ...headers, | ||
| 99 | + 'content-type': 'application/x-www-form-urlencoded' | ||
| 100 | + } | ||
| 101 | + config.data = qs.stringify(config.data) | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + | ||
| 70 | return config | 105 | return config |
| 71 | }, | 106 | }, |
| 72 | error => { | 107 | error => { |
| ... | @@ -90,7 +125,7 @@ service.interceptors.response.use( | ... | @@ -90,7 +125,7 @@ service.interceptors.response.use( |
| 90 | */ | 125 | */ |
| 91 | response => { | 126 | response => { |
| 92 | const res = response.data | 127 | const res = response.data |
| 93 | - | 128 | + |
| 94 | // 401 未授权处理 | 129 | // 401 未授权处理 |
| 95 | if (res.code === 401) { | 130 | if (res.code === 401) { |
| 96 | // 跳转到授权页 | 131 | // 跳转到授权页 |
| ... | @@ -98,6 +133,11 @@ service.interceptors.response.use( | ... | @@ -98,6 +133,11 @@ service.interceptors.response.use( |
| 98 | const pages = Taro.getCurrentPages(); | 133 | const pages = Taro.getCurrentPages(); |
| 99 | const currentPage = pages[pages.length - 1]; | 134 | const currentPage = pages[pages.length - 1]; |
| 100 | if (currentPage && currentPage.route !== 'pages/auth/index') { | 135 | if (currentPage && currentPage.route !== 'pages/auth/index') { |
| 136 | + const router = routerStore() | ||
| 137 | + const currentPath = getCurrentPageFullPath() | ||
| 138 | + if (currentPath) { | ||
| 139 | + router.add(currentPath) | ||
| 140 | + } | ||
| 101 | Taro.navigateTo({ | 141 | Taro.navigateTo({ |
| 102 | url: '/pages/auth/index' | 142 | url: '/pages/auth/index' |
| 103 | }); | 143 | }); |
| ... | @@ -108,11 +148,10 @@ service.interceptors.response.use( | ... | @@ -108,11 +148,10 @@ service.interceptors.response.use( |
| 108 | if (['预约ID不存在'].includes(res.msg)) { | 148 | if (['预约ID不存在'].includes(res.msg)) { |
| 109 | res.show = false; | 149 | res.show = false; |
| 110 | } | 150 | } |
| 111 | - | 151 | + |
| 112 | return response | 152 | return response |
| 113 | }, | 153 | }, |
| 114 | error => { | 154 | error => { |
| 115 | - console.log('err' + error) // for debug | ||
| 116 | // Taro.showToast({ | 155 | // Taro.showToast({ |
| 117 | // title: error.message, | 156 | // title: error.message, |
| 118 | // icon: 'none', | 157 | // icon: 'none', | ... | ... |
-
Please register or login to post a comment