Files
lilishop-ui/im/src/utils/functions.js
pikachu1995@126.com 97e611c072 refactor(im): 重构聊天面板UI组件并修复多处交互问题
- 将头像组件从 dicebear 外链改为本地首字+底色生成,新增 avatarColor 工具函数
- 重构订单卡片(footPrint.vue)布局,优化商品与订单信息展示结构
- 重构店铺详情卡片(storeDetail.vue),统一信息行与评分行样式
- 移除消息气泡中的昵称时间行,调整气泡背景色与头像内边距
- 修复 face 变量名与全局组件 <face> 冲突导致头像URL被当作标签渲染
- 将 unload 事件监听替换为 pagehide,兼容 Chrome 弃用策略
- 修复搜索关键词未转义正则元字符导致输入特殊字符时抛异常
- 修复 WebSocket onMessage 对无 text 字段的推送消息误调用 JSON.parse
- 将 setup 中同步调用全局 mixin 方法移至 onMounted,避免代理未就绪
- 商品/订单图片统一添加 onerror 默认图兜底
- 聊天列表消息预览支持 emoji 图片渲染(textReplaceEmoji)
- 搜索框增加 clearable 属性及"没有找到相关好友"空态提示
- 锁定 element-plus 版本为 2.14.1 防止自动升级引入兼容问题
- 日期格式化分隔符由斜杠改为横杠
- 提现设置页新增提现规则说明字段,满足微信审核公示要求
2026-10-29 10:56:28 +08:00

538 lines
12 KiB
JavaScript

/** 公共方法类 */
import { getToken } from "@/utils/auth";
import config from "@/config/config";
/**
* 人性化时间显示
*
* @param {Object} datetime
*/
export function formatTime (datetime) {
if (datetime == null) return "";
datetime = datetime.replace(/-/g, "/");
let time = new Date();
let outTime = new Date(datetime);
if (/^[1-9]\d*$/.test(datetime)) {
outTime = new Date(parseInt(datetime) * 1000);
}
if (
time.getTime() < outTime.getTime() ||
time.getFullYear() != outTime.getFullYear()
) {
return parseTime(outTime, "{y}-{m}-{d} {h}:{i}");
}
if (time.getMonth() != outTime.getMonth()) {
return parseTime(outTime, "{m}-{d} {h}:{i}");
}
if (time.getDate() != outTime.getDate()) {
let day = outTime.getDate() - time.getDate();
if (day == -1) {
return parseTime(outTime, "昨天 {h}:{i}");
}
if (day == -2) {
return parseTime(outTime, "前天 {h}:{i}");
}
return parseTime(outTime, "{m}-{d} {h}:{i}");
}
if (time.getHours() != outTime.getHours()) {
return parseTime(outTime, "{h}:{i}");
}
let minutes = outTime.getMinutes() - time.getMinutes();
if (minutes == 0) {
return "刚刚";
}
minutes = Math.abs(minutes);
return `${minutes}分钟前`;
}
/**
* 格式化文件大小
*
* @param {String} value 文件大小(字节)
*/
export function formatSize (value) {
if (null == value || value == "") {
return "0";
}
let unitArr = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
let index = 0;
let srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
let size = srcsize / Math.pow(1024, index);
size = size.toFixed(2); //保留的小数位数
return size + unitArr[index];
}
/**
* 获取文件后缀名
*
* @param {String} fileName
*/
export function getFileExt (fileName) {
let ext = fileName.split(".");
ext = ext[ext.length - 1]; // 获取文件后缀名
return ext;
}
/**
* 根据图片url下载图片
* @param {String} imgsrc
* @param {String} name
*/
export function downloadIamge (imgsrc, name) {
//下载图片地址和图片名
let image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function () {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
let a = document.createElement("a"); // 生成一个a元素
let event = new MouseEvent("click"); // 创建一个单击事件
a.download = name || "photo"; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = imgsrc;
}
/**
* 通过图片url获取图片大小
*
* @param {String} imgsrc 例如图片名: D8x5f13a53dbc4b9_350x345.png
*/
export function getImageInfo (imgsrc) {
let data = {
width: 0,
height: 0,
};
let arr = imgsrc.split("_");
if (arr.length == 1) return data;
let info = arr[arr.length - 1].match(/\d+x\d+/g);
if (info == null) return data;
info = info[0].split("x");
return {
width: parseInt(info[0]),
height: parseInt(info[1]),
};
}
/**
* 文件下载方法
*
* @param {Number} cr_id
*/
export function download (cr_id) {
let api = config.BASE_API_URL;
let token = getToken();
try {
let link = document.createElement("a");
link.href = `${api}/download/user-chat-file?cr_id=${cr_id}&token=${token}`;
link.click();
} catch (e) { }
}
/**
* 时间格式化方法
*
* @param {(Object|string|number)} time
* @param {String} cFormat
* @returns {String | null}
*/
export function parseTime (time, cFormat) {
if (arguments.length === 0) {
return null;
}
let date;
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
if (typeof time === "object") {
date = time;
} else {
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
time = parseInt(time);
}
if (typeof time === "number" && time.toString().length === 10) {
time = time * 1000;
console.log("时间判断为number");
}
date = new Date(time.replace(/-/g, "/"));
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key];
// Note: getDay() returns 0 on Sunday
if (key === "a") {
return ["日", "一", "二", "三", "四", "五", "六"][value];
}
return value.toString().padStart(2, "0");
});
return time_str;
}
/**
* 去除字符串控制
*
* @param {String} str
*/
export function trim (str, type = null) {
if (type) {
return str.replace(/(^\s*)|(\s*$)/g, "");
} else if (type == "l") {
return str.replace(/(^\s*)/g, "");
} else {
return str.replace(/(\s*$)/g, "");
}
}
/**
* 解析url中参数
*
* @param {String} url
* @returns {Object}
*/
export function param2Obj (url) {
const search = url.split("?")[1];
if (!search) return {};
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')
.replace(/\+/g, " ") +
'"}'
);
}
/**
* @param {Object} json
* @returns {Array}
*/
export function param (json) {
if (!json) return "";
return cleanArray(
Object.keys(json).map((key) => {
if (json[key] === undefined) return "";
return encodeURIComponent(key) + "=" + encodeURIComponent(json[key]);
})
).join("&");
}
/**
* @param {Array} actual
* @returns {Array}
*/
export function cleanArray (actual) {
const newArray = [];
for (let i = 0; i < actual.length; i++) {
if (actual[i]) {
newArray.push(actual[i]);
}
}
return newArray;
}
/**
* @param {HTMLElement} element
* @param {String} className
*/
export function toggleClass (element, className) {
if (!element || !className) {
return;
}
let classString = element.className;
let nameIndex = classString.indexOf(className);
if (nameIndex === -1) {
classString += "" + className;
} else {
classString =
classString.substr(0, nameIndex) +
classString.substr(nameIndex + className.length);
}
element.className = classString;
}
/**
* Check if an element has a class
*
* @param {HTMLElement} elm
* @param {String} cls
* @returns {Boolean}
*/
export function hasClass (ele, cls) {
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
}
/**
* Add class to element
*
* @param {HTMLElement} elm
* @param {String} cls
*/
export function addClass (ele, cls) {
if (!hasClass(ele, cls)) ele.className += " " + cls;
}
/**
* Remove class from element
*
* @param {HTMLElement} elm
* @param {String} cls
*/
export function removeClass (ele, cls) {
if (hasClass(ele, cls)) {
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
ele.className = ele.className.replace(reg, " ");
}
}
/**
* 通过图片Url获取图片等比例缩放的宽度和高度信息
*
* @param {String} src
* @param {Number} width
*/
export function imgZoom (src, width = 200) {
const info = getImageInfo(src);
if (info.width < width) {
return {
width: `${info.width}px`,
height: `${info.height}px`,
};
}
return {
width: width + "px",
height: parseInt(info.height / (info.width / width)) + "px",
};
}
/**
* 获取浏览器光标选中内容
*
* @export
* @returns
*/
export function getSelection () {
return window.getSelection
? window.getSelection().toString()
: document.selection.createRange().text;
}
/**
* 剪贴板复制功能
*
* @param {String} value 复制内容
* @param {Function} callback 复制成功回调方法
*/
export const copyTextToClipboard = (value, callback) => {
let textArea = document.createElement("textarea");
textArea.style.background = "transparent";
textArea.value = value;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
if (callback) callback();
} catch (err) {
this.$message.error("Oops, unable to copy");
}
document.body.removeChild(textArea);
};
/**
* 隐藏用户手机号中间四位
*
* @param {String} phone 手机号
*/
export function hidePhone (phone) {
return phone.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}
/**
* 根据名字生成稳定的头像底色(本地首字头像用,替代 dicebear 外链)
*
* @param {String} name
*/
export function avatarColor (name = "") {
let hash = 0;
for (let i = 0; i < name.length; i++) {
hash = (hash * 31 + name.charCodeAt(i)) >>> 0;
}
return `hsl(${hash % 360}, 55%, 55%)`;
}
/**
* 人性化显示时间
*
* @param {Object} datetime
*/
export function beautifyTime (datetime = "") {
if (datetime == null) {
return "";
}
datetime = datetime.replace(/-/g, "/");
let time = new Date();
let outTime = new Date(datetime);
if (/^[1-9]\d*$/.test(datetime)) {
outTime = new Date(parseInt(datetime) * 1000);
}
if (time.getTime() < outTime.getTime()) {
return parseTime(outTime, "{y}-{m}-{d}");
}
if (time.getFullYear() != outTime.getFullYear()) {
return parseTime(outTime, "{y}-{m}-{d}");
}
if (time.getMonth() != outTime.getMonth()) {
return parseTime(outTime, "{m}-{d}");
}
if (time.getDate() != outTime.getDate()) {
let day = outTime.getDate() - time.getDate();
if (day == -1) {
return parseTime(outTime, "昨天 {h}:{i}");
}
if (day == -2) {
return parseTime(outTime, "前天 {h}:{i}");
}
return parseTime(outTime, "{m}-{d}");
}
if (time.getHours() != outTime.getHours()) {
return parseTime(outTime, "{h}:{i}");
}
let minutes = outTime.getMinutes() - time.getMinutes();
if (minutes == 0) {
return "刚刚";
}
minutes = Math.abs(minutes);
return `${minutes}分钟前`;
}
export function getSort (fn) {
return function (a, b) {
let ret = 0;
if (fn.call(this, a, b)) {
ret = -1;
} else if (fn.call(this, b, a)) {
ret = 1;
}
return ret;
};
}
/**
* 批量排序
*
* @param {*} arr
*/
export function getMutipSort (arr) {
return function (a, b) {
let tmp;
let i = 0;
do {
tmp = arr[i++](a, b);
} while (tmp == 0 && i < arr.length);
return tmp;
};
}
/**
* Url 替换超链接
*
* @param {String} text 文本
* @param {String} color 超链接颜色
*/
export function textReplaceLink (text, color = "#409eff") {
let exp =
/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
return text.replace(
exp,
`<a href='$1' target="_blank" style="color:${color};text-decoration: revert;">$1</a >`
);
}
/**
* 防抖
*
* @param {*} func
* @param {*} wait
* @param {*} immediate
* @returns
*/
export function debounce (func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout 不为null
if (immediate) {
let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
};
}