wo-user-toolkit-uniapp/index.js
2021-03-10 15:58:43 +08:00

377 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// uniapp people side tools
// #ifdef H5
// import device from 'current-device' // https://github.com/matthewhudson/current-device
// #endif
import './ican-H5Api.js' // 对齐H5Api: https://ext.dcloud.net.cn/plugin?id=415 // 注意要取消默认自带的 showToast https://uniapp.dcloud.io/api/system/clipboard?id=%e6%b3%a8%e6%84%8f
module.exports = {
RED: 'error',
GREEN: 'success',
BLUE: 'primary',
YELLOW: 'warning',
GREY: 'info',
BLACK_TOAST: 'default',
WHITE_BUTTON: 'default',
BACKEND: 'SERVER',
clog(...message) {
console.log(
'【【【【【【【【【【',
getCurrentPages().length > 0 ? getCurrentPages().pop().route : 'pages/Welcome', // 在首页时getApp() 或 getCurrentPages() 有可能获取不到。
...message,
'】】】】】】】】】】】'
)
},
sleep: (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms)),
async request({ method = 'POST', url, header = {}, data = {} }) {
url = this.makeUrl(url)
header._passtoken = uni.getStorageSync('_passtoken')
if (method === 'GET') {
// 如果不是 POST 方法,要额外把参数JSON化
for (let key in data) {
data[key] = JSON.stringify(data[key])
}
}
console.log('👇 👇 👇 👇 < Request > 👇 👇 👇 👇 ', { method, url, header, data }, '👆 👆 👆 👆 < /Request > 👆 👆 👆 👆')
let [error, response] = await uni.request({ method, url, header, data })
console.log('⬇️ ⬇️ ⬇️ ⬇️ < Response > ⬇️ ⬇️ ⬇️ ⬇️ ', response, '⬆️ ⬆️ ⬆️ ⬆️ < /Response > ⬆️ ⬆️ ⬆️ ⬆️')
return [error, response]
},
async callBackend({ backend = this.BACKEND, method = 'POST', apiVersion = 'api', apiWho, apiHow, apiWhat = {} }) {
console.log('👇 < BackendRequest > ', { apiWho, apiHow, apiWhat }, ' < /BackendRequest > 👇')
let result = {}
if (backend === 'CLOUD') {
let { /* success, header, requestedId, */ result: resultCloud = {} } = await uniCloud
.callFunction({
name: apiWho,
data: {
apiHow,
apiWhat,
_passtoken: uni.getStorageSync('_passtoken'),
// uniIdToken // uniCloud自动getStorageSync('uni_id_token')并传递为 uniIdToken也可自行传入 uniIdToken
},
})
.catch((exp) => {
// 断网或云端返回异常 {errMsg, stack} = error
if (/request:fail/.test(exp.errMsg)) {
return { result: { _state: 'CLIENT_BACKEND_BROKEN' } }
}else {
return { result: { _state: 'CLIENT_BACKEND_EXCEPTION' }}
}
})
result = resultCloud
} else {
if (method === 'GET') {
// 如果不是 POST 方法,要额外把参数JSON化
for (let key in apiWhat) {
apiWhat[key] = JSON.stringify(apiWhat[key])
}
}
let [error, { data: resultServer = {}, statusCode, header, errMsg } = {}] = await uni.request({
method,
url: this.makeUrl(`${apiVersion}/${apiWho}/${apiHow}`),
header: { _passtoken: uni.getStorageSync('_passtoken') },
data: apiWhat,
})
if (error) {
if (error.errMsg === 'request:fail') {
// 后台断线
result = { _state: 'CLIENT_BACKEND_BROKEN' }
} else (error.errMsg==='request:fail timeout') {
// 后台异常而超时返回
result = { _state: 'CLIENT_BACKEND_TIMEOUT' }
}else{
result = { _state: 'CLIENT_BACKEND_EXCEPTION' }
}
} else {
result = resultServer
}
}
console.log('👆 < BackendResult > ', result, '< /BackendResult > 👆')
return result
},
async pickupFile({
mediaType = 'image',
count = 1,
sizeType = ['original', 'compressed'],
sourceType = ['album', 'camera'],
url,
header = {},
formData = {},
name = 'file',
} = {}) {
if (uni.getStorageSync('_passtoken')) {
header._passtoken = uni.getStorageSync('_passtoken')
} else {
return [{ _ERROR: 'USER_OFFLINE', errMsg: 'offline user cannot upload files' }, null]
}
let filePath
if (mediaType === 'image') {
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
filePath = tempFilePaths[0]
} else if (mediaType === 'video') {
let [errorChoose, { tempFilePath }] = await uni.chooseVideo({ sourceType })
filePath = tempFilePath
} else {
return [{ _ERROR: 'UNKNOWN_MEDIATYPE' }, null]
}
if (filePath) {
for (let key in formData) {
// multer 不会自动处理 JSON 数据,必须前后端配合处理
formData[key] = JSON.stringify(formData[key])
}
uni.showLoading()
let [errorUpload, response] = await uni.uploadFile({ url: this.makeUrl(url), filePath, name, header, formData })
uni.hideLoading()
if (response && response.data) {
try {
response.data = JSON.parse(response.data)
} catch (exception) {}
}
return [errorUpload, response]
}
return [{ _ERROR: 'USER_CANCELED' }, null]
},
async pickupFile2Cloud({ mediaType = 'image', count = 1, sizeType = ['original', 'compressed'], sourceType = ['album', 'camera'], maxDuration } = {}) {
if (!uni.getStorageSync('_passtoken')) {
return { _state: 'USER_OFFLINE', errMsg: 'offline user cannot upload files' }
}
let filePath,
cloudPath,
systemInfo = this.getSystemInfo()
if (mediaType === 'image') {
let [errorChoose, { tempFilePaths, tempFiles } = {}] = await uni.chooseImage({ count, sizeType, sourceType })
// uni.showModal({ title: 'tempFilePaths[0]=' + tempFilePaths[0] })
filePath = tempFilePaths[0] // 在 H5 上并不是文件路径名,而是类似 "blob:http://localhost:8080/f0d3e54d-0694-4803-8097-641d76a10b0d“。
// #ifndef H5
// let [errorGetImageInfo, { path, width, height, orientation, type }] = await uni.getImageInfo({ src: filePath })
// cloudPath = path // 完整路径,包含后缀名。形如 file:///var/mobile/Containers/Data/Application/55A76332-44F5-4D5F-A9F6-3F857D584883/Documents/Pandora/apps/D064A425A8BEC13F9D8F741B98B37BC5/doc/uniapp_temp_1598593902955/compressed/1598593925815.png
cloudPath = `APP_${systemInfo.platform}__${filePath}` // 在 iOS 上是 "_doc/uniapp_temp_1598593902955/compressed/1598593925815.png", 有时还包含从 file:/// 开始的完整路径名
// #endif
// #ifdef H5
cloudPath = `H5_${systemInfo.platform}_${systemInfo.browser}__${tempFiles[0].name}` // name is available in H5 only. 只包含文件名和后缀名,不包含路径。
// #endif
} else if (mediaType === 'video') {
let [errorChoose, { tempFilePath, tempFile, duration, size, width, height, name }] = await uni.chooseVideo({ sourceType, maxDuration })
// uni.showModal({ title: 'tempFilePath=' + tempFilePath })
filePath = tempFilePath // 在 iOS 上形如 "file:///var/mobile/Containers/Data/Application/55A76332-44F5-4D5F-A9F6-3F857D584883/Documents/Pandora/apps/26B43CD2F587D37FC6799108434A6F84/doc/uniapp_temp_1598596171580/gallery/IMG_3082.MOV"
// #ifndef H5
cloudPath = `APP_${systemInfo.platform}_dur${duration}__${filePath}`
// #endif
// #ifdef H5
cloudPath = `H5_${systemInfo.platform}_${systemInfo.browser}_dur${duration}__${name}` // tempFile and name are H5 only
// #endif
// iOS 上测试filePath 为 *.MOV而阿里云只允许 *.mp4, 所以添加 .mp4 后缀。参见 https://uniapp.dcloud.net.cn/uniCloud/storage?id=clouduploadfile
// 20200915测试阿里云支持上传 *.mov 了。
if (!/\.(mp4|mov)$/i.test(cloudPath)) cloudPath = cloudPath + '.mp4'
} else {
return { _state: 'UNKNOWN_MEDIA_TYPE' }
}
if (process.env.NODE_ENV === 'development') {
cloudPath = 'dev_' + cloudPath
}
if (filePath) {
uni.showLoading()
const { fileID, requestId } = await uniCloud.uploadFile({
filePath: filePath,
cloudPath: cloudPath, // 关键是要具有文件格式后缀名,这样可以保持阿里云下载链接也用这个后缀名。
fileType: mediaType, // = image, video, audio
onUploadProgress: function (progressEvent) {
// console.log(progressEvent)
var percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total)
},
})
uni.hideLoading()
console.log('文件上传结果:', { fileID, requestId })
if (fileID) {
return { _state: 'SUCCESS', fileID, requestId }
}
}
return { _state: 'FAIL' }
},
openUrl(url) {
// #ifdef APP-PLUS
plus.runtime.openURL(url)
// #endif
// #ifdef H5
window.open(url, '_blank')
// #endif
},
getSystemInfo() {
let systemInfo = uni.getSystemInfoSync() // 包含 platform (ios|android|other), model (iPhone, iPad, Nexus 6, ...), system (iOS 11.0, Android 4.0, Other 0, ...) 等等
// #ifdef H5
systemInfo.environment = 'h5'
if (['ios', 'android'].indexOf(systemInfo.platform) >= 0) {
systemInfo.device = 'mobile'
} else {
systemInfo.device = 'desktop'
}
// if (device.mobile()){
// systemInfo.device = 'mobile'
// }else if (device.desktop()){
// systemInfo.device = 'desktop'
// }else if (device.tablet()){
// systemInfo.device = 'tablet'
// }
let userAgent = window.navigator.userAgent.toLowerCase()
systemInfo.browser =
/msie/.test(userAgent) && !/opera/.test(userAgent)
? 'msie'
: /opera/.test(userAgent)
? 'opera'
: /version.*safari/.test(userAgent)
? 'safari'
: /chrome/.test(userAgent)
? 'chrome'
: /gecko/.test(userAgent) && !/webkit/.test(userAgent)
? 'firefox'
: /micromessenger/.test(userAgent)
? 'wechat'
: 'unknown'
// #endif
// #ifdef APP-PLUS || APP-PLUS-NVUE
systemInfo.environment = 'app'
// #endif
// #ifdef MP
systemInfo.environment = 'mp'
// 细分成 WEIXIN, ...
// #endif
return systemInfo
},
/*
* uni.showToast({
icon=success (by default)/loading/none,
position(app only):center|top|bottom,
success, fail, complete // 函数调用后立刻发生不是在toast之后
})
* ucToast.show({
type=info (by default)/success/error/warning|loading,
position:top/bottom
})
* u-toast.show({
type=default (by default)/primary/success/error/warning/info,
position:center/top/bottom,
callback // 发生在 toast 之后
})
*/
showToast({ tool, type, image, title, duration = 2000, ...rest }) {
let pageNow = this.$store ? this : getCurrentPages().pop()
if (tool === 'uni' || !(pageNow.$refs && pageNow.$refs.toast)) {
// #ifdef APP-PLUS
uni.showToast({ icon: 'none', title, duration, ...rest })
// #endif
// #ifdef H5
uni.showToast({ icon: 'none', image, title, duration, ...rest })
// #endif
} else {
pageNow.$refs.toast.show({ type, title, duration, ...rest })
}
},
setBarTitles({ windowTitle, pageTitle } = {}) {
let page = this.$store ? this : getCurrentPages()[getCurrentPages().length - 1]
uni.setNavigationBarTitle({ title: pageTitle || page.i18nText[page.$store.state.i18n.mylang].tPageTitle })
// #ifdef H5
document.title = windowTitle || page.$store.getters['i18n/getAppName'] // 必须放在 setNavigationBarTitle 之后,否则会被其覆盖掉。
// #endif
if (page.$store._mutations['i18n/setTabbar']) page.$store.commit('i18n/setTabbar') // 必须要在有 tab 的页面里重置才有效果
},
localeText() {
let page = this.$store ? this : getCurrentPages()[getCurrentPages().length - 1]
return page.i18nText[page.$store.state.i18n.mylang]
},
formatMoney(value, decimal) {
return Number(value || 0).toFixed(decimal || 2) // Number(undefined)===NaN
},
formatPercent(value, decimal) {
return Number(value * 100 || 0).toFixed(decimal || 2)
},
formatDate(date, format) {
if (!(date instanceof Date)) {
if (typeof date === 'string' && /^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d$/.test(date)) {
// 这是从 typeorm 数据库得到的Date类型的值
date = date.replace(/-/g, '/') // safari 不支持 yyyy-mm-dd必须改成 yyyy/mm/dd
}
date = new Date(date)
}
if (!date.toJSON()) {
date = new Date()
}
format = format && typeof format === 'string' ? format : 'yyyy-mm-dd HH:MM:SS'
let o = {
'm+': date.getMonth() + 1, //月份
'q+': Math.floor((date.getMonth() + 3) / 3), //季度
'd+': date.getDate(), //日
'H+': date.getHours(), //小时
'M+': date.getMinutes(), //分
'S+': date.getSeconds(), //秒
s: date.getMilliseconds(), //毫秒
}
if (/(y+)/.test(format)) format = format.replace(RegExp.$1, `${date.getFullYear()}`.substr(4 - RegExp.$1.length))
for (var k in o) {
if (new RegExp(`(${k})`).test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length))
}
return format
},
hash(data, { hasher = 'sha256', salt, input = 'utf8', output = 'hex' } = {}) {
if (typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof DataView)) data = JSON.stringify(data)
if (salt && typeof salt === 'string') data = data + salt
let inputEncoding = input // my.INPUT_LIST.indexOf(option.input)>=0?option.input:my.INPUT // 'utf8', 'ascii' or 'latin1' for string data, default to utf8 if not specified; ignored for Buffer, TypedArray, or DataView.
let outputEncoding = output === 'buf' ? undefined : output // (my.OUTPUT_LIST.indexOf(output)>=0?output:my.OUTPUT) // option.output: 留空=》默认输出hex格式或者手动指定 'buf', hex', 'latin1' or 'base64'
return require('crypto').createHash(hasher).update(data, inputEncoding).digest(outputEncoding)
},
regcode2aiid(code) {
if (typeof code === 'string' && /^[a-zA-Z0-9]+$/.test(code)) {
const alphabet = 'e5fcdg3hqa4b1n0pij2rstuv67mwx89klyz'
const base = 16367
code = code.toLowerCase()
let len = code.length
let num = 0
for (let i = 0; i < len; i++) {
num += alphabet.indexOf(code[i]) * Math.pow(alphabet.length, i)
}
let aiid = num / (base - alphabet.length) - base
if (aiid >= 0 && Number.isInteger(aiid)) {
// 允许 aiid===0当第一个用户aiid==1登录时需要一个系统默认的邀请码。
return aiid
}
}
return null
},
}