/*
* @Description: 公共方法
* @Author: qrn
* @Date: 2022-06-22 14:47:53
* @LastEditors: qrn
* @LastEditTime: 2022-07-14 14:30:02
*/
import Vue from "vue";
import "./rulesUtils";
let areaData = JSON.parse(localStorage.getItem('regionList'))
/**
* @description 白色按钮的失去焦点事件
* @param {*} evt
* @example this.$btnBlur(evt)
*/
Vue.prototype.$btnBlur = function BlurFn(evt) {
var target = evt.target;
if (target.nodeName == "SPAN") {
target = evt.target.parentNode;
}
target.blur();
};
/**
* @description: 复制字符串
* @param {要复制的字符串} url
*/
Vue.prototype.$CopyStr = function copyUrl(url) {
const cInput = document.createElement("input");
cInput.value = url;
document.body.appendChild(cInput);
cInput.select();
document.execCommand("Copy");
this.$message.success("复制成功");
cInput.remove();
};
/**
* @description: 根据省市区的code 筛选组合出省市区Object返回
* @param {传入要筛选的省市区数组,type:Array} arr:[11,1101,110102]
* @return {Object||undefined}
* @example let obj = this.$HandlerArea([province_code, city_code, area_code])
*/
Vue.prototype.$HandlerArea = (arr) => {
let [provinceCode, cityCode, areaCode] = arr;
let areaObj = {};
areaObj.province = Vue.prototype.$filterArr(areaData, provinceCode, 'value')
areaObj.city = Vue.prototype.$filterArr(areaObj.province.children, cityCode, 'value');
areaObj.area = Vue.prototype.$filterArr(areaObj.city.children, areaCode, 'value');
return areaObj;
}
/**
* @description: 在一个数组中筛选出符合条件的数据
* @param {传入要过滤的数组,type:Array} arr
* @param {要对比的参数名称,type:number||string||Boolean} typeData
* @param {数组对象中要对比的参数名称,type:number||string||Boolean} target
* @return {Object||undefined}
*/
Vue.prototype.$filterArr = (arr, typeData, target) => {
if (typeof arr != "object") return;
return arr.filter((r) => {
return r[target] == typeData;
})[0];
};
/**
* @description 限制input输入框只能输入两位小数
* @param {*} value
* @returns
* @example @input="value = $clearNoNum(value)"
*/
Vue.prototype.$clearNoNum = (value) => {
value = value.replace(/[^\d.]/g, ""); //清除"数字"和"."以外的字符
value = value.replace(/^\./g, ""); //验证第一个字符是数字而不是字符
value = value.replace(/\.{2,}/g, "."); //只保留第一个.清除多余的
value = value.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
value = value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3'); //只能输入两个小数
return value
}
Vue.prototype.$inputDecimal = (value, decimalPlaces) => {
const regex = new RegExp(`^(-)*(\\d+)\\.(\\d{1,${decimalPlaces}}).*`);
value = value.replace(/[^\d.]/g, ""); //清除"数字"和"."以外的字符
value = value.replace(/^\./g, ""); //验证第一个字符是数字而不是字符
value = value.replace(/\.{2,}/g, "."); //只保留第一个.清除多余的
value = value.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
value = value.replace(regex, '$1$2.$3'); //只能输入指定位数的小数
return value;
}
/**
* @description 过滤false, 空字符串不能过滤,因为一些数组会作以逗号分隔的字符串
* @param {*} obj
* @returns
* @example
*/
Vue.prototype.$filterFalse = (obj) => {
let arr = [undefined, false, 0 ,'0', '0.00', null] // NaN
for(let key in obj) {
obj[key] = arr.includes(obj[key]) ? '' : obj[key]
}
return obj
}
/**
* @description 枚举对象转数组对象
* @param {*} obj
* @return {Array}
* @example
*/
Vue.prototype.$objToArr = function(obj) {
let arr = []
for (let i in obj) {
let item = { key: i , value: obj[i]}
arr.push(item)
}
return arr
}
/**
* @description 字符串转数组字符串
* @param {*} str
* @return {Array}
* @example
*/
Vue.prototype.$strToArr = (str) => {
return str ? str.split(",") : []
}
/**
* @description 数组字符串转字符串
* @param {*} arr
* @return {str}
* @example
*/
Vue.prototype.$arrToStr = (arr) => {
return arr.length ? arr.join(',') : ''
}
表单提交自定义指令防抖函数
/*
* @Description: 自定义指令
* @Author: qrn
* @Date: 2022-07-11 16:51:11
* @LastEditors: qrn
* @FilePath: \oddv5-admin\src\utils\directive.js
*/
import Vue from 'vue'
/**
* @description 表单提交防抖
* @param {*}
* @returns
* @example 防抖之前的正常写法@click='submit' -->改为防抖之后的 v-debounce = 'submit'即可
*/
Vue.directive('debounce', {
inserted(el, binding) {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
binding.value();
}, 500)
})
}
})
// 优化后的自定义指令,上面的写法容易造成内存泄漏以及当一个页面使用多个指令时的事件问题
Vue.directive('debounce', {
inserted: function (el, binding) {
let timer = null;
const handleClick = () => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
// 在这里写入需要执行的代码
binding.value();
}, 500);
};
el._debounceClick = handleClick; // 将函数保存在元素的自定义属性中
el.addEventListener('click', handleClick);
},
unbind: function (el) {
const handleClick = el._debounceClick;
if (handleClick) {
el.removeEventListener('click', handleClick);
delete el._debounceClick; // 清除保存的函数
}
}
});
/**
* @description 动态设置标题
* @param {*}
* @returns
* @example
*/
Vue.directive('title', {
inserted: function (el, binding) {
document.title = el.dataset.title
}
})
el-select分页数据的下拉加载
Vue.directive('loadmore', {
bind(el, binding) {
// 获取element-ui定义好的scroll盒子
const SELECTWRAP_DOM = el.querySelector(
'.el-select-dropdown .el-select-dropdown__wrap'
)
SELECTWRAP_DOM.addEventListener('scroll', function() {
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
if (CONDITION) {
binding.value()
}
})
}
})
// 使用
v-loadmore="loadMore"
loadMore() {
// 这里写入要触发的方法
console.log('指派用户加载更多')
this.memberParams.page++
this.getMemberData()
},
filterParams(params) {
// props为只想要params里面对象的参数
const props = [
"name",
"remark",
"type",
"merchant_ids",
"member_id",
"use_time_type",
"get_start_time",
"get_end_time",
"get_range_time",
"use_period_start",
"use_period_end",
"use_period_range_time",
"use_conditon_amount",
"use_discount_amount",
"one_day_get_count",
"one_account_get_count",
"total_count",
"is_open_screen",
"open_screen_start",
"open_screen_end",
"open_screen_range",
"is_coupon_center",
"coupon_center_start",
"coupon_center_end",
"coupon_center_range"
];
const dataObj = {};
props.forEach(v => {
dataObj[v] = params[v];
});
return dataObj;
},
const obj = this.filterParams(form);
/**
* @description 时间差
* @param startDate 开始日期 yyyy-MM-dd
* @param enDate 结束日期 yyyy-MM-dd
* @returns {number} 两日期相差的天数
* @example const d1 = '1900-01-01'; const d2 = '2021-03-22';const days = getDaysBetween(Date.now(), v.activity_end_time * 1000);console.log(days)
*/
export function getDaysBetween(startDate, enDate) {
if (startDate > enDate) {
return 0
}
// 这个判断可以根据需求来确定是否需要加上
if (startDate === enDate) {
return 1
}
const days = (enDate - startDate) / (1 * 24 * 60 * 60 * 1000)
return days
}
/**
* @description: 区分一维二维数组
* @param {*} arr
* @returns 几维
*/
export function multiarr(arr) {
let a = 1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
a++;
arr = arr[i];
this.multiarr(arr);
}
}
return a;
}
// 日期时间选择器
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']"
var _formatDate = function (date, fmt) {
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;
};
/**
* 时间格式化
* @param {Object} time
*/
export function changeShowTime(time, flag = true) {
if (!time) {
return;
}
var thisTime = time;
// thisTime = thisTime.replace(/-/g, "/");
var time2 = new Date(thisTime);
var timestamp = time2.getTime();
var mustIncludeTime = flag;
// 当前时间
var currentDate = new Date();
// 目标判断时间
var srcDate = new Date(parseInt(timestamp));
var currentYear = currentDate.getFullYear();
var currentMonth = currentDate.getMonth() + 1;
var currentDateD = currentDate.getDate();
var srcYear = srcDate.getFullYear();
var srcMonth = srcDate.getMonth() + 1;
var srcDateD = srcDate.getDate();
var ret = "";
// 要额外显示的时间分钟
var timeExtraStr = mustIncludeTime ? " " + _formatDate(srcDate, "hh:mm") : "";
// 当年
if (currentYear == srcYear) {
var currentTimestamp = currentDate.getTime();
var srcTimestamp = timestamp;
// 相差时间(单位:毫秒)
var deltaTime = currentTimestamp - srcTimestamp;
// 当天(月份和日期一致才是)
if (currentMonth == srcMonth && currentDateD == srcDateD) {
// 时间相差60秒以内
if (deltaTime < 60 * 1000) ret = "刚刚";
// 否则当天其它时间段的,直接显示“时:分”的形式
else ret = _formatDate(srcDate, "hh:mm");
}
// 当年 && 当天之外的时间(即昨天及以前的时间)
else {
// 昨天(以“现在”的时候为基准-1天)
var yesterdayDate = new Date();
yesterdayDate.setDate(yesterdayDate.getDate() - 1);
// 前天(以“现在”的时候为基准-2天)
var beforeYesterdayDate = new Date();
beforeYesterdayDate.setDate(beforeYesterdayDate.getDate() - 2);
// 用目标日期的“月”和“天”跟上方计算出来的“昨天”进行比较,是最为准确的(如果用时间戳差值
// 的形式,是不准确的,比如:现在时刻是2019年02月22日1:00、而srcDate是2019年02月21日23:00,
// 这两者间只相差2小时,直接用“deltaTime/(3600 * 1000)” > 24小时来判断是否昨天,就完全是扯蛋的逻辑了)
if (
srcMonth == yesterdayDate.getMonth() + 1 &&
srcDateD == yesterdayDate.getDate()
)
ret = "昨天" + timeExtraStr; // -1d
// “前天”判断逻辑同上
else if (
srcMonth == beforeYesterdayDate.getMonth() + 1 &&
srcDateD == beforeYesterdayDate.getDate()
)
ret = "前天" + timeExtraStr; // -2d
else {
// 跟当前时间相差的小时数
var deltaHour = deltaTime / (3600 * 1000);
// 如果小于或等 7*24小时就显示星期几
if (deltaHour <= 7 * 24) {
var weekday = new Array(7);
weekday[0] = "星期日";
weekday[1] = "星期一";
weekday[2] = "星期二";
weekday[3] = "星期三";
weekday[4] = "星期四";
weekday[5] = "星期五";
weekday[6] = "星期六";
// 取出当前是星期几
var weedayDesc = weekday[srcDate.getDay()];
ret = weedayDesc + timeExtraStr;
}
// 否则直接显示完整日期时间
else ret = _formatDate(srcDate, "yyyy.M.d") + timeExtraStr;
}
}
}
// 往年
else {
ret = _formatDate(srcDate, "yyyy.M.d") + timeExtraStr;
}
return ret;
}
// -----分割线------ 上面的比较垃圾
export const timeConversion = {
justNow: "刚刚",
past: (n: string) => {
return n.match(/\d/) ? `${n}之前` : n;
},
future: (n: string): string => {
return n.match(/\d/) ? `${n}之后` : n;
},
month: (n: number, past: any) => {
if (n === 1) {
return past ? "上个月" : "下个月";
}
return `${n}月`;
},
year: (n: number, past: any) => {
if (n === 1) {
return past ? "去年" : "明年";
}
return `${n}年`;
},
day: (n: number, past: any) => {
if (n === 1) {
return past ? "昨天" : "明天";
}
return `${n}天`;
},
week: (n: number, past: any) => {
if (n === 1) {
return past ? "上周" : "下周";
}
return `${n}周`;
},
hour: (n: any) => `${n}小时`,
minute: (n: any) => `${n}分钟`,
second: (n: any) => `${n}秒`,
invalid: ""
};
/**
* @description 计算时间戳函数 calculate timestamp
* @param timestamp
* @returns
*/
export const calculateTimestamp = (timestamp: number): string => {
const todayZero = new Date().setHours(0, 0, 0, 0);
const thisYear = new Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0).getTime();
const target = new Date(timestamp);
const oneDay = 24 * 60 * 60 * 1000;
const oneWeek = 7 * oneDay; // const oneYear = 365 * oneDay;
const diff = todayZero - target.getTime();
function formatNum(num: number): string {
return num < 10 ? `0${num}` : num.toString();
}
if (diff <= 0) {
// 今天,只显示小时:分钟
return `${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
}
if (diff <= oneDay) {
// 昨天,显示昨天:小时:分钟
return `昨天 ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
}
if (diff <= oneWeek - oneDay) {
// 一周内,显示工作日小时:分钟
const weekdays = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
const weekday = weekdays[target.getDay()];
return `${weekday} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
}
if (target.getTime() >= thisYear) {
// 一周以上、年内。显示月/日时:分
return `${target.getMonth() + 1}/${target.getDate()} ${formatNum(target.getHours())}:${formatNum(target.getMinutes())}`;
} // 不在今年内,显示 年/月/日 时:分
return `${target.getFullYear()}/${target.getMonth() + 1}/${target.getDate()} ${formatNum(target.getHours())}:${formatNum(
target.getMinutes()
)}`;
};
一些校验
var validateWechat = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入微信号'))
} else if (value.length < 6 || value.length > 20) {
callback(new Error('密码长度为6-20位'))
} else if (!/^[a-zA-Z_]([-_a-zA-Z0-9]{5,19})+$/.test(value)) {
callback(new Error('请使用6-20位数字、字母、下划线、减号或他们的组合'))
} else {
callback()
}
};
var checkWechat = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入微信号码'))
} else if (/.*[\u4e00-\u9fa5]+.*$/.test(value)) {
callback(new Error('微信号码不支持中文'))
} else {
callback()
}
}
/**
* @description 返回一个安全的访问对象,当访问的key不存在时,返回一个兜底的值
* @param targetObject 需要代理的对象或数组
* @param defaultParam 兜底值
* @returns ProxyObject
*/
export const getSecureObject = (targetObject: T, defaultParam: T | any): T => {
return new Proxy(targetObject, {
get(target, prop) {
const propHasTarget = prop in target;
return propHasTarget ? target[prop] : defaultParam;
}
});
};
/**
* 验证邮箱
* @param {*} rule
* @param {*} value
* @param {*} callback
*/
export function validateEmail(rule, value, callback) {
const mailReg = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)+/
if (!value) {
return callback()
}
if (mailReg.test(value)) {
callback()
} else {
callback(new Error('请输入正确的邮箱格式'))
}
}
聊天框列表总是在底部
import Vue from 'vue'
function scrollToBottom(el) {
el.scrollTop = el.scrollHeight
}
// Monitors an element and scrolls to the bottom if a new child is added
// (always:false = prevent scrolling if user manually scrolled up)
//
Vue.directive('chat-scroll', {
bind: function(el, binding) {
var timeout,
scrolled = false
const handleClick = (e) => {
if (timeout) window.clearTimeout(timeout)
timeout = window.setTimeout(function() {
scrolled = el.scrollTop + el.clientHeight + 1 < el.scrollHeight
}, 200)
}
el._debounceClick = handleClick // 将函数保存在元素的自定义属性中
el.addEventListener('scroll', handleClick)
new MutationObserver(function(e) {
var config = binding.value || {}
var pause = config.always === false && scrolled
if (pause || e[e.length - 1].addedNodes.length != 1) return
scrollToBottom(el)
}).observe(el, { childList: true })
},
inserted: scrollToBottom,
unbind: function(el) {
const handleClick = el._debounceClick
if (handleClick) {
el.removeEventListener('scroll', handleClick)
delete el._debounceClick // 清除保存的函数
}
}
})
ctrl+enter 或者shift+enter可以换行
handleKeyCode(event) {
if (event.key === 'Enter') {
if (event.ctrlKey || event.shiftKey || event.metaKey) {
event.preventDefault(); // 阻止默认的换行行为
this.msgTextContent += '\n'; // 在输入框中添加一个换行符
} else {
event.preventDefault(); // 阻止默认的换行行为
this.sendTextMsg();
}
}
},