bk-shop/src/utils/index.js

904 lines
22 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import Taro, { getCurrentInstance } from '@tarojs/taro'
import classNames from 'classnames'
import qs from 'qs'
import dayjs from 'dayjs'
import copy from 'copy-to-clipboard'
import S from '@/spx'
import { STATUS_TYPES_MAP, SG_ROUTER_PARAMS } from '@/consts'
import api from '@/api'
import configStore from '@/store'
import _get from 'lodash/get'
import _findKey from 'lodash/findKey'
import _pickBy from 'lodash/pickBy'
import _keys from 'lodash/keys'
import _isEmpty from 'lodash/isEmpty'
import _remove from 'lodash/remove'
import debounce from 'lodash/debounce'
import throttle from 'lodash/throttle'
import parse from 'mini-html-parser2'
import log from './log'
import canvasExp from './canvasExp'
import calCommonExp from './calCommonExp'
import entryLaunch from './entryLaunch'
import validate from './validate'
import checkAppVersion from './updateManager'
import linkPage from './linkPage'
const { store } = configStore()
export * from './platforms'
const isPrimitiveType = (val, type) => Object.prototype.toString.call(val) === type
export function isFunction (val) {
return isPrimitiveType(val, '[object Function]')
}
export function uniqueFunc (arr, uniId) {
const res = new Map()
return arr.filter((item) => !res.has(item[uniId]) && res.set(item[uniId], 1))
}
export function isNumber (val) {
return isPrimitiveType(val, '[object Number]')
}
export function isPointerEvent (val) {
return isPrimitiveType(val, '[object PointerEvent]')
}
export function isObject (val) {
return isPrimitiveType(val, '[object Object]')
}
export function isBoolean (val) {
return isPrimitiveType(val, '[object Boolean]')
}
export function isArray (arr) {
return Array.isArray(arr)
}
export function isString (val) {
return typeof val === 'string'
}
export function isEmpty (obj) {
return _isEmpty(obj)
}
export function removeItemInArray (array, fn) {
_remove(array, fn)
// return array
}
export function isObjectsValue (val) {
// 判断对象是否有值 true有 false
return val && Object.keys(val).length > 0
}
/** 在支付宝平台 */
export const isAlipay = Taro.getEnv() == Taro.ENV_TYPE.ALIPAY
/** 在微信平台 */
export const isWeixin = Taro.getEnv() == Taro.ENV_TYPE.WEAPP
/** 在H5平台 */
export const isWeb = Taro.getEnv() == Taro.ENV_TYPE.WEB
console.log('utils:', Taro.getEnv(), Taro.ENV_TYPE.APP)
/** 在APP平台 */
export const isAPP = () => Taro.getEnv() == Taro.ENV_TYPE.APP
// 云店
export const VERSION_STANDARD = process.env.APP_PLATFORM == 'standard'
// exc
export const VERSION_PLATFORM = process.env.APP_PLATFORM == 'platform'
// 官方商城
export const VERSION_B2C = process.env.APP_PLATFORM == 'b2c'
// 内购
export const VERSION_IN_PURCHASE = process.env.APP_PLATFORM == 'in_purchase'
export const getBrowserEnv = () => {
const ua = navigator.userAgent
// console.log( `user-agent:`, ua );
return {
trident: ua.indexOf('Trident') > -1, //IE内核
presto: ua.indexOf('Presto') > -1, //opera内核
webKit: ua.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') == -1, //火狐内核
mobile: !!ua.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1, //android终端
weixin: ua.match(/MicroMessenger/i),
qq: ua.match(/\sQQ/i) == ' qq', //是否QQ
isWeapp:
(ua.match(/MicroMessenger/i) && ua.match(/miniprogram/i)) ||
global.__wxjs_environment === 'miniprogram',
isAlipay: ua.match(/AlipayClient/i)
}
}
/** 在H5平台(微信浏览器) */
export const isWxWeb = isWeb && !!getBrowserEnv().weixin
export function isObjectValueEqual (a, b) {
var aProps = Object.getOwnPropertyNames(a)
var bProps = Object.getOwnPropertyNames(b)
if (aProps.length != bProps.length) {
return false
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i]
if (a[propName] !== b[propName]) {
return false
}
}
return true
}
export const isIphoneX = () => {
if (isWeixin) {
try {
const {
model,
system,
windowWidth,
windowHeight,
screenHeight,
screenWidth,
pixelRatio,
brand
} = Taro.getSystemInfoSync()
const { networkType } = Taro.getNetworkType()
let px = screenWidth / 750 //rpx换算px iphone51rpx=0.42px
Taro.$systemSize = {
windowWidth,
windowHeight,
screenHeight,
screenWidth,
model,
px,
pixelRatio,
brand,
system,
networkType
}
if (system.indexOf('iOS') !== -1) {
Taro.$system = 'iOS'
}
S.set('ipxClass', model.toLowerCase().indexOf('iphone x') >= 0 ? 'is-ipx' : '')
} catch (e) {
console.log(e)
}
}
}
// TODO: 验证方法在h5及边界情况稳定性
export function getCurrentRoute () {
const router = getCurrentInstance().router
// eslint-disable-next-line
const { $taroTimestamp, ...params } = router.params || {}
const path = router.path
const fullPath = `${path}${Object.keys(params).length > 0 ? '?' + qs.stringify(params) : ''}`
return {
path,
fullPath,
params
}
}
// 除以100以后的千分符
export function formatPriceToHundred (price) {
if (price) {
return (Number(price) / 100)
.toFixed(2)
.toString()
.replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
} else {
return 0
}
}
export function thousandthFormat (value) {
return (value.toString() + '').replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,')
}
export async function normalizeQuerys (params = {}) {
const { scene, ...rest } = params
const queryStr = decodeURIComponent(scene)
const obj = qs.parse(queryStr)
if (obj.sid || obj.share_id) {
const data = await api.wx.getShareId({
share_id: obj.sid || obj.share_id
})
return {
...rest,
...data
}
}
const ret = {
...rest,
...obj
}
return ret
}
export function pickBy (arr = [], keyMaps = {}) {
const picker = (item) => {
const ret = {}
Object.keys(keyMaps).forEach((key) => {
const val = keyMaps[key]
if (isString(val)) {
ret[key] = _get(item, val)
} else if (isFunction(val)) {
ret[key] = val(item)
} else if (isObject(val)) {
ret[key] = _get(item, val.key) || val.default
} else {
ret[key] = val
}
})
return ret
}
if (isArray(arr)) {
return arr.map(picker)
} else {
return picker(arr)
}
}
export function navigateTo (url, isRedirect) {
if (isObject(isRedirect) || isPointerEvent(isRedirect)) {
isRedirect = false
}
if (isRedirect) {
return Taro.redirectTo({ url })
}
return Taro.navigateTo({ url })
}
export function resolvePath (baseUrl, params = {}) {
const queryStr = typeof params === 'string' ? params : qs.stringify(params)
return `${baseUrl}${baseUrl.indexOf('?') >= 0 ? '&' : '?'}${queryStr}`
}
export function diffInDays (time1, time2) {
return Math.abs(dayjs(time1).diff(dayjs(time2), 'day'))
}
export function formatTime (time, formatter = 'YYYY-MM-DD') {
const newTime = time.toString().length < 13 ? time * 1000 : time
return dayjs(newTime).format(formatter)
}
export function formatDateTime (time, formatter = 'YYYY-MM-DD HH:mm:ss') {
const newTime = time.toString().length < 13 ? time * 1000 : time
return dayjs(newTime).format(formatter)
}
export function copyText (text, msg = '内容已复制') {
return new Promise((resolve, reject) => {
if (process.env.TARO_ENV === 'weapp') {
Taro.setClipboardData({
data: text,
success: resolve,
error: reject
})
} else {
if (isAlipay) {
console.log('copyText:text', text)
my.setClipboard({
text: text,
})
resolve(text)
return
}
if (copy(text)) {
S.toast(msg)
resolve(text)
} else {
reject()
}
}
})
}
export function calcTimer (totalSec) {
let remainingSec = totalSec
const dd = Math.floor(totalSec / 24 / 3600)
remainingSec -= dd * 3600 * 24
const hh = Math.floor(remainingSec / 3600)
remainingSec -= hh * 3600
const mm = Math.floor(remainingSec / 60)
remainingSec -= mm * 60
const ss = Math.floor(remainingSec)
return {
dd,
hh,
mm,
ss
}
}
export function resolveOrderStatus (status, isBackwards) {
if (isBackwards) {
return _findKey(STATUS_TYPES_MAP, (o) => o === status)
}
return STATUS_TYPES_MAP[status]
}
export function goToPage (page) {
// eslint-disable-next-line
const loc = location
page = page.replace(/^\//, '')
const url = `${loc.protocol}//${loc.host}/${page}`
console.log(url)
loc.href = url
}
export function maskMobile (mobile) {
return mobile.replace(/^(\d{2})(\d+)(\d{2}$)/, '$1******$3')
}
// 不可使用promise/async异步写法
export function authSetting (scope, succFn, errFn) {
Taro.getSetting({
success (res) {
const result = res.authSetting[isWeixin ? `scope.${scope}` : `${scope}`]
if (isWeixin) {
if (result === undefined) {
Taro.authorize({
scope: `scope.${scope}`
}).then(succFn, errFn)
} else if (!result) {
Taro.openSetting().then(succFn, errFn)
} else {
succFn()
}
} else if (isAlipay) {
// const alipayScope = {
// "album": "album",
// "writePhotosAlbum": "album"
// }
if (result === false) {
Taro.openSetting().then(succFn, errFn)
} else {
succFn()
}
}
},
fail (res) {
console.error(res)
}
})
}
export function imgCompression (url) {
const rule = '?imageView2/1/w/80'
return url + rule
}
export const browser = (() => {
if (process.env.TARO_ENV === 'h5') {
var ua = navigator.userAgent
return {
trident: ua.indexOf('Trident') > -1, //IE内核
presto: ua.indexOf('Presto') > -1, //opera内核
webKit: ua.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') == -1, //火狐内核
mobile: !!ua.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1, //android终端
weixin: ua.match(/MicroMessenger/i),
qq: ua.match(/\sQQ/i) == ' qq', //是否QQ
isWeapp:
(ua.match(/MicroMessenger/i) && ua.match(/miniprogram/i)) ||
global.__wxjs_environment === 'miniprogram',
isAlipay: ua.match(/AlipayClient/i)
}
}
})()
// 注入美洽客服插件
export const meiqiaInit = () => {
; (function (m, ei, q, i, a, j, s) {
m[i] =
m[i] ||
function () {
; (m[i].a = m[i].a || []).push(arguments)
}
; (j = ei.createElement(q)), (s = ei.getElementsByTagName(q)[0])
j.async = true
j.charset = 'UTF-8'
j.src = 'https://static.meiqia.com/dist/meiqia.js?_=t'
s.parentNode.insertBefore(j, s)
})(window, document, 'script', '_MEIQIA')
}
const redirectUrl = async (api, url, type = 'redirectTo') => {
if (!browser?.weixin) {
typeof url === 'string' && Taro[type]({ url })
return
}
let newUrl = getUrl(url)
let { redirect_url } = await api.wx.getredirecturl({ url: newUrl })
global.location.href = redirect_url
}
const getUrl = (url) => {
let href = global.location.href
let hrefList = href.split('/')
return `${hrefList[0]}//${hrefList[2]}${url}`
}
export function tokenParseH5 (token) {
try {
let base64Url = token.split('.')[1]
return JSON.parse(atob(base64Url))
} catch (e) {
return {}
}
}
export function tokenParse (token) {
var base64Url = token.split('.')[1]
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
console.log('Taro.base64ToArrayBuffer', base64)
var arr_base64 = Taro.base64ToArrayBuffer(base64)
arr_base64 = String.fromCharCode.apply(null, new Uint8Array(arr_base64))
var jsonPayload = decodeURIComponent(
arr_base64
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join('')
)
return JSON.parse(jsonPayload)
}
// 解析字符串
function getQueryVariable (herf) {
const url = herf.split('?')
let query = {}
if (url[1]) {
const str = url[1]
// const str = url.substr(1)
const pairs = str.split('&')
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split('=')
query[pair[0]] = pair[1]
}
}
return query
}
/** 是否是合法的color */
function validColor (color) {
var re1 = /^#([0-9a-f]{6}|[0-9a-f]{3})$/i
var re2 =
/^rgb\(([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\)$/i
var re3 =
/^rgba\(([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\,([0-9]|[0-9][0-9]|25[0-5]|2[0-4][0-9]|[0-1][0-9][0-9])\,(1|1.0|0.[0-9])\)$/i
return re2.test(color) || re1.test(color) || re3.test(color)
}
/**
* 导购埋点上报
* @param {
* event_type: {
* activeItemDetail 分享商品详情页
* activeSeedingDetail 分享种草详情页
* activeDiscountCoupon 分享优惠券
* activeCustomPage 分享自定义页面
* orderPaymentSuccess 订单支付成功
* }
* } data 新增上报数据
*/
export async function buriedPoint (data) {
const params = getCurrentInstance().router.params
let {
gu,
subtask_id = '',
dtid = '',
shop_code = '',
item_id = '',
smid = '',
gu_user_id = ''
} = await normalizeQuerys(params)
let employee_number = smid,
store_bn = ''
if (gu) {
;[employee_number, store_bn] = gu.split('_')
}
if (gu_user_id) {
employee_number = gu_user_id
}
// 任务埋点
if (subtask_id) {
const { distributor_id: shopId } = Taro.getStorageSync('curStore')
if (VERSION_STANDARD) {
dtid = shopId
}
const newData = {
employee_number,
store_bn,
subtask_id,
distributor_id: dtid,
shop_code,
item_id,
...data
}
api.wx.taskReportData(newData)
}
if (data.event_type && S.getAuthToken() && employee_number) {
const { userId } = Taro.getStorageSync('userinfo')
api.wx.interactiveReportData({
event_id: employee_number,
user_type: 'wechat',
user_id: userId,
event_type: data.event_type,
store_bn
})
}
}
/**
* 参数拼接
*
*/
export function paramsSplice (params) {
let str = ''
let arr = []
for (var key in params) {
let p = `${key}=${params[key]}`
arr.push(p)
}
str = arr.join('&')
return str
}
export function resolveFavsList (list, favs) {
return list.map((t) => {
const { item_id } = t
return {
...t,
is_fav: Boolean(favs[item_id])
}
})
}
// 判断是否在导购货架
export function isGoodsShelves () {
const system = Taro.getSystemInfoSync()
log.debug(`this system is: ${JSON.stringify(system)}`)
if (system && system.environment && system.environment === 'wxwork') {
return true
} else {
return false
}
}
export function styleNames (styles) {
if (!styles || typeof styles !== 'object') {
return '""'
}
let styleNames = ''
_keys(styles).forEach((key) => {
if (typeof styles[key] === 'string') {
styleNames += `${key}:${styles[key]};`
return
}
if (typeof styles[key] !== 'object' || styles[key]?.length === 0) {
return
}
let conditions = styles[key]
_keys(conditions).forEach((value) => {
if (
(typeof conditions[value] !== 'function' && conditions[value]) ||
(typeof conditions[value] === 'function' && conditions[value]())
) {
styleNames += `${key}:${value};`
return
}
})
})
return `${styleNames}`
}
export function getThemeStyle () {
const result = store.getState()
const { colorPrimary, colorMarketing, colorAccent, rgb } = result.sys
return {
'--color-primary': colorPrimary,
'--color-marketing': colorMarketing,
'--color-accent': colorAccent,
'--color-rgb': rgb
}
}
export function isNavbar () {
return isWeb && !getBrowserEnv().weixin
}
export const hasNavbar = isWeb && !getBrowserEnv().weixin
export function showToast (title, callback) {
Taro.showToast({
title,
icon: 'none'
})
setTimeout(() => {
callback?.()
}, 2000)
}
export function hex2rgb (hex) {
if (![4, 7].includes(hex.length)) {
throw new Error('格式错误')
}
let result = hex.slice(1)
// 如果是颜色叠值, 统一转换成6位颜色值
if (result.length === 3) {
result = result
.split('')
.map((a) => `${a}${a}`)
.join('')
}
const rgb = []
// 计算hex值
for (let i = 0, len = result.length; i < len; i += 2) {
rgb[i / 2] = getHexVal(result[i]) * 16 + getHexVal(result[i + 1])
}
function getHexVal (letter) {
let num = -1
switch (letter.toUpperCase()) {
case 'A':
num = 10
break
case 'B':
num = 11
break
case 'C':
num = 12
break
case 'D':
num = 13
break
case 'E':
num = 14
break
case 'F':
num = 15
break
}
if (num < 0) {
num = Number(letter)
}
return num
}
return rgb
}
export function exceedLimit ({ size: fileSize }) {
const size = fileSize / 1024 / 1024
return size > 15
}
function isBase64 (str) {
let isStr = str?.url ? str?.url : str
if (isStr.indexOf('data:') != -1 && isStr.indexOf('base64') != -1) {
return true
} else {
return false
}
}
//判断是否是商家入驻
const isMerchantModule = () => {
let pathname = isWeb ? location.pathname : getCurrentInstance()?.router?.path
return /\/subpages\/merchant/.test(pathname)
}
function isUndefined (val) {
return typeof val === 'undefined'
}
// 查询商家是否可用
const merchantIsvaild = async (parmas) => {
const { status } = await api.distribution.merchantIsvaild(parmas)
return status
}
export function getExtConfigData () {
const extConfig = Taro.getExtConfigSync ? Taro.getExtConfigSync() : {}
if (_isEmpty(extConfig)) {
return {
appid: process.env.APP_ID,
company_id: process.env.APP_COMPANY_ID,
wxa_name: process.env.APP_NAME,
youshutoken: process.env.APP_YOUSHU_TOKEN
}
} else {
return extConfig
}
}
const getDistributorId = (distribution_id) => {
const { sys, shop } = store.getState()
const { openStore } = sys
const {
shopInfo: { distributor_id, shop_id = 0 }
} = shop
if (VERSION_STANDARD) {
if (typeof distribution_id == 'undefined') {
// 小程序启动后URL是否携带店铺id
const { dtid } = Taro.getStorageSync(SG_ROUTER_PARAMS)
if (dtid) {
return dtid
} else {
return openStore ? distributor_id : shop_id
}
} else {
return distribution_id
}
} else {
return distribution_id || 0
}
}
/**
* 保留两个位小数不足补0
* @param { Number } value
*/
export const returnFloat = (value) => {
var value = Math.round(parseFloat(value) * 100) / 100
var s = value.toString().split('.')
if (s.length == 1) {
value = value.toString() + '.00'
return value
}
if (s.length > 1) {
if (s[1].length < 2) {
value = value.toString() + '0'
}
return value
}
}
export const isShowMarketPrice = (mktPrice) => {
return !isNaN(mktPrice) && mktPrice > 0
}
export const onEventChannel = (eventName, data) => {
const pages = Taro.getCurrentPages()
const current = pages[pages.length - 1]
const eventChannel = current.getOpenerEventChannel()
eventChannel.emit(eventName, data)
}
// 支付宝小程序取code
const alipayAutoLogin = () => {
return new Promise((resolve, reject) => {
my.getAuthCode({
scopes: 'auth_base',
success: (res) => {
const code = res.authCode
resolve({ code })
},
fail: (res) => {
console.error('[sp-login] alipayAutoLogin login fail:', res)
reject(res)
}
})
})
}
// 支付宝小程序支付
const requestAlipayminiPayment = (tradeNO) => {
return new Promise((resolve, reject) => {
my.tradePay({
tradeNO: tradeNO,
success: (res) => {
console.log('支付回调成功res', res)
resolve(res)
},
fail: (res) => {
console.log('支付回调失败res', res)
reject(res)
}
})
})
}
const htmlStringToNodeArray = (htmlString) => {
let nodeArray = null
parse(htmlString, (err, nodes) => {
if (!err) {
nodeArray = nodes
} else {
log.error('htmlStringToNodeArray error')
}
})
return nodeArray
}
const getCurrentPageRouteParams = () => {
const pages = Taro.getCurrentPages()
const options = {}
Object.keys(pages[pages.length - 1].options).forEach(key => {
if (key != '$taroTimestamp') {
options[key] = pages[pages.length - 1].options[key]
}
})
return options
}
const resolveStringifyParams = (params) => {
return qs.stringify(params)
}
const resolveUrlParamsParse = (url) => {
const res = {}
const n_url = decodeURIComponent(url) || ''
const paramArr = n_url.split('&') // 返回类似于 a=10&b=20&c=30
paramArr.forEach(item => {
const itemArr = item.split('=')
const key = itemArr[0]
const value = itemArr[1]
res[key] = value
})
return res
}
export {
classNames,
log,
debounce,
throttle,
calCommonExp,
canvasExp,
getQueryVariable,
validColor,
entryLaunch,
validate,
checkAppVersion,
linkPage,
redirectUrl,
isBase64,
isMerchantModule,
isUndefined,
merchantIsvaild,
getDistributorId,
alipayAutoLogin,
requestAlipayminiPayment,
htmlStringToNodeArray,
getCurrentPageRouteParams,
resolveStringifyParams,
resolveUrlParamsParse
}
export * from './platforms'
export * from './system'
export * from './store'