This commit is contained in:
feng0207
2026-08-11 22:22:10 +08:00
commit 5e312c1266
6985 changed files with 1061169 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
import HttpRequest from '@/libs/axios'
import config from '@/config'
const baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro
const axios = new HttpRequest(baseUrl)
export default axios
+29
View File
@@ -0,0 +1,29 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
/**
* @description 判断列表1中是否包含了列表2中的某一项
* 因为用户权限 access 为数组,includes 方法无法直接得出结论
* */
function includeArray (list1, list2) {
let status = false
if (list1 === true) {
return true
} else {
if (typeof list2 !== 'object') {
return false
}
list2.forEach(item => {
if (list1.includes(item)) status = true
})
return status
}
}
export { includeArray }
+86
View File
@@ -0,0 +1,86 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
import axios from 'axios'
import store from '@/store'
// import { Spin } from 'iview'
const addErrorLog = errorInfo => {
const { statusText, status, request: { responseURL } } = errorInfo
let info = {
type: 'ajax',
code: status,
mes: statusText,
url: responseURL
}
if (!responseURL.includes('save_error_logger')) store.dispatch('addErrorLog', info)
}
class HttpRequest {
constructor (baseUrl = baseURL) {
this.baseUrl = 'http://admin.crmeb.net/adminapi'
this.queue = {}
}
getInsideConfig () {
const config = {
baseURL: this.baseUrl,
headers: {
//
}
}
return config
}
destroy (url) {
delete this.queue[url]
if (!Object.keys(this.queue).length) {
// Spin.hide()
}
}
interceptors (instance, url) {
// 请求拦截
instance.interceptors.request.use(config => {
// 添加全局的loading...
if (!Object.keys(this.queue).length) {
// Spin.show() // 不建议开启,因为界面不友好
}
this.queue[url] = true
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截
instance.interceptors.response.use(res => {
console.log('red');
console.log(res);
this.destroy(url)
const { data, status } = res
return { data, status }
}, error => {
this.destroy(url)
let errorInfo = error.response
if (!errorInfo) {
const { request: { statusText, status }, config } = JSON.parse(JSON.stringify(error))
errorInfo = {
statusText,
status,
request: { responseURL: config.url }
}
}
addErrorLog(errorInfo)
return Promise.reject(error)
})
}
request (options) {
const instance = axios.create()
options = Object.assign(this.getInsideConfig(), options)
this.interceptors(instance, options.url)
return instance(options)
}
}
export default HttpRequest
File diff suppressed because one or more lines are too long
+66
View File
@@ -0,0 +1,66 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
import {
Confirm as confirm,
Alert as alert,
Toast as toast,
Notify as notify,
Loading as loading
} from "vue-ydui/dist/lib.rem/dialog";
const dialog = {
confirm,
alert,
toast,
notify,
loading
};
const icons = { error: "操作失败", success: "操作成功" };
Object.keys(icons).reduce((dialog, key) => {
dialog[key] = (mes, obj = {}) => {
return new Promise(function(resolve) {
toast({
mes: mes || icons[key],
timeout: 1000,
icon: key,
callback: () => {
resolve();
},
...obj
});
});
};
return dialog;
}, dialog);
dialog.message = (mes = "操作失败", obj = {}) => {
return new Promise(function(resolve) {
toast({
mes,
timeout: 1000,
callback: () => {
resolve();
},
...obj
});
});
};
dialog.validateError = (...args) => {
validatorDefaultCatch(...args);
};
export function validatorDefaultCatch(err, type = "message") {
return dialog[type](err.errors[0].message);
}
export default dialog;
+123
View File
@@ -0,0 +1,123 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
/* eslint-disable */
import XLSX from 'xlsx';
function auto_width(ws, data){
/*set worksheet max width per col*/
const colWidth = data.map(row => row.map(val => {
/*if null/undefined*/
if (val == null) {
return {'wch': 10};
}
/*if chinese*/
else if (val.toString().charCodeAt(0) > 255) {
return {'wch': val.toString().length * 2};
} else {
return {'wch': val.toString().length};
}
}));
/*start in the first row*/
let result = colWidth[0];
for (let i = 1; i < colWidth.length; i++) {
for (let j = 0; j < colWidth[i].length; j++) {
if (result[j]['wch'] < colWidth[i][j]['wch']) {
result[j]['wch'] = colWidth[i][j]['wch'];
}
}
}
ws['!cols'] = result;
}
function json_to_array(key, jsonData){
return jsonData.map(v => key.map(j => { return v[j] }));
}
// fix data,return string
function fixdata(data) {
let o = '';
let l = 0;
const w = 10240;
for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)));
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));
return o
}
// get head from excel file,return array
function get_header_row(sheet) {
const headers = [];
const range = XLSX.utils.decode_range(sheet['!ref']);
let C;
const R = range.s.r; /* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
var cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]; /* find the cell in the first row */
var hdr = 'UNKNOWN ' + C; // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
headers.push(hdr)
}
return headers
}
export const export_table_to_excel= (id, filename) => {
const table = document.getElementById(id);
const wb = XLSX.utils.table_to_book(table);
XLSX.writeFile(wb, filename);
/* the second way */
// const table = document.getElementById(id);
// const wb = XLSX.utils.book_new();
// const ws = XLSX.utils.table_to_sheet(table);
// XLSX.utils.book_append_sheet(wb, ws, filename);
// XLSX.writeFile(wb, filename);
};
export const export_json_to_excel = ({data, key, title, filename, autoWidth}) => {
const wb = XLSX.utils.book_new();
data.unshift(title);
const ws = XLSX.utils.json_to_sheet(data, {header: key, skipHeader: true});
if(autoWidth){
const arr = json_to_array(key, data);
auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
};
export const export_array_to_excel = ({key, data, title, filename, autoWidth}) => {
const wb = XLSX.utils.book_new();
const arr = json_to_array(key, data);
arr.unshift(title);
const ws = XLSX.utils.aoa_to_sheet(arr);
if(autoWidth){
auto_width(ws, arr);
}
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb, filename + '.xlsx');
};
export const read = (data, type) => {
/* if type == 'base64' must fix data first */
// const fixedData = fixdata(data)
// const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const workbook = XLSX.read(data, { type: type });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const header = get_header_row(worksheet);
const results = XLSX.utils.sheet_to_json(worksheet);
return {header, results};
};
export default {
export_table_to_excel,
export_array_to_excel,
export_json_to_excel,
read
}
+48
View File
@@ -0,0 +1,48 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
const events = [];
const $scroll = function(dom, fn) {
events.push({ dom, fn });
fn._index = events.length - 1;
};
$scroll.remove = function(fn) {
fn._index && events.splice(fn._index, 1);
};
//上拉加载;
const Scroll = {
addHandler: function(element, type, handler) {
if (element.addEventListener)
element.addEventListener(type, handler, false);
else if (element.attachEvent) element.attachEvent("on" + type, handler);
else element["on" + type] = handler;
},
listenTouchDirection: function() {
this.addHandler(window, "scroll", function() {
const wh = window.innerHeight,
st = window.scrollY;
events
.filter(e => e.dom.scrollHeight && e.dom.scrollHeight > 0)
.forEach(e => {
var dh = e.dom.scrollHeight;
var s = Math.ceil((st / (dh - wh)) * 100);
if (s > 85) e.fn();
});
});
}
};
Scroll.listenTouchDirection();
export default $scroll;
export { Scroll };
+20
View File
@@ -0,0 +1,20 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
export default {
name: 'RenderDom',
functional: true,
props: {
render: Function
},
render: (h, ctx) => {
return ctx.props.render(h)
}
}
+88
View File
@@ -0,0 +1,88 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
import axios from 'axios'
import { Message } from 'iview'
import { getCookies, removeCookies, getSen, getLoc } from '@/libs/util'
import Setting from '@/setting'
import router from '@/router';
const service = axios.create({
baseURL: Setting.apiBaseURL,
timeout: 10000 // 请求超时时间
})
axios.defaults.withCredentials = true// 携带cookie
// 请求拦截器
service.interceptors.request.use(
config => {
let baseUrl;
if(config.kefu) {
baseUrl = Setting.apiBaseURL.replace(/admin/, "kefu")
config.baseURL = baseUrl
} else if(config.mobile) {
baseUrl = Setting.apiBaseURL.replace(/admin/, "mobile");
config.baseURL = baseUrl
} else {
config.baseURL = Setting.apiBaseURL
}
const token = getCookies('token')
const kefuToken = getCookies('kefu_token');
const mobileToken = getLoc('mobile_token');
if(token || kefuToken || mobileToken) {
config.headers['Authori-zation'] = config.mobile ? 'Bearer ' + mobileToken : config.kefu ? 'Bearer ' + kefuToken : 'Bearer ' + token;
}
return config
},
error => {
// do something with request error
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
response => {
let status = response.data ? response.data.status : 0
const code = status
switch(code) {
case 200:
return response.data
case 400: case 400011: case 400012:
return Promise.reject(response.data || { msg: '未知错误' })
case 410000:
case 410001:
case 410002:
console.log(code);
localStorage.clear()
removeCookies('token')
removeCookies('expires_time')
removeCookies('uuid')
router.replace({ path: '/admin/login' })
break
case 410003:
removeCookies('kefuInfo')
removeCookies('kefu_token')
removeCookies('kefu_expires_time')
removeCookies('kefu_uuid')
router.replace({ path: '/kefu' })
default:
break
}
},
error => {
Message.error(error.msg)
return Promise.reject(error)
}
)
export default service
+269
View File
@@ -0,0 +1,269 @@
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
import {wss} from '@/libs/util';
import {netWorkPing} from '@/api/kefu';
import Setting from '@/setting';
import Cookies from "js-cookie";
import Vue from 'vue';
let reconneTimer = {};
let reconneCount = {};
let connectGuid = {};
let NetWork = null;
class wsSocket {
constructor(opt) {
this.vm = new Vue;
this.ws = null;
this.opt = opt || {};
this.networkStatus = true;
this.reconneMax = 100;
this.connectLing = false;
reconneTimer[this.opt.key] = null;
reconneCount[this.opt.key] = 0;
this.init(opt);
this.networkWath();
this.defaultEvenv();
}
defaultEvenv() {
this.vm.$on('timeout', this.timeoutEvent.bind(this));
}
timeoutEvent() {
this.reconne();
}
guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
addHandler(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
}
networkStatusFn(onlineFun, offlineFun) {
this.addHandler(window, 'online', () => {
onlineFun()
})
this.addHandler(window, 'offline', () => {
offlineFun()
});
}
networkStatusFnv2(onlineFun, offlineFun) {
if (NetWork) {
clearInterval(NetWork);
NetWork = null;
}
let online = null,
offline = null;
NetWork = setInterval(() => {
netWorkPing().then(res => {
if (online === null) {
onlineFun();
online = true;
}
offline = null;
}).catch(() => {
if (offline === null) {
offlineFun();
offline = true;
}
online = null;
})
}, 1000)
}
networkWath() {
this.networkStatusFn(() => {
this.networkStatus = true;
console.log('联网了')
this.vm.$on('timeout', this.timeoutEvent);
}, () => {
this.networkStatus = false;
this.socketStatus = false;
this.timer && clearInterval(this.timer);
this.timer = null;
this.ws.close();
console.log('断网了')
});
}
reconne() {
if (reconneCount[this.opt.key] > this.reconneMax) {
//重连次数超过限制不再重连
if (reconneTimer[this.opt.key]) {
clearInterval(reconneTimer[this.opt.key]);
}
return;
}
if (reconneTimer[this.opt.key] || this.socketStatus) {
return;
}
reconneTimer[this.opt.key] = setInterval(() => {
//断线连接中发现状态为真就不用再连接
if (this.socketStatus) {
return;
}
//正在连接中也不需要在连接了
if (!this.connectLing) {
console.log('重新连接')
this.init(this.opt);
reconneCount[this.opt.key]++;
}
}, 2000)
}
onOpen(key = false) {
//关闭断线重连定时器
clearInterval(reconneTimer[this.opt.key]);
reconneTimer[this.opt.key] = null;
this.connectLing = false;
this.opt.open && this.opt.open();
reconneCount[this.opt.key] = 0
this.socketStatus = true;
this.ping();
}
init(opt) {
if (this.socketStatus) {
return;
}
let wsUrl = ''
let hostUrl = wss(Setting.wsSocketUrl);
hostUrl = hostUrl + '/ws';
if (opt.key == 1) {
wsUrl = hostUrl + '?type=admin' + '&token=' + util.cookies.get("token")
}
if (opt.key == 2) {
wsUrl = hostUrl + `?type=kefu` + '&token=' + `${Cookies.get("kefu_token")}`;
}
if (opt.key == 3) {
wsUrl = `${hostUrl}?type=user&form=${opt.form}&token=${opt.token}`;
}
if (opt.tourist_uid) {
wsUrl += '&tourist_uid=' + opt.tourist_uid
}
if (wsUrl) {
this.connectLing = true;
// connectGuid[opt.key] = this.guid();
this.ws = new WebSocket(wsUrl);
this.ws.onopen = this.onOpen.bind(this);
this.ws.onerror = this.onError.bind(this);
this.ws.onmessage = this.onMessage.bind(this);
this.ws.onclose = this.onClose.bind(this);
}
}
ping() {
var that = this;
this.timer = setInterval(() => {
that.send({type: 'ping'});
}, 10000);
}
send(data) {
if (!this.socketStatus || this.ws.readyState === 0 || !this.networkStatus) {
this.reconne();
}
return new Promise((resolve, reject) => {
try {
this.ws.send(JSON.stringify(data));
resolve({status: true});
} catch (e) {
console.log(e)
reject({status: false, socketStatus: this.socketStatus, networkStatus: this.networkStatus})
}
});
}
onMessage(res) {
this.opt.message && this.opt.message(res);
}
onClose() {
this.connectLing = false;
this.timer && clearInterval(this.timer);
this.timer = null;
this.opt.close && this.opt.close();
this.socketStatus = false;
this.reconne();
}
onError(e) {
this.connectLing = false;
this.timer && clearInterval(this.timer);
this.timer = null;
this.opt.error && this.opt.error(e);
this.socketStatus = false;
this.reconne();
}
$on(...args) {
this.vm.$on(...args);
}
$off(...args) {
this.vm.$off(...args);
}
}
let promises = {};
function createSocket(key, flag, token, tourist_uid, type, form) {
if (flag) promises[key] = null;
if (!promises[key])
promises[key] = new Promise((resolve, reject) => {
const ws = new wsSocket({
key,
token,
tourist_uid,
type,
form,
open() {
resolve(ws);
},
error(e) {
reject(e)
},
message(res) {
const {type, data = {}} = JSON.parse(res.data);
ws.vm.$emit(type, data);
},
close(e) {
ws.vm.$emit('close', e);
}
})
});
return promises[key];
}
export const adminSocket = (flag, token) => createSocket(1, flag, token);
export const Socket = (flag, token, tourist_uid, type) => createSocket(2, flag, token, tourist_uid, type);
export const mobileScoket = (flag, token, form, tourist_uid, type,) => createSocket(3, flag, token, tourist_uid, type, form);
+69
View File
@@ -0,0 +1,69 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
export default {
shortcuts: [
{
text: '今天',
value () {
const end = new Date()
const start = new Date()
start.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()))
return [start, end]
}
},
{
text: '昨天',
value () {
const end = new Date()
const start = new Date()
start.setTime(start.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() - 1)))
end.setTime(end.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() -1 )))
return [start, end]
}
},
{
text: '最近7天',
value () {
const end = new Date()
const start = new Date()
start.setTime(start.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() - 6)))
return [start, end]
}
},
{
text: '最近30天',
value () {
const end = new Date()
const start = new Date()
start.setTime(start.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() - 29)))
return [start, end]
}
},
{
text: '本月',
value () {
const end = new Date()
const start = new Date()
start.setTime(start.setTime(new Date(new Date().getFullYear(), new Date().getMonth(), 1)))
return [start, end]
}
},
{
text: '本年',
value () {
const end = new Date()
const start = new Date()
start.setTime(start.setTime(new Date(new Date().getFullYear(), 0, 1)))
return [start, end]
}
}
]
}
+236
View File
@@ -0,0 +1,236 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
export const forEach = (arr, fn) => {
if (!arr.length || !fn) return
let i = -1
let len = arr.length
while (++i < len) {
let item = arr[i]
fn(item, i, arr)
}
}
/**
* @param {Array} arr1
* @param {Array} arr2
* @description 得到两个数组的交集, 两个数组的元素为数值或字符串
*/
export const getIntersection = (arr1, arr2) => {
let len = Math.min(arr1.length, arr2.length)
let i = -1
let res = []
while (++i < len) {
const item = arr2[i]
if (arr1.indexOf(item) > -1) res.push(item)
}
return res
}
/**
* @param {Array} arr1
* @param {Array} arr2
* @description 得到两个数组的并集, 两个数组的元素为数值或字符串
*/
export const getUnion = (arr1, arr2) => {
return Array.from(new Set([...arr1, ...arr2]))
}
/**
* @param {Array} target 目标数组
* @param {Array} arr 需要查询的数组
* @description 判断要查询的数组是否至少有一个元素包含在目标数组中
*/
export const hasOneOf = (targetarr, arr) => {
return targetarr.some(_ => arr.indexOf(_) > -1)
}
/**
* @param {String|Number} value 要验证的字符串或数值
* @param {*} validList 用来验证的列表
*/
export function oneOf (value, validList) {
for (let i = 0; i < validList.length; i++) {
if (value === validList[i]) {
return true
}
}
return false
}
/**
* @param {Number} timeStamp 判断时间戳格式是否是毫秒
* @returns {Boolean}
*/
const isMillisecond = timeStamp => {
const timeStr = String(timeStamp)
return timeStr.length > 10
}
/**
* @param {Number} timeStamp 传入的时间戳
* @param {Number} currentTime 当前时间时间戳
* @returns {Boolean} 传入的时间戳是否早于当前时间戳
*/
const isEarly = (timeStamp, currentTime) => {
return timeStamp < currentTime
}
/**
* @param {Number} num 数值
* @returns {String} 处理后的字符串
* @description 如果传入的数值小于10,即位数只有1位,则在前面补充0
*/
const getHandledValue = num => {
return num < 10 ? '0' + num : num
}
/**
* @param {Number} timeStamp 传入的时间戳
* @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间
*/
const getDate = (timeStamp, startType) => {
const d = new Date(timeStamp * 1000)
const year = d.getFullYear()
const month = getHandledValue(d.getMonth() + 1)
const date = getHandledValue(d.getDate())
const hours = getHandledValue(d.getHours())
const minutes = getHandledValue(d.getMinutes())
const second = getHandledValue(d.getSeconds())
let resStr = ''
if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second
else resStr = month + '-' + date + ' ' + hours + ':' + minutes
return resStr
}
/**
* @param {String|Number} timeStamp 时间戳
* @returns {String} 相对时间字符串
*/
export const getRelativeTime = timeStamp => {
// 判断当前传入的时间戳是秒格式还是毫秒
const IS_MILLISECOND = isMillisecond(timeStamp)
// 如果是毫秒格式则转为秒格式
if (IS_MILLISECOND) Math.floor(timeStamp /= 1000)
// 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型
timeStamp = Number(timeStamp)
// 获取当前时间时间戳
const currentTime = Math.floor(Date.parse(new Date()) / 1000)
// 判断传入时间戳是否早于当前时间戳
const IS_EARLY = isEarly(timeStamp, currentTime)
// 获取两个时间戳差值
let diff = currentTime - timeStamp
// 如果IS_EARLY为false则差值取反
if (!IS_EARLY) diff = -diff
let resStr = ''
const dirStr = IS_EARLY ? '前' : '后'
// 少于等于59秒
if (diff <= 59) resStr = diff + '秒' + dirStr
// 多于59秒,少于等于59分钟59秒
else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr
// 多于59分钟59秒,少于等于23小时59分钟59秒
else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr
// 多于23小时59分钟59秒,少于等于29天59分钟59秒
else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr
// 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp)
else resStr = getDate(timeStamp, 'year')
return resStr
}
/**
* @returns {String} 当前浏览器名称
*/
export const getExplorer = () => {
const ua = window.navigator.userAgent
const isExplorer = (exp) => {
return ua.indexOf(exp) > -1
}
if (isExplorer('MSIE')) return 'IE'
else if (isExplorer('Firefox')) return 'Firefox'
else if (isExplorer('Chrome')) return 'Chrome'
else if (isExplorer('Opera')) return 'Opera'
else if (isExplorer('Safari')) return 'Safari'
}
/**
* @description 绑定事件 on(element, event, handler)
*/
export const on = (function () {
if (document.addEventListener) {
return function (element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler)
}
}
}
})()
/**
* @description 解绑事件 off(element, event, handler)
*/
export const off = (function () {
if (document.removeEventListener) {
return function (element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event) {
element.detachEvent('on' + event, handler)
}
}
}
})()
/**
* 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性
* 如果没有传入key这个参数,则判断obj对象是否有键值对
*/
export const hasKey = (obj, key) => {
if (key) return key in obj
else {
let keysArr = Object.keys(obj)
return keysArr.length
}
}
/**
* @param {*} obj1 对象
* @param {*} obj2 对象
* @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串
*/
export const objEqual = (obj1, obj2) => {
const keysArr1 = Object.keys(obj1)
const keysArr2 = Object.keys(obj2)
if (keysArr1.length !== keysArr2.length) return false
else if (keysArr1.length === 0 && keysArr2.length === 0) return true
/* eslint-disable-next-line */
else return !keysArr1.some(key => obj1[key] != obj2[key])
}
/**
* 去除乘法计算出现多位小数
* @param arg1返回值,arg2乘以的参数
*/
export const accMul = (arg1, arg2) => {
var m=0,s1=arg1.toString(),s2=arg2.toString();
try{m+=s1.split(".")[1].length}catch(e){}
try{m+=s2.split(".")[1].length}catch(e){}
return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)
}
+527
View File
@@ -0,0 +1,527 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
import Cookies from 'js-cookie'
// cookie保存的天数
import config from '@/config'
import {forEach, hasOneOf, objEqual} from '@/libs/tools'
import {cloneDeep} from 'lodash'
const {title, useI18n} = config
// 设置setCookies
// setToken
export const setCookies = (key, val, cookieExpires) => {
Cookies.set(key, val, {expires: cookieExpires || 1})
}
// 获取getCookies
// getToken
export const getCookies = (key) => {
return Cookies.get(key)
}
export const removeCookies = (key) => {
return Cookies.remove(key)
}
export const hasChild = (item) => {
return item.children && item.children.length !== 0
}
const showThisMenuEle = (item, access) => {
if (item.meta && item.meta.access && item.meta.access.length) {
if (hasOneOf(item.meta.access, access)) return true
else return false
} else return true
}
/**
* @param {Array} list 通过路由列表得到菜单列表
* @returns {Array}
*/
export const getMenuByRouter = (list, access) => {
let res = []
forEach(list, item => {
if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
meta: item.meta
}
if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
obj.children = getMenuByRouter(item.children, access)
}
if (item.meta && item.meta.href) obj.href = item.meta.href
if (showThisMenuEle(item, access)) res.push(obj)
}
})
return res
}
/**
* @param {Array} routeMetched 当前路由metched
* @returns {Array}
*/
export const getBreadCrumbList = (route, homeRoute) => {
let homeItem = {...homeRoute, icon: homeRoute.meta.icon}
let routeMetched = route.matched
if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem]
let res = routeMetched.filter(item => {
return item.meta === undefined || !item.meta.hideInBread
}).map(item => {
let meta = {...item.meta}
if (meta.title && typeof meta.title === 'function') {
meta.__titleIsFunction__ = true
meta.title = meta.title(route)
}
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
meta: meta
}
return obj
})
res = res.filter(item => {
return !item.meta.hideInMenu
})
return [{...homeItem, to: homeRoute.path}, ...res]
}
export const getRouteTitleHandled = (route) => {
let router = {...route}
let meta = {...route.meta}
let title = ''
if (meta.title) {
if (typeof meta.title === 'function') {
meta.__titleIsFunction__ = true
title = meta.title(router)
} else title = meta.title
}
meta.title = title
router.meta = meta
return router
}
export const showTitle = (item, vm) => {
let {title, __titleIsFunction__} = item.meta
if (!title) return
if (useI18n) {
if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim())))
else if (__titleIsFunction__) title = item.meta.title
else title = vm.$t(item.name)
} else title = (item.meta && item.meta.title) || item.name
return title
}
/**
* @description 本地存储和获取标签导航列表
*/
export const setTagNavListInLocalstorage = list => {
localStorage.tagNaveList = JSON.stringify(list)
}
/**
* @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项
*/
export const getTagNavListFromLocalstorage = () => {
const list = localStorage.tagNaveList
return list ? JSON.parse(list) : []
}
/**
* @param {Array} routers 路由列表数组
* @description 用于找到路由列表中name为home的对象
*/
export const getHomeRoute = (routers, homeName = 'home') => {
let i = -1
let len = routers.length
let homeRoute = {}
while (++i < len) {
let item = routers[i]
if (item.children && item.children.length) {
let res = getHomeRoute(item.children, homeName)
if (res.name) return res
} else {
if (item.name === homeName) homeRoute = item
}
}
return homeRoute
}
/**
* @param {*} list 现有标签导航列表
* @param {*} newRoute 新添加的路由原信息对象
* @description 如果该newRoute已经存在则不再添加
*/
export const getNewTagList = (list, newRoute) => {
const {name, path, meta} = newRoute
let newList = [...list]
if (newList.findIndex(item => item.name === name) >= 0) return newList
else newList.push({name, path, meta})
return newList
}
/**
* @param {*} access 用户权限数组,如 ['super_admin', 'admin']
* @param {*} route 路由列表
*/
const hasAccess = (access, route) => {
if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access)
else return true
}
/**
* 权鉴
* @param {*} name 即将跳转的路由name
* @param {*} access 用户权限数组
* @param {*} routes 路由列表
* @description 用户是否可跳转到该页
*/
export const canTurnTo = (name, access, routes) => {
const routePermissionJudge = (list) => {
return list.some(item => {
if (item.children && item.children.length) {
return routePermissionJudge(item.children)
} else if (item.name === name) {
return hasAccess(access, item)
}
})
}
return routePermissionJudge(routes)
}
/**
* @param {String} url
* @description 从URL中解析参数
*/
export const getParams = url => {
const keyValueArr = url.split('?')[1].split('&')
let paramObj = {}
keyValueArr.forEach(item => {
const keyValue = item.split('=')
paramObj[keyValue[0]] = keyValue[1]
})
return paramObj
}
/**
* @param {Array} list 标签列表
* @param {String} name 当前关闭的标签的name
*/
export const getNextRoute = (list, route) => {
let res = {}
if (list.length === 2) {
res = getHomeRoute(list)
} else {
const index = list.findIndex(item => routeEqual(item, route))
if (index === list.length - 1) res = list[list.length - 2]
else res = list[index + 1]
}
return res
}
/**
* @param {Number} times 回调函数需要执行的次数
* @param {Function} callback 回调函数
*/
export const doCustomTimes = (times, callback) => {
let i = -1
while (++i < times) {
callback(i)
}
}
/**
* @param {Object} file 从上传组件得到的文件对象
* @returns {Promise} resolve参数是解析后的二维数组
* @description 从Csv文件中解析出表格,解析成二维数组
*/
export const getArrayFromFile = (file) => {
let nameSplit = file.name.split('.')
let format = nameSplit[nameSplit.length - 1]
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.readAsText(file) // 以文本格式读取
let arr = []
reader.onload = function (evt) {
let data = evt.target.result // 读到的数据
let pasteData = data.trim()
arr = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => {
return row.split('\t')
}).map(item => {
return item[0].split(',')
})
if (format === 'csv') resolve(arr)
else reject(new Error('[Format Error]:你上传的不是Csv文件'))
}
})
}
/**
* @param {Array} array 表格数据二维数组
* @returns {Object} { columns, tableData }
* @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在iView的表格中展示数据
*/
export const getTableDataFromArray = (array) => {
let columns = []
let tableData = []
if (array.length > 1) {
let titles = array.shift()
columns = titles.map(item => {
return {
title: item,
key: item
}
})
tableData = array.map(item => {
let res = {}
item.forEach((col, i) => {
res[titles[i]] = col
})
return res
})
}
return {
columns,
tableData
}
}
export const findNodeUpper = (ele, tag) => {
if (ele.parentNode) {
if (ele.parentNode.tagName === tag.toUpperCase()) {
return ele.parentNode
} else {
return findNodeUpper(ele.parentNode, tag)
}
}
}
export const findNodeUpperByClasses = (ele, classes) => {
let parentNode = ele.parentNode
if (parentNode) {
let classList = parentNode.classList
if (classList && classes.every(className => classList.contains(className))) {
return parentNode
} else {
return findNodeUpperByClasses(parentNode, classes)
}
}
}
export const findNodeDownward = (ele, tag) => {
const tagName = tag.toUpperCase()
if (ele.childNodes.length) {
let i = -1
let len = ele.childNodes.length
while (++i < len) {
let child = ele.childNodes[i]
if (child.tagName === tagName) return child
else return findNodeDownward(child, tag)
}
}
}
export const showByAccess = (access, canViewAccess) => {
return hasOneOf(canViewAccess, access)
}
/**
* @description 根据name/params/query判断两个路由对象是否相等
* @param {*} route1 路由对象
* @param {*} route2 路由对象
*/
export const routeEqual = (route1, route2) => {
const params1 = route1.params || {}
const params2 = route2.params || {}
const query1 = route1.query || {}
const query2 = route2.query || {}
return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2)
}
/**
* 判断打开的标签列表里是否已存在这个新添加的路由对象
*/
export const routeHasExist = (tagNavList, routeItem) => {
let len = tagNavList.length
let res = false
doCustomTimes(len, (index) => {
if (routeEqual(tagNavList[index], routeItem)) res = true
})
return res
}
export const localSave = (key, value) => {
localStorage.setItem(key, value)
}
export const localRead = (key) => {
return localStorage.getItem(key) || ''
}
// scrollTop animation
export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
return window.setTimeout(callback, 1000 / 60)
}
)
}
const difference = Math.abs(from - to)
const step = Math.ceil(difference / duration * 50)
const scroll = (start, end, step) => {
if (start === end) {
endCallback && endCallback()
return
}
let d = (start + step > end) ? end : start + step
if (start > end) {
d = (start - step < end) ? end : start - step
}
if (el === window) {
window.scrollTo(d, d)
} else {
el.scrollTop = d
}
window.requestAnimationFrame(() => scroll(d, end, step))
}
scroll(from, to, step)
}
/**
* @description 根据当前跳转的路由设置显示在浏览器标签的title
* @param {Object} routeItem 路由对象
* @param {Object} vm Vue实例
*/
export const setTitle = (routeItem, vm) => {
const handledRoute = getRouteTitleHandled(routeItem)
const pageTitle = showTitle(handledRoute, vm)
let pageTitleCooke = getCookies('pageTitle')
let title1 = pageTitleCooke === undefined ? title : pageTitleCooke;
const resTitle = pageTitle ? `${title1} - ${pageTitle}` : title1
window.document.title = resTitle
}
export const R = (menuList, newOpenMenus) => {
menuList.forEach(item => {
let newMenu = {}
for (let i in item) {
if (i !== 'children') newMenu[i] = cloneDeep(item[i])
}
newOpenMenus.push(newMenu)
item.children && R(item.children, newOpenMenus)
})
return newOpenMenus
}
export function getMenuopen(to, menuList) {
const allMenus = []
menuList.forEach(menu => {
const menus = transMenu(menu, [])
allMenus.push({
path: menu.path,
openNames: []
})
menus.forEach(item => allMenus.push(item))
})
const currentMenu = allMenus.find(item => item.path === to.path)
return currentMenu ? currentMenu.openNames : []
}
function transMenu(menu, openNames) {
if (menu.children && menu.children.length) {
const itemOpenNames = openNames.concat([menu.path])
return menu.children.reduce((all, item) => {
all.push({
path: item.path,
openNames: itemOpenNames
})
const foundChildren = transMenu(item, itemOpenNames)
return all.concat(foundChildren)
}, [])
} else {
return [menu].map(item => {
return {
path: item.path,
openNames: openNames
}
})
}
}
export function wss(wsSocketUrl) {
let ishttps = document.location.protocol == 'https:';
if (ishttps) {
return wsSocketUrl.replace('ws:', 'wss:');
} else {
return wsSocketUrl.replace('wss:', 'ws:');
}
}
//set session
export function setSen(k, val) {
if (typeof val == 'string') {
sessionStorage.setItem(k, val);
return val;
}
sessionStorage.setItem(k, JSON.stringify(val));
return val;
}
//get session
export function getSen(k) {
let uu = sessionStorage.getItem(k);
try {
if (typeof JSON.parse(uu) != 'number') {
uu = JSON.parse(uu);
}
} catch (e) {
}
return uu;
}
//set local
export function setLoc(k, val) {
if (typeof val == 'string') {
localStorage.setItem(k, val);
return val;
}
localStorage.setItem(k, JSON.stringify(val));
return val;
}
//get local
export function getLoc(k) {
let uu = localStorage.getItem(k);
try {
if (typeof JSON.parse(uu) != 'number') {
uu = JSON.parse(uu);
}
} catch (e) {
}
return uu;
}
export function getGuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
+27
View File
@@ -0,0 +1,27 @@
// +---------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +---------------------------------------------------------------------
// | Copyright (c) 2016~2021 https://www.crmeb.com All rights reserved.
// +---------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +---------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +---------------------------------------------------------------------
class AuthWechat {
/**
* 是否是微信
*/
isWeixin() {
return navigator.userAgent.toLowerCase().indexOf("micromessenger") !== -1;
}
/**
* 是否是手机端
*/
_isMobile() {
let flag = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)
return flag;
}
}
export default new AuthWechat();