no message
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 反调试工具
|
||||
*
|
||||
* - 仅在 H5 平台生效(其他平台条件编译后为空文件)
|
||||
* - 仅在生产构建生效,dev 环境完全无感
|
||||
* - 域名包含 "test" 关键字时跳过(测试服务器允许调试)
|
||||
* - 使用字符串拼接 + Function 构造,避免 terser 在压缩时识别并移除 debugger
|
||||
* - 循环触发 debugger 卡死调试器,同时通过暂停耗时检测 DevTools 是否被打开,
|
||||
* 一旦判定为调试状态立即跳转到空白页,防止用户继续观察页面 / 拦截请求
|
||||
*/
|
||||
|
||||
// #ifdef H5
|
||||
const PROD = import.meta.env.PROD
|
||||
const TRAP_INTERVAL = 500 // 触发间隔(ms)
|
||||
const DETECT_THRESHOLD = 200 // debugger 暂停超过此值判定为调试中(ms)
|
||||
const REDIRECT_URL = 'about:blank' // 检测到调试时跳转目标
|
||||
const TEST_DOMAIN_KEYWORD = 'test' // 域名包含此关键字时跳过反调试(测试服免调试干扰)
|
||||
|
||||
// 字符串拼接 + Function 构造,防止 terser/minifier 在压缩阶段移除 debugger 关键字
|
||||
const trapBody = ['de', 'bug', 'ger'].join('')
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
|
||||
const trap = new Function(trapBody)
|
||||
|
||||
function isTestEnv(): boolean {
|
||||
try {
|
||||
return window.location.hostname.toLowerCase().includes(TEST_DOMAIN_KEYWORD)
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
let started = false
|
||||
function startAntiDebug() {
|
||||
if (!PROD) return
|
||||
if (isTestEnv()) return
|
||||
if (started) return
|
||||
started = true
|
||||
|
||||
const tick = () => {
|
||||
const start = Date.now()
|
||||
try {
|
||||
trap()
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
if (Date.now() - start > DETECT_THRESHOLD) {
|
||||
try {
|
||||
window.location.href = REDIRECT_URL
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick()
|
||||
setInterval(tick, TRAP_INTERVAL)
|
||||
}
|
||||
|
||||
startAntiDebug()
|
||||
// #endif
|
||||
|
||||
export {}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { TOKEN_KEY } from '@/enums/constantEnums'
|
||||
import cache from './cache'
|
||||
|
||||
export function getToken() {
|
||||
return cache.get(TOKEN_KEY)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
const cache = {
|
||||
key: 'app_',
|
||||
//设置缓存(expire为缓存时效)
|
||||
set(key: string, value: any, expire?: number) {
|
||||
key = this.getKey(key)
|
||||
let data: any = {
|
||||
expire: expire ? this.time() + expire : '',
|
||||
value
|
||||
}
|
||||
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data)
|
||||
}
|
||||
try {
|
||||
uni.setStorageSync(key, data)
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
get(key: string) {
|
||||
key = this.getKey(key)
|
||||
try {
|
||||
const data = uni.getStorageSync(key)
|
||||
if (!data) {
|
||||
return null
|
||||
}
|
||||
const { value, expire } = JSON.parse(data)
|
||||
if (expire && expire < this.time()) {
|
||||
uni.removeStorageSync(key)
|
||||
return null
|
||||
}
|
||||
return value
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
//获取当前时间
|
||||
time() {
|
||||
return Math.round(new Date().getTime() / 1000)
|
||||
},
|
||||
remove(key: string) {
|
||||
key = this.getKey(key)
|
||||
uni.removeStorageSync(key)
|
||||
},
|
||||
getKey(key: string) {
|
||||
return this.key + key
|
||||
}
|
||||
}
|
||||
|
||||
export default cache
|
||||
@@ -0,0 +1,66 @@
|
||||
import { ClientEnum } from '@/enums/appEnums'
|
||||
|
||||
/**
|
||||
* @description 判断是否为微信环境
|
||||
* @return { Boolean }
|
||||
*/
|
||||
export const isWeixinClient = () => {
|
||||
// #ifdef H5
|
||||
return /MicroMessenger/i.test(navigator.userAgent)
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 判断是否为安卓环境
|
||||
* @return { Boolean }
|
||||
*/
|
||||
export function isAndroid() {
|
||||
const u = navigator.userAgent
|
||||
return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取当前是什么端
|
||||
* @return { Object }
|
||||
*/
|
||||
|
||||
export const getClient = () => {
|
||||
//@ts-ignore
|
||||
return handleClientEvent({
|
||||
// 微信小程序
|
||||
MP_WEIXIN: () => ClientEnum['MP_WEIXIN'],
|
||||
// 微信公众号
|
||||
OA_WEIXIN: () => ClientEnum['OA_WEIXIN'],
|
||||
// H5
|
||||
H5: () => ClientEnum['H5'],
|
||||
// APP
|
||||
IOS: () => ClientEnum['IOS'],
|
||||
ANDROID: () => ClientEnum['ANDROID'],
|
||||
// 其它
|
||||
OTHER: () => null
|
||||
})
|
||||
}
|
||||
|
||||
// 根据端处理事件
|
||||
//@ts-ignore
|
||||
export const handleClientEvent = ({ MP_WEIXIN, OA_WEIXIN, H5, IOS, ANDROID, OTHER }: any) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
return MP_WEIXIN()
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
return isWeixinClient() ? OA_WEIXIN() : H5()
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
const system = uni.getSystemInfoSync()
|
||||
if (system.platform == 'ios') {
|
||||
return IOS()
|
||||
} else {
|
||||
return ANDROID()
|
||||
}
|
||||
// #endif
|
||||
return OTHER()
|
||||
}
|
||||
|
||||
export const client = getClient()
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* 设备信息采集 + 持久化 device_id
|
||||
*/
|
||||
import { registerDevice, type DeviceRegisterPayload } from '@/api/device'
|
||||
|
||||
const DEVICE_ID_KEY = 'app_device_id'
|
||||
|
||||
/** 生成或读取持久化的 device_id(UUID v4 简化版) */
|
||||
export function getOrCreateDeviceId(): string {
|
||||
try {
|
||||
const cached = uni.getStorageSync(DEVICE_ID_KEY)
|
||||
if (cached) return cached
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
const id = generateUUID()
|
||||
try {
|
||||
uni.setStorageSync(DEVICE_ID_KEY, id)
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
function generateUUID(): string {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0
|
||||
const v = c === 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
/** 当前平台标识 */
|
||||
export function getPlatform(): string {
|
||||
// #ifdef H5
|
||||
return 'h5'
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
return 'mp_weixin'
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
const sys = uni.getSystemInfoSync()
|
||||
const p = (sys.platform || '').toLowerCase()
|
||||
if (p === 'ios') return 'ios'
|
||||
if (p === 'android') return 'android'
|
||||
if (p === 'windows') return 'windows'
|
||||
if (p === 'mac') return 'mac'
|
||||
return p || 'app'
|
||||
} catch (e) {
|
||||
return 'app'
|
||||
}
|
||||
// #endif
|
||||
return 'unknown'
|
||||
}
|
||||
|
||||
/** 收集设备信息 */
|
||||
export function collectDeviceInfo(): DeviceRegisterPayload {
|
||||
const deviceId = getOrCreateDeviceId()
|
||||
const platform = getPlatform()
|
||||
|
||||
let sys: any = {}
|
||||
try {
|
||||
sys = uni.getSystemInfoSync()
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
let networkType = ''
|
||||
try {
|
||||
// 同步获取(部分平台不支持)
|
||||
// @ts-ignore
|
||||
const net = (uni as any).getNetworkType?.sync?.()
|
||||
if (net && net.networkType) networkType = net.networkType
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return {
|
||||
device_id: deviceId,
|
||||
platform,
|
||||
system: [sys.osName, sys.osVersion || sys.system].filter(Boolean).join(' '),
|
||||
model: sys.model || sys.deviceModel || '',
|
||||
brand: sys.brand || sys.deviceBrand || '',
|
||||
app_version: sys.appVersion || '',
|
||||
app_version_code: Number(sys.appWgtVersion || sys.appVersionCode || 0) || 0,
|
||||
screen_width: sys.screenWidth || sys.windowWidth || 0,
|
||||
screen_height: sys.screenHeight || sys.windowHeight || 0,
|
||||
network_type: networkType,
|
||||
language: sys.language || sys.osLanguage || '',
|
||||
timezone: (typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : '') || '',
|
||||
}
|
||||
}
|
||||
|
||||
/** 上报设备信息(失败不影响主流程) */
|
||||
export async function reportDevice(): Promise<void> {
|
||||
try {
|
||||
const payload = collectDeviceInfo()
|
||||
|
||||
// 异步获取网络类型并补上
|
||||
try {
|
||||
await new Promise<void>((resolve) => {
|
||||
uni.getNetworkType({
|
||||
success: (res) => {
|
||||
payload.network_type = res.networkType || ''
|
||||
resolve()
|
||||
},
|
||||
fail: () => resolve(),
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
await registerDevice(payload)
|
||||
} catch (e) {
|
||||
console.warn('[device] 设备信息上报失败:', e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @description: 开发模式
|
||||
*/
|
||||
export function isDevMode(): boolean {
|
||||
return import.meta.env.DEV
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 生成模式
|
||||
*/
|
||||
export function isProdMode(): boolean {
|
||||
return import.meta.env.PROD
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export async function saveImageToPhotosAlbum(url: string) {
|
||||
if (!url) return uni.$u.toast('图片错误')
|
||||
//#ifdef H5
|
||||
uni.$u.toast('长按图片保存')
|
||||
//#endif
|
||||
//#ifndef H5
|
||||
try {
|
||||
const res: any = await uni.downloadFile({ url, timeout: 10000 })
|
||||
await uni.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath
|
||||
})
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error: any) {
|
||||
uni.$u.toast(error.errMsg || '保存失败')
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export class MiddleWare {
|
||||
private middleware = new Map()
|
||||
private nextIterator = this.middleware.values()
|
||||
use(fn: any) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw 'middleware must be a function'
|
||||
}
|
||||
this.middleware.set(fn, fn)
|
||||
return this
|
||||
}
|
||||
private next(params?: any): any {
|
||||
const middleware = this.nextIterator.next().value
|
||||
if (middleware) {
|
||||
middleware.call(this, this.next.bind(this), params)
|
||||
}
|
||||
}
|
||||
run() {
|
||||
this.nextIterator = this.middleware.values()
|
||||
this.next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { PayStatusEnum } from '@/enums/appEnums'
|
||||
import { handleClientEvent } from '../client'
|
||||
|
||||
export class Alipay {
|
||||
init(name: string, pay: any) {
|
||||
pay[name] = this
|
||||
}
|
||||
openNewPage(options: any) {
|
||||
uni.navigateBack()
|
||||
const alipayPage = window.open('', '_self')!
|
||||
alipayPage.document.body.innerHTML = options
|
||||
alipayPage.document.forms[0].submit()
|
||||
}
|
||||
async run(options: any) {
|
||||
try {
|
||||
const res = await handleClientEvent({
|
||||
H5: () => {
|
||||
return new Promise((resolve) => {
|
||||
this.openNewPage(options)
|
||||
resolve(PayStatusEnum.PENDING)
|
||||
})
|
||||
},
|
||||
OA_WEIXIN: () => {
|
||||
return new Promise((resolve) => {
|
||||
this.openNewPage(options)
|
||||
resolve(PayStatusEnum.PENDING)
|
||||
})
|
||||
},
|
||||
ANDROID: () => {
|
||||
// const option =
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
provider: 'alipay',
|
||||
orderInfo: options,
|
||||
success() {
|
||||
resolve(PayStatusEnum.SUCCESS)
|
||||
},
|
||||
fail() {
|
||||
resolve(PayStatusEnum.FAIL)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
IOS: () => {
|
||||
// const option =
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.requestPayment({
|
||||
provider: 'alipay',
|
||||
orderInfo: options,
|
||||
success() {
|
||||
resolve(PayStatusEnum.SUCCESS)
|
||||
},
|
||||
fail() {
|
||||
resolve(PayStatusEnum.FAIL)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
return res
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Pay } from './pay'
|
||||
import { Alipay } from './alipay'
|
||||
import { Wechat } from './wechat'
|
||||
|
||||
// 支付方式
|
||||
enum PayWayEnum {
|
||||
BALANCE = 1,
|
||||
WECHAT = 2,
|
||||
ALIPAY = 3
|
||||
}
|
||||
|
||||
// 注入微信支付
|
||||
const wechat = new Wechat()
|
||||
Pay.inject(PayWayEnum[2], wechat)
|
||||
|
||||
// 注入支付宝支付
|
||||
const alipay = new Alipay()
|
||||
Pay.inject(PayWayEnum[3], alipay)
|
||||
|
||||
const pay = new Pay()
|
||||
export { pay, PayWayEnum }
|
||||
@@ -0,0 +1,28 @@
|
||||
import { PayWayEnum } from '.'
|
||||
|
||||
export class Pay {
|
||||
private static modules = new Map()
|
||||
static inject(name: string, module: any) {
|
||||
this.modules.set(name, module)
|
||||
}
|
||||
constructor() {
|
||||
//动态注入支付方式
|
||||
for (const [name, module] of Pay.modules.entries()) {
|
||||
module.init(name, this)
|
||||
}
|
||||
}
|
||||
|
||||
//调用支付
|
||||
async payment(payWay: PayWayEnum, options: any) {
|
||||
try {
|
||||
//@ts-ignore
|
||||
const module = this[PayWayEnum[payWay]]
|
||||
if (!module) {
|
||||
throw new Error(`can not find pay way ${payWay}`)
|
||||
}
|
||||
return await module.run(options)
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { PayStatusEnum } from '@/enums/appEnums'
|
||||
import { handleClientEvent } from '../client'
|
||||
//#ifdef H5
|
||||
import wechatOa from '../wechat'
|
||||
//#endif
|
||||
export class Wechat {
|
||||
init(name: string, pay: any) {
|
||||
pay[name] = this
|
||||
}
|
||||
|
||||
async run(options: any) {
|
||||
try {
|
||||
const res = await handleClientEvent({
|
||||
MP_WEIXIN: () => {
|
||||
return new Promise((resolve) => {
|
||||
uni.requestPayment({
|
||||
provider: 'wxpay',
|
||||
...options,
|
||||
success() {
|
||||
resolve(PayStatusEnum.SUCCESS)
|
||||
},
|
||||
fail() {
|
||||
resolve(PayStatusEnum.FAIL)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
OA_WEIXIN: () => {
|
||||
return new Promise((resolve) => {
|
||||
wechatOa
|
||||
.pay(options)
|
||||
.then(() => {
|
||||
resolve(PayStatusEnum.SUCCESS)
|
||||
})
|
||||
.catch(() => {
|
||||
resolve(PayStatusEnum.FAIL)
|
||||
})
|
||||
})
|
||||
},
|
||||
H5: () => {
|
||||
return new Promise((resolve) => {
|
||||
window.open(options, '_self')
|
||||
resolve(PayStatusEnum.PENDING)
|
||||
})
|
||||
}
|
||||
})
|
||||
return res
|
||||
} catch (error) {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { RequestTask } from './type'
|
||||
|
||||
const cancelerMap = new Map<string, RequestTask>()
|
||||
|
||||
export class RequestCancel {
|
||||
private static instance?: RequestCancel
|
||||
|
||||
static createInstance() {
|
||||
return this.instance ?? (this.instance = new RequestCancel())
|
||||
}
|
||||
add(url: string, requestTask: RequestTask) {
|
||||
this.remove(url)
|
||||
if (cancelerMap.has(url)) {
|
||||
cancelerMap.delete(url)
|
||||
}
|
||||
cancelerMap.set(url, requestTask)
|
||||
}
|
||||
remove(url: string) {
|
||||
if (cancelerMap.has(url)) {
|
||||
const requestTask = cancelerMap.get(url)
|
||||
requestTask && requestTask.abort()
|
||||
cancelerMap.delete(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const requestCancel = RequestCancel.createInstance()
|
||||
|
||||
export default requestCancel
|
||||
@@ -0,0 +1,146 @@
|
||||
import { merge } from 'lodash-es'
|
||||
import { isFunction } from '@vue/shared'
|
||||
import { HttpRequestOptions, RequestConfig, RequestOptions, UploadFileOption } from './type'
|
||||
import { RequestErrMsgEnum, RequestMethodsEnum } from '@/enums/requestEnums'
|
||||
import requestCancel from './cancel'
|
||||
|
||||
export default class HttpRequest {
|
||||
private readonly options: HttpRequestOptions
|
||||
constructor(options: HttpRequestOptions) {
|
||||
this.options = options
|
||||
}
|
||||
/**
|
||||
* @description 重新请求
|
||||
*/
|
||||
retryRequest(options: RequestOptions, config: RequestConfig) {
|
||||
const { retryCount, retryTimeout } = config
|
||||
if (!retryCount || options.method?.toUpperCase() == RequestMethodsEnum.POST) {
|
||||
return Promise.reject()
|
||||
}
|
||||
uni.showLoading({ title: '加载中...' })
|
||||
config.hasRetryCount = config.hasRetryCount ?? 0
|
||||
if (config.hasRetryCount >= retryCount) {
|
||||
return Promise.reject()
|
||||
}
|
||||
config.hasRetryCount++
|
||||
config.requestHooks.requestInterceptorsHook = (options) => options
|
||||
return new Promise((resolve) => setTimeout(resolve, retryTimeout))
|
||||
.then(() => this.request(options, config))
|
||||
.finally(() => uni.hideLoading())
|
||||
}
|
||||
/**
|
||||
* @description get请求
|
||||
*/
|
||||
get<T = any>(options: RequestOptions, config?: Partial<RequestConfig>): Promise<T> {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.GET }, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description post请求
|
||||
*/
|
||||
post<T = any>(options: RequestOptions, config?: Partial<RequestConfig>): Promise<T> {
|
||||
return this.request({ ...options, method: RequestMethodsEnum.POST }, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 上传图片
|
||||
*/
|
||||
uploadFile(options: UploadFileOption, config?: Partial<RequestConfig>) {
|
||||
let mergeOptions: RequestOptions = merge({}, this.options.requestOptions, options)
|
||||
const mergeConfig: RequestConfig = merge({}, this.options, config)
|
||||
const { requestInterceptorsHook, responseInterceptorsHook, responseInterceptorsCatchHook } =
|
||||
mergeConfig.requestHooks || {}
|
||||
if (requestInterceptorsHook && isFunction(requestInterceptorsHook)) {
|
||||
mergeOptions = requestInterceptorsHook(mergeOptions, mergeConfig)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
...mergeOptions,
|
||||
success: async (response) => {
|
||||
if (response.statusCode == 200) {
|
||||
response.data = JSON.parse(response.data)
|
||||
if (responseInterceptorsHook && isFunction(responseInterceptorsHook)) {
|
||||
try {
|
||||
response = await responseInterceptorsHook(response, mergeConfig)
|
||||
resolve(response)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
resolve(response)
|
||||
}
|
||||
},
|
||||
fail: async (err) => {
|
||||
if (
|
||||
responseInterceptorsCatchHook &&
|
||||
isFunction(responseInterceptorsCatchHook)
|
||||
) {
|
||||
reject(await responseInterceptorsCatchHook(mergeOptions, err))
|
||||
return
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
/**
|
||||
* @description 请求函数
|
||||
*/
|
||||
async request(options: RequestOptions, config?: Partial<RequestConfig>): Promise<any> {
|
||||
let mergeOptions: RequestOptions = merge({}, this.options.requestOptions, options)
|
||||
const mergeConfig: RequestConfig = merge({}, this.options, config)
|
||||
const { requestInterceptorsHook, responseInterceptorsHook, responseInterceptorsCatchHook } =
|
||||
mergeConfig.requestHooks || {}
|
||||
if (requestInterceptorsHook && isFunction(requestInterceptorsHook)) {
|
||||
mergeOptions = requestInterceptorsHook(mergeOptions, mergeConfig)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const rejectRequestError = async (err: any) => {
|
||||
if (
|
||||
responseInterceptorsCatchHook &&
|
||||
isFunction(responseInterceptorsCatchHook)
|
||||
) {
|
||||
try {
|
||||
reject(await responseInterceptorsCatchHook(mergeOptions, err))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
reject(err)
|
||||
}
|
||||
const requestTask = uni.request({
|
||||
...mergeOptions,
|
||||
async success(response) {
|
||||
if (responseInterceptorsHook && isFunction(responseInterceptorsHook)) {
|
||||
try {
|
||||
response = await responseInterceptorsHook(response, mergeConfig)
|
||||
resolve(response)
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
resolve(response)
|
||||
},
|
||||
fail: async (err) => {
|
||||
if (err.errMsg == RequestErrMsgEnum.TIMEOUT) {
|
||||
this.retryRequest(mergeOptions, mergeConfig)
|
||||
.then((res) => resolve(res))
|
||||
.catch(() => rejectRequestError(err))
|
||||
return
|
||||
}
|
||||
rejectRequestError(err)
|
||||
},
|
||||
complete(err) {
|
||||
if (err.errMsg !== RequestErrMsgEnum.ABORT) {
|
||||
requestCancel.remove(options.url)
|
||||
}
|
||||
}
|
||||
})
|
||||
const { ignoreCancel } = mergeConfig
|
||||
!ignoreCancel && requestCancel.add(options.url, requestTask)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import HttpRequest from './http'
|
||||
import { merge } from 'lodash-es'
|
||||
import { HttpRequestOptions, RequestHooks } from './type'
|
||||
import { getToken } from '../auth'
|
||||
import { RequestCodeEnum, RequestMethodsEnum } from '@/enums/requestEnums'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import appConfig from '@/config'
|
||||
import { getClient } from '../client'
|
||||
import cache from '@/utils/cache'
|
||||
import { BACK_URL } from '@/enums/constantEnums'
|
||||
|
||||
let isLoginModalShowing = false
|
||||
|
||||
function showLoginModal() {
|
||||
if (isLoginModalShowing) return
|
||||
isLoginModalShowing = true
|
||||
// 先关闭可能存在的 toast/loading,否则 showModal 不会弹出
|
||||
uni.hideToast()
|
||||
uni.hideLoading()
|
||||
setTimeout(() => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '该功能需要登录,是否前往登录?',
|
||||
confirmText: '去登录',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
const pages = getCurrentPages()
|
||||
const current = pages[pages.length - 1]
|
||||
if (current) {
|
||||
const route = '/' + current.route
|
||||
const options = (current as any).options || {}
|
||||
const query = Object.keys(options)
|
||||
.map((k) => `${k}=${options[k]}`)
|
||||
.join('&')
|
||||
cache.set(BACK_URL, query ? `${route}?${query}` : route)
|
||||
}
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
isLoginModalShowing = false
|
||||
}
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const requestHooks: RequestHooks = {
|
||||
requestInterceptorsHook(options, config) {
|
||||
const { urlPrefix, baseUrl, withToken, isAuth } = config
|
||||
options.header = options.header ?? {}
|
||||
if (urlPrefix) {
|
||||
options.url = `${urlPrefix}${options.url}`
|
||||
}
|
||||
if (baseUrl) {
|
||||
options.url = `${baseUrl}${options.url}`
|
||||
}
|
||||
const token = getToken()
|
||||
// 有token就带上,无token则不传
|
||||
if (token && !options.header.token) {
|
||||
options.header.token = token
|
||||
}
|
||||
options.header.version = appConfig.version
|
||||
// options.header.terminal = getClient();
|
||||
return options
|
||||
},
|
||||
async responseInterceptorsHook(response, config) {
|
||||
const { isTransformResponse, isReturnDefaultResponse, isAuth } = config
|
||||
|
||||
//返回默认响应,当需要获取响应头及其他数据时可使用
|
||||
if (isReturnDefaultResponse) {
|
||||
return response
|
||||
}
|
||||
// 是否需要对数据进行处理
|
||||
if (!isTransformResponse) {
|
||||
return response.data
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
const { code, data, msg, show } = response.data as any
|
||||
switch (code) {
|
||||
case RequestCodeEnum.SUCCESS:
|
||||
msg && show && uni.$u.toast(msg)
|
||||
return data
|
||||
case RequestCodeEnum.FAILED:
|
||||
if (msg && (/token/i.test(msg) || /登录/i.test(msg))) {
|
||||
if (userStore.isLogin || isAuth) {
|
||||
userStore.logout()
|
||||
showLoginModal()
|
||||
}
|
||||
} else if (msg) {
|
||||
uni.$u.toast(msg)
|
||||
}
|
||||
return Promise.reject(msg)
|
||||
|
||||
case RequestCodeEnum.TOKEN_INVALID:
|
||||
if (userStore.isLogin || isAuth) {
|
||||
userStore.logout()
|
||||
showLoginModal()
|
||||
}
|
||||
return Promise.reject(msg)
|
||||
|
||||
default:
|
||||
return data
|
||||
}
|
||||
},
|
||||
async responseInterceptorsCatchHook(options, error) {
|
||||
if (options.method?.toUpperCase() == RequestMethodsEnum.POST) {
|
||||
uni.$u.toast('请求失败,请重试')
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultOptions: HttpRequestOptions = {
|
||||
requestOptions: {
|
||||
timeout: appConfig.timeout
|
||||
},
|
||||
baseUrl: appConfig.baseUrl,
|
||||
//是否返回默认的响应
|
||||
isReturnDefaultResponse: false,
|
||||
// 需要对返回数据进行处理
|
||||
isTransformResponse: true,
|
||||
// 接口拼接地址
|
||||
urlPrefix: 'api',
|
||||
// 忽略重复请求
|
||||
ignoreCancel: false,
|
||||
// 是否携带token
|
||||
withToken: true,
|
||||
isAuth: false,
|
||||
retryCount: 2,
|
||||
retryTimeout: 1000,
|
||||
requestHooks: requestHooks
|
||||
}
|
||||
|
||||
function createRequest(opt?: HttpRequestOptions) {
|
||||
return new HttpRequest(
|
||||
// 深度合并
|
||||
merge(defaultOptions, opt || {})
|
||||
)
|
||||
}
|
||||
const request = createRequest()
|
||||
export default request
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
export type RequestOptions = UniApp.RequestOptions
|
||||
export type ResponseResult =
|
||||
| UniApp.RequestSuccessCallbackResult
|
||||
| UniApp.UploadFileSuccessCallbackResult
|
||||
export type RequestOptionsResponseError = UniApp.GeneralCallbackResult
|
||||
export type RequestTask = UniApp.RequestTask
|
||||
export type UploadFileOption = UniApp.UploadFileOption
|
||||
export interface HttpRequestOptions extends RequestConfig {
|
||||
requestOptions: Partial<RequestOptions>
|
||||
}
|
||||
|
||||
export interface RequestConfig {
|
||||
baseUrl: string
|
||||
requestHooks: RequestHooks
|
||||
isReturnDefaultResponse: boolean
|
||||
isTransformResponse: boolean
|
||||
urlPrefix: string
|
||||
ignoreCancel: boolean
|
||||
withToken: boolean
|
||||
isAuth: boolean
|
||||
retryCount: number
|
||||
retryTimeout: number
|
||||
hasRetryCount?: number
|
||||
}
|
||||
|
||||
export interface RequestHooks {
|
||||
requestInterceptorsHook?(options: RequestOptions, config: RequestConfig): RequestOptions
|
||||
responseInterceptorsHook?(response: ResponseResult, config: RequestConfig): any
|
||||
responseInterceptorsCatchHook?(options: RequestOptions, error: any): any
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import colors from 'css-color-function'
|
||||
const lightConfig = {
|
||||
'dark-2': 'shade(20%)',
|
||||
'light-3': 'tint(30%)',
|
||||
'light-5': 'tint(50%)',
|
||||
'light-7': 'tint(70%)',
|
||||
'light-9': 'tint(90%)'
|
||||
}
|
||||
|
||||
const darkConfig = {
|
||||
'light-3': 'shade(20%)',
|
||||
'light-5': 'shade(30%)',
|
||||
'light-7': 'shade(50%)',
|
||||
'light-9': 'shade(70%)',
|
||||
'dark-2': 'tint(20%)'
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jason
|
||||
* @description 用于生成主题的行为变量
|
||||
* 可选值有primary、success、warning、error、info
|
||||
*/
|
||||
|
||||
export const generateVarsMap = (
|
||||
color: string,
|
||||
type = 'primary',
|
||||
isDark = false
|
||||
) => {
|
||||
const colors = {
|
||||
[`--color-${type}`]: color
|
||||
}
|
||||
const config: Record<string, string> = isDark ? darkConfig : lightConfig
|
||||
for (const key in config) {
|
||||
colors[`--color-${type}-${key}`] = `color(${color} ${config[key]})`
|
||||
}
|
||||
return colors
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jason
|
||||
* @description 生成主题
|
||||
*/
|
||||
export const generateVars = (
|
||||
options: Record<string, string>,
|
||||
extra: Record<string, string> = {},
|
||||
isDark = false
|
||||
) => {
|
||||
const varsMap: Record<string, string> = Object.keys(options).reduce(
|
||||
(prev, key) => {
|
||||
return Object.assign(
|
||||
prev,
|
||||
generateVarsMap(options[key], key, isDark)
|
||||
)
|
||||
},
|
||||
extra
|
||||
)
|
||||
|
||||
const vars = Object.keys(varsMap).reduce((prev, key) => {
|
||||
const color = colors.convert(varsMap[key])
|
||||
return `${prev}${key}:${color};`
|
||||
}, '')
|
||||
return vars
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import cache from '@/utils/cache'
|
||||
import { BACK_URL } from '@/enums/constantEnums'
|
||||
|
||||
/**
|
||||
* 检查是否已登录,未登录则弹窗确认后跳转登录页
|
||||
* @returns Promise<boolean> true=已登录可继续,false=未登录已拦截
|
||||
*/
|
||||
export function checkLogin(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const userStore = useUserStore()
|
||||
if (userStore.isLogin) {
|
||||
resolve(true)
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '该功能需要登录,是否前往登录?',
|
||||
confirmText: '去登录',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 记录当前页面,登录后可返回
|
||||
const pages = getCurrentPages()
|
||||
const current = pages[pages.length - 1]
|
||||
if (current) {
|
||||
const route = '/' + current.route
|
||||
const options = (current as any).options || {}
|
||||
const query = Object.keys(options)
|
||||
.map((k) => `${k}=${options[k]}`)
|
||||
.join('&')
|
||||
cache.set(BACK_URL, query ? `${route}?${query}` : route)
|
||||
}
|
||||
uni.navigateTo({ url: '/pages/login/login' })
|
||||
}
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import {isObject} from '@vue/shared'
|
||||
import {getToken} from './auth'
|
||||
import {parseQuery} from "uniapp-router-next";
|
||||
|
||||
/**
|
||||
* @description 获取元素节点信息(在组件中的元素必须要传ctx)
|
||||
* @param { String } selector 选择器 '.app' | '#app'
|
||||
* @param { Boolean } all 是否多选
|
||||
* @param { ctx } context 当前组件实例
|
||||
*/
|
||||
export const getRect = (selector: string, all = false, context?: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let qurey = uni.createSelectorQuery()
|
||||
if (context) {
|
||||
qurey = uni.createSelectorQuery().in(context)
|
||||
}
|
||||
qurey[all ? 'selectAll' : 'select'](selector)
|
||||
.boundingClientRect(function (rect) {
|
||||
if (all && Array.isArray(rect) && rect.length) {
|
||||
return resolve(rect)
|
||||
}
|
||||
if (!all && rect) {
|
||||
return resolve(rect)
|
||||
}
|
||||
reject('找不到元素')
|
||||
})
|
||||
.exec()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取当前页面实例
|
||||
*/
|
||||
export function currentPage() {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
return currentPage || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 后台选择链接专用跳转
|
||||
*/
|
||||
interface Link {
|
||||
path: string
|
||||
name?: string
|
||||
type: string
|
||||
canTab: boolean
|
||||
query?: Record<string, any>
|
||||
}
|
||||
|
||||
export enum LinkTypeEnum {
|
||||
'SHOP_PAGES' = 'shop',
|
||||
'CUSTOM_LINK' = 'custom',
|
||||
'MINI_PROGRAM' = 'mini_program'
|
||||
}
|
||||
|
||||
export function navigateTo(link: Link, navigateType: 'navigateTo' | 'switchTab' | 'reLaunch' = 'navigateTo') {
|
||||
// 如果是小程序跳转
|
||||
if (link.type === LinkTypeEnum.MINI_PROGRAM) {
|
||||
navigateToMiniProgram(link)
|
||||
return
|
||||
}
|
||||
|
||||
const url = link?.query ? `${link.path}?${objectToQuery(link?.query)}` : link.path;
|
||||
|
||||
(navigateType == 'switchTab' || link.canTab) && uni.switchTab({url})
|
||||
navigateType == 'navigateTo' && uni.navigateTo({url})
|
||||
navigateType == 'reLaunch' && uni.reLaunch({url})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 小程序跳转
|
||||
* @param link 跳转信息,由装修数据进行输入
|
||||
*/
|
||||
export function navigateToMiniProgram(link: Link) {
|
||||
const query = link.query;
|
||||
// #ifdef H5
|
||||
window.open(
|
||||
`weixin://dl/business/?appid=${query?.appId}&path=${query?.path}&env_version=${query?.env_version}&query=${encodeURIComponent(query?.query)}`
|
||||
)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.navigateToMiniProgram({
|
||||
appId: query?.appId,
|
||||
path: query?.path,
|
||||
extraData: parseQuery(query?.query),
|
||||
envVersion: query?.env_version,
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将一个数组分成几个同等长度的数组
|
||||
* @param { Array } array[分割的原数组]
|
||||
* @param { Number } size[每个子数组的长度]
|
||||
*/
|
||||
export const sliceArray = (array: any[], size: number) => {
|
||||
const result = []
|
||||
for (let x = 0; x < Math.ceil(array.length / size); x++) {
|
||||
const start = x * size
|
||||
const end = start + size
|
||||
result.push(array.slice(start, end))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 是否为空
|
||||
* @param {unknown} value
|
||||
* @return {Boolean}
|
||||
*/
|
||||
export const isEmpty = (value: unknown) => {
|
||||
return value == null && typeof value == 'undefined'
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 对象格式化为Query语法
|
||||
* @param { Object } params
|
||||
* @return {string} Query语法
|
||||
*/
|
||||
export function objectToQuery(params: Record<string, any>): string {
|
||||
let query = ''
|
||||
for (const props of Object.keys(params)) {
|
||||
const value = params[props]
|
||||
const part = encodeURIComponent(props) + '='
|
||||
if (!isEmpty(value)) {
|
||||
console.log(encodeURIComponent(props), isObject(value))
|
||||
if (isObject(value)) {
|
||||
for (const key of Object.keys(value)) {
|
||||
if (!isEmpty(value[key])) {
|
||||
const params = props + '[' + key + ']'
|
||||
const subPart = encodeURIComponent(params) + '='
|
||||
query += subPart + encodeURIComponent(value[key]) + '&'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
query += part + encodeURIComponent(value) + '&'
|
||||
}
|
||||
}
|
||||
}
|
||||
return query.slice(0, -1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 添加单位
|
||||
* @param {String | Number} value 值 100
|
||||
* @param {String} unit 单位 px em rem
|
||||
*/
|
||||
export const addUnit = (value: string | number, unit = 'rpx') => {
|
||||
return !Object.is(Number(value), NaN) ? `${value}${unit}` : value
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 格式化输出价格
|
||||
* @param { string } price 价格
|
||||
* @param { string } take 小数点操作
|
||||
* @param { string } prec 小数位补
|
||||
*/
|
||||
export function formatPrice({price, take = 'all', prec = undefined}: any) {
|
||||
let [integer, decimals = ''] = (price + '').split('.')
|
||||
|
||||
// 小数位补
|
||||
if (prec !== undefined) {
|
||||
const LEN = decimals.length
|
||||
for (let i = prec - LEN; i > 0; --i) decimals += '0'
|
||||
decimals = decimals.substr(0, prec)
|
||||
}
|
||||
|
||||
switch (take) {
|
||||
case 'int':
|
||||
return integer
|
||||
case 'dec':
|
||||
return decimals
|
||||
case 'all':
|
||||
return integer + '.' + decimals
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 组合异步任务
|
||||
* @param { string } task 异步任务
|
||||
*/
|
||||
|
||||
export function series(...task: Array<(_arg: any) => any>) {
|
||||
return function (): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const iteratorTask = task.values()
|
||||
const next = (res?: any) => {
|
||||
const nextTask = iteratorTask.next()
|
||||
if (nextTask.done) {
|
||||
resolve(res)
|
||||
} else {
|
||||
Promise.resolve(nextTask.value(res)).then(next).catch(reject)
|
||||
}
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import wx from 'weixin-js-sdk'
|
||||
import { getWxCodeUrl } from '@/api/account'
|
||||
import { isAndroid } from './client'
|
||||
import { wxJsConfig } from '@/api/app'
|
||||
import { objectToQuery } from './util'
|
||||
export enum UrlScene {
|
||||
LOGIN = 'login',
|
||||
PC_LOGIN = 'pcLogin',
|
||||
BIND_WX = 'bindWx',
|
||||
BASE = 'base'
|
||||
}
|
||||
const wechatOa = {
|
||||
_authData: {
|
||||
code: '',
|
||||
scene: ''
|
||||
},
|
||||
setAuthData(data: any = {}) {
|
||||
this._authData = data
|
||||
},
|
||||
getAuthData() {
|
||||
return this._authData
|
||||
},
|
||||
getSignLink() {
|
||||
if (typeof window.signLink === 'undefined' || window.signLink === '') {
|
||||
window.signLink = location.href.split('#')[0]
|
||||
}
|
||||
return isAndroid() ? location.href.split('#')[0] : window.signLink
|
||||
},
|
||||
getUrl(
|
||||
scene: UrlScene,
|
||||
scope = 'snsapi_userinfo',
|
||||
extra = {}
|
||||
): Promise<void> {
|
||||
const currentUrl = `${location.href}${
|
||||
location.search ? '&' : '?'
|
||||
}scene=${scene || ''}&${objectToQuery(extra)}`
|
||||
return new Promise((resolve, reject) => {
|
||||
getWxCodeUrl({
|
||||
url: currentUrl,
|
||||
scope
|
||||
}).then((res) => {
|
||||
location.href = res.url
|
||||
resolve()
|
||||
}, reject)
|
||||
})
|
||||
},
|
||||
config() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wxJsConfig({
|
||||
url: this.getSignLink()
|
||||
}).then((res) => {
|
||||
wx.config({
|
||||
...res,
|
||||
success: () => {
|
||||
resolve('success')
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject('wx config is fail')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
miniProgram: wx.miniProgram,
|
||||
ready(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.ready(() => {
|
||||
resolve()
|
||||
})
|
||||
wx.error(() => {
|
||||
reject()
|
||||
})
|
||||
})
|
||||
},
|
||||
pay(options: Record<any, any>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ready()
|
||||
.then(() => {
|
||||
wx.chooseWXPay({
|
||||
timestamp: options.timeStamp,
|
||||
nonceStr: options.nonceStr,
|
||||
package: options.package,
|
||||
signType: options.signType,
|
||||
paySign: options.paySign,
|
||||
success: (res: any) => {
|
||||
if (res.errMsg === 'chooseWXPay:ok') {
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(res.errMsg)
|
||||
}
|
||||
},
|
||||
cancel: (res: any) => {
|
||||
reject(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
async share(options: Record<any, any>): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ready()
|
||||
.then(() => {
|
||||
const { title, link, imgUrl, desc } = options
|
||||
const shareApi = [
|
||||
'updateTimelineShareData',
|
||||
'updateAppMessageShareData'
|
||||
]
|
||||
for (const api of shareApi) {
|
||||
wx[api]({
|
||||
title: title,
|
||||
link: link,
|
||||
imgUrl: imgUrl,
|
||||
desc: desc,
|
||||
success() {
|
||||
resolve()
|
||||
},
|
||||
fail() {
|
||||
reject()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
},
|
||||
getAddress() {
|
||||
return new Promise((reslove, reject) => {
|
||||
this.ready().then(() => {
|
||||
wx.openAddress({
|
||||
success: (res: any) => {
|
||||
reslove(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
getLocation() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ready().then(() => {
|
||||
wx.getLocation({
|
||||
type: 'gcj02',
|
||||
success: (res: any) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
hideMenuItems(menuList: string[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ready().then(() => {
|
||||
wx.hideMenuItems({
|
||||
menuList,
|
||||
success: (res: any) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
showMenuItems(menuList: string[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.ready().then(() => {
|
||||
wx.showMenuItems({
|
||||
menuList,
|
||||
success: (res: any) => {
|
||||
resolve(res)
|
||||
},
|
||||
fail: (res: any) => {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default wechatOa
|
||||
Reference in New Issue
Block a user