Files
cansnow 6720c15e30 27
2026-02-09 07:29:02 +08:00

560 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
//import i18n from '@/locales'
import base from '@/common/config';
import store from "@/store";
import IMSDK from "openim-uniapp-polyfill";
import CryptoJS from 'crypto-js';
import md5 from "md5";
// #ifdef APP
import {downloadFile} from "@/uni_modules/network-manage";
// #endif
const isString = (v)=> {
return typeof v === 'string' || v instanceof String;
},
isNumber=(v) =>{
return typeof v === 'number' && !isNaN(v);
},
isInteger=(v) =>{
return typeof v === 'number' && isFinite(v) && Math.floor(v) === v;
},
isBoolean=(v) =>{
return typeof v === 'boolean';
},
isArray=(v) =>{
return Array.isArray(v);
},
isObject=(v) =>{
return v !== null && typeof v === 'object' && !Array.isArray(v);
},
isFunction=(v) =>{
return typeof v === 'function';
},
isNull=(v) =>{
return v === null;
},
isUndefined=(v) =>{
return typeof v === 'undefined';
},
isSymbol=(v) =>{
return typeof v === 'symbol';
},
isDate=(v) =>{
return Object.prototype.toString.call(v) === '[object Date]';
},
isRegExp=(v) =>{
return Object.prototype.toString.call(v) === '[object RegExp]';
},
isError=(v) =>{
return v instanceof Error;
},
isMap=(v) =>{
return v instanceof Map;
},
isSet=(v) =>{
return v instanceof Set;
},
isWeakMap=(v) =>{
return v instanceof WeakMap;
},
isWeakSet=(v) => {
return v instanceof WeakSet;
},
_goto = (url,type) => {
//console.log(url);
type = type || '0'; //0 navigateTo 1 redirectTo
if(url){
if(isInteger(url)){
uni.navigateBack({
delta:url,
})
}else{
url+="";
if(url.substr(0,6) != '/pages'){
url='/pages'+url
}
if(type == '1'){
return uni.redirectTo({
url:url
});
}
if(type == '2'){
return uni.switchTab({
url:url
});
}
uni.navigateTo({
url:url
})
}
}
};
const showToast = (msg,url,icon) => {
//msg = i18n.t(msg);
// #ifdef APP
plus.nativeUI.closeToast();
plus.nativeUI.toast(msg,{
align:'center',
verticalAlign:"center",
style:"inline",
icon:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
iconWidth:24,
iconHeight:24
});
if(url){
setTimeout(()=>{
_goto(url);
},3000)
}
// #endif
// #ifndef APP
uni.showToast({
//image:icon=='error' ? '/static/img/common/error.png' : '/static/img/common/success.png',
icon:icon=='error' ? icon : 'success',
title:msg,
showToast:3000,
complete:()=>{
if(url){
setTimeout(()=>{
_goto(url);
},3000)
}
}
});
// #endif
}
const error = (msg,url) => {
showToast(msg,url,'error');
}
const success = (msg,url) => {
showToast(msg,url,'success');
}
const scan = ()=>{
uni.scanCode({
success(res){
/**
* result 所扫码的内容
scanType 所扫码的类型 App、微信小程序、百度小程序、QQ小程序、京东小程序、支付宝小程序
charSet 所扫码的字符集 App、微信小程序、百度小程序(所扫码的字符集,仅支持 Android 系统)、QQ小程序、京东小程序
path 当所扫的码为当前应用的合法二维码时,会返回此字段,内容为二维码携带的 path。 微信小程序、QQ小程序、京东小程序
rawData 原始数据,base64 编码 微信小程序、QQ小程序、京东小程序、支付宝小程序
code 扫码所得数据 支付宝小程序
qrCode 扫描二维码时返回二维码数据 支付宝小程序
barCode 扫描条形码时返回条形码数据 支付宝小程序
imageChannel 来源 支付宝小程序
*/
if(res.scanType == "QR_CODE"){
const user_prefix = `${store.getters.config.website}/u/`;
console.log(res.result,user_prefix)
if(res.result.startsWith(user_prefix)){
return uni.navigateTo({
url:"/pages/common/userCard/index?sourceID="+res.result.replace(user_prefix,'')
});
}
const group_prefix = `${store.getters.config.website}/g/`;
console.log(res.result,group_prefix)
if(res.result.startsWith(group_prefix)){
return uni.navigateTo({
url:"/pages/common/groupCard/index?sourceID="+res.result.replace(group_prefix,'')
});
}
if(res.result.startsWith("http")){
uni.navigateTo({
url:"/pages/common/webview?url="+encodeURIComponent(res.result)
});
//res.result;
return ;
}
}
},
fail(res){
console.log(res)
}
});
}
const fileExsit = async(fn)=>{
return await new Promise((resolve) => {
plus.io.resolveLocalFileSystemURL(fn, function(entry) {
resolve(true);
}, function() {
resolve(false);
});
})
}
const downloadFile1 = (url, savepath, successCb, errorCb, progressCb) => {
const root_dir = "_doc/";
if (!url) {
errorCb && errorCb.call(this,new Error('empty url'));
return;
}
const startDownload = () => {
console.log(33333333333333,root_dir+savepath);
const task = plus.downloader.createDownload(url, { filename: root_dir+savepath, timeout: 2 }, function(d, status) {
console.log('completedCB');
console.log(d);
console.log(status);
if (status === 200) {
const local = d && d.filename ? d.filename : root_dir+savepath;
successCb && successCb.call(this,local);
} else {
errorCb && errorCb.call(this,new Error('download status ' + status));
}
});
task.addEventListener('statechanged', function(t, status) {
console.log('statechanged',d,status);
if (t.state === 3) {
var downloaded = t.downloadedSize || t.downloaded || 0;
var total = t.totalSize || t.total || 0;
var prog = 0;
if (total > 0) prog = Math.min(100, Math.floor(downloaded / total * 100));
progressCb && progressCb.call(this,prog);
}
});
try{
task.start();
plus.downloader.startAll();
}catch(e){
console.log('e',e);
}
plus.downloader.enumerate((downloads )=>{
for(var i =0;i<downloads.length;i++){
//downloads[i].abort();
console.log(downloads[i]);
}
})
};
// 目录不存在,尝试创建(针对 _doc/<conversationID> 结构)
const fns = savepath.split('/');
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
fs.root.getDirectory(fns[0], { create: true }, function(entry) {
startDownload();
}, function(e) {
// 创建失败也尝试下载,可能运行时会自动创建
startDownload();
});
}, function(e) {
startDownload();
});
}
const get_absolute_path = (fn)=>{
return plus.io.convertLocalFileSystemURL(fn);
}
const pendingDownloads = new Map();
const getCachePath = (url,saveDir)=>{
if(!url || !url.startsWith('http')){
return url;
}
const key = md5(url);
var ext = "png"
if(url.toLowerCase().indexOf('.mp4')!==-1){
ext = "mp4";
}
let cacheDir = plus.io.convertLocalFileSystemURL(`_doc/{{dir}}/{{key}}.{{ext}}`);
if(uni.getSystemInfoSync().osName == 'android'){
cacheDir = cacheDir.replace('apps/'+plus.runtime.appid+'/doc','cache');
}
return cacheDir.replace('{{dir}}',saveDir)
.replace('{{key}}',key)
.replace('{{ext}}',ext);
}
const cacheFile = (url, saveDir,progressCallback) => {
//console.log(url);
// #ifndef APP
return new Promise(async (resolve, reject) => {
resolve(url);
});
// #endif
return new Promise(async (resolve, reject) => {
try {
if(!url || !url.startsWith('http')){
resolve(url);
return;
}
const key = md5(url);
var ext = "png"
if(url.toLowerCase().indexOf('.mp4')!==-1){
ext = "mp4";
}
let cacheDir = plus.io.convertLocalFileSystemURL(`_doc/{{dir}}/{{key}}.{{ext}}`);
if(uni.getSystemInfoSync().osName == 'android'){
cacheDir = cacheDir.replace('apps/'+plus.runtime.appid+'/doc','cache');
}
const cacheFilePath = cacheDir.replace('{{dir}}',saveDir)
.replace('{{key}}',key)
.replace('{{ext}}',ext);
//console.error('cacheDir:',cacheDir);
// 如果缓存存在且文件存在,直接返回缓存路径
if (cacheFilePath) {
//console.error('cacheFilePath:', cacheFilePath);
const coverExists = await fileExsit(cacheFilePath);
//console.log("coverExists" ,coverExists);
if (coverExists) {
//console.log("已缓存为:" , cacheFilePath,url);
resolve(cacheFilePath);
return;
}
}
// 2. 检查是否已经有相同的 URL 正在下载
if (pendingDownloads.has(url)) {
// 如果已经有相同的下载在进行,则添加到等待列表
pendingDownloads.get(url).promises.push({ resolve, reject });
return;
}
// 3. 如果没有正在下载,则创建新的下载任务
pendingDownloads.set(url, {
promises: [{ resolve, reject }],
completed: false,
savedPath: saveDir
});
// 4. 开始下载文件
const task = downloadFile({
url:url,
timeout: 30000,
filePath:cacheFilePath,
success(res) {
// 下载成功,解决所有等待的 Promise
const info = pendingDownloads.get(url);
if (info) {
info.completed = true;
info.promises.forEach(p => p.resolve(res.tempFilePath));
pendingDownloads.delete(url);
}
},
fail(e){
// 下载失败,拒绝所有等待的 Promise
const info = pendingDownloads.get(url);
if (info) {
info.promises.forEach(p => p.reject(error));
pendingDownloads.delete(url);
}
},
complate(){
task = null;
}
});
//console.error('task:', task);
task.onProgressUpdate(({progress,totalBytesWritten,totalBytesExpectedToWrite})=>{
//console.error('pres:', progress,totalBytesWritten,totalBytesExpectedToWrite);
progressCallback && progressCallback.call(this,progress,totalBytesWritten,totalBytesExpectedToWrite)
});
} catch (error) {
console.error('cacheFile 发生错误:', error);
resolve(url); // 返回原始URL作为fallback
}
});
};
const toMapAPP = (latitude,longitude,name) =>{
let url = "";
if (plus.os.name == "Android") {//判断是安卓端
plus.nativeUI.actionSheet({//选择菜单
title: "选择地图应用",
cancel: "取消",
buttons: [{title: "腾讯地图"},{title: "百度地图"}, {title: "高德地图"}]
}, function(e) {
switch (e.index) {
//下面是拼接url,不同系统以及不同地图都有不同的拼接字段
case 1:
//注意referer=xxx的xxx替换成你在腾讯地图开发平台申请的key
url = `qqmap://map/geocoder?coord=${latitude},${longitude}&referer=xxx`;
break;
case 2:
url = `baidumap://map/marker?location=${latitude},${longitude}&title=${name}&coord_type=gcj02&src=andr.baidu.openAPIdemo`;
break;
case 3:
url = `androidamap://viewMap?sourceApplication=appname&poiname=${name}&lat=${latitude}&lon=${longitude}&dev=0`;
break;
default:
break;
}
if (url != "") {
url = encodeURI(url);
//plus.runtime.openURL(url,function(e){})调起手机APP应用
plus.runtime.openURL(url, function(e) {
plus.nativeUI.alert("本机未安装指定的地图应用");
});
}
})
} else {
// iOS上获取本机是否安装了百度高德地图,需要在manifest里配置
// 在manifest.json文件app-plus->distribute->apple->urlschemewhitelist节点下添加
//(如urlschemewhitelist:["iosamap","baidumap"]
plus.nativeUI.actionSheet({
title: "选择地图应用",
cancel: "取消",
buttons: [{title: "腾讯地图"},{title: "百度地图"}, {title: "高德地图"}]
}, function(e) {
switch (e.index) {
case 1:
url = `qqmap://map/geocoder?coord=${latitude},${longitude}&referer=xxx`;
break;
case 2:
url = `baidumap://map/marker?location=${latitude},${longitude}&title=${name}&content=${name}&src=ios.baidu.openAPIdemo&coord_type=gcj02`;
break;
case 3:
url = `iosamap://viewMap?sourceApplication=applicationName&poiname=${name}&lat=${latitude}&lon=${longitude}&dev=0`;
break;
default:
break;
}
if (url != "") {
url = encodeURI(url);
plus.runtime.openURL(url, function(e) {
plus.nativeUI.alert("本机未安装指定的地图应用");
});
}
})
}
}
/**
* AES 加密函数
* @param {String|Object|Array} str 要加密的内容
* @param {String} key 加密密钥
* @returns {String} 加密后的Base64字符串
*/
const aesencode = (str, key = 'muNcJyt0XXV6faCGe41VSIaf0ecZeW2jXmgpL0Ak93Kbwjyr')=> {
// 如果是对象或数组,转为JSON字符串
if (typeof str === 'object') {
str = JSON.stringify(str);
}
// 使用SHA-256哈希处理密钥
const hashedKey = CryptoJS.SHA256(key);
// 生成IV(前16字节)
const iv = CryptoJS.lib.WordArray.create(hashedKey.words.slice(0, 4));
// 加密
const encrypted = CryptoJS.AES.encrypt(str,hashedKey,{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回Base64编码结果
const _str = encrypted.toString();
return _str.replace(/\+/ig,'%2b');
}
/**
* AES 解密函数
* @param {String} str 要解密的Base64字符串
* @param {String} key 解密密钥
* @returns {String} 解密后的原始字符串
*/
const aesdecode = (str, key = 'muNcJyt0XXV6faCGe41VSIaf0ecZeW2jXmgpL0Ak93Kbwjyr') => {
// 使用SHA-256哈希处理密钥
const hashedKey = CryptoJS.SHA256(key);
// 生成IV(前16字节)
const iv = CryptoJS.lib.WordArray.create(hashedKey.words.slice(0, 4));
str = str.replace(/\%2b/ig,'+');
// 解密
const decrypted = CryptoJS.AES.decrypt(str,hashedKey,{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 转为UTF-8字符串
let _str = decrypted.toString(CryptoJS.enc.Utf8);
console.log(_str);
const obj = _str.startsWith('{') || _str.startsWith('[') ? JSON.parse(_str) : _str;
return obj;
}
export default{
aesdecode,
aesencode,
toMapAPP,
cacheFile,
fileExsit,
getCachePath,
get_absolute_path,
// #ifdef APP
downloadFile,
// #endif
isString :isString,
isNumber :isNumber,
isInteger :isInteger,
isBoolean :isBoolean,
isArray :isArray,
isObject :isObject,
isFunction :isFunction,
isNull :isNull,
isUndefined :isUndefined,
isSymbol :isSymbol,
isDate :isDate,
isRegExp :isRegExp,
isError :isError,
isMap :isMap,
isSet :isSet,
isWeakMap :isWeakMap,
isWeakSet :isWeakSet,
cdn(v){
v= v || "";
v = v.replace(/\\/ig,"/").replace('/\/\/ig',"/");
//console.log(v);
if(v && isString(v)){
if(v.startsWith('//')){
return 'http:'+v;
}
if(v.startsWith('blob:') || v.startsWith('file://') || v.startsWith('http')){
return v;
}
v= v.startsWith('/') ? v : '/'+v;
return base.cdnUrl+''+v;
}
return "";
},
"goto":_goto,
showToast,
error,
success,
scan,
copy(v){
let that = this;
uni.setClipboardData({
data:v+'',
success() {
that.success('复制成功');
}
})
},
toDate(time) {
var date = new Date(time * 1000);
var fmt = 'yyyy-MM-dd hh:mm:ss';
var o = {
'M+': date.getMonth() + 1, //月份
'd+': date.getDate(), //日
'h+': date.getHours(), //小时
'm+': date.getMinutes(), //分
's+': date.getSeconds(), //秒
'q+': Math.floor((date.getMonth() + 3) / 3), //季度
S: date.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
for (var k in o) if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
return fmt;
},
formatAmount(v,wei){
if(!v){return 0.00;}
v=v+"";
v = parseFloat(v).toFixed(wei || 2);
return parseFloat(v);
},
imapi(method,data){
return IMSDK.asyncApi(method,IMSDK.uuid(),data);
}
}