新增
This commit is contained in:
@@ -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,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,140 @@
|
||||
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 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((err) => reject(err))
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
responseInterceptorsCatchHook &&
|
||||
isFunction(responseInterceptorsCatchHook)
|
||||
) {
|
||||
reject(await responseInterceptorsCatchHook(mergeOptions, err))
|
||||
return
|
||||
}
|
||||
reject(err)
|
||||
},
|
||||
complete(err) {
|
||||
if (err.errMsg !== RequestErrMsgEnum.ABORT) {
|
||||
requestCancel.remove(options.url)
|
||||
}
|
||||
}
|
||||
})
|
||||
const { ignoreCancel } = mergeConfig
|
||||
!ignoreCancel && requestCancel.add(options.url, requestTask)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
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";
|
||||
|
||||
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
|
||||
if (withToken && !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 { logout } = 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:
|
||||
uni.$u.toast(msg);
|
||||
return Promise.reject(msg);
|
||||
|
||||
case RequestCodeEnum.TOKEN_INVALID:
|
||||
logout();
|
||||
if (isAuth && !getToken()) {
|
||||
uni.navigateTo({
|
||||
url: "/pages/login/login",
|
||||
});
|
||||
}
|
||||
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,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