项目初始化
This commit is contained in:
0
sheep/hooks/useApp.js
Normal file
0
sheep/hooks/useApp.js
Normal file
655
sheep/hooks/useGoods.js
Normal file
655
sheep/hooks/useGoods.js
Normal file
@@ -0,0 +1,655 @@
|
||||
import { ref } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import $url from '@/sheep/url';
|
||||
import { formatDate } from '@/sheep/util';
|
||||
|
||||
/**
|
||||
* 格式化销量
|
||||
* @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
|
||||
* @param {number} num 销量
|
||||
* @return {string} 格式化后的销量字符串
|
||||
*/
|
||||
export function formatSales(type, num) {
|
||||
let prefix = type !== 'exact' && num < 10 ? '销量' : '已售';
|
||||
return formatNum(prefix, type, num)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化兑换量
|
||||
* @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
|
||||
* @param {number} num 销量
|
||||
* @return {string} 格式化后的销量字符串
|
||||
*/
|
||||
export function formatExchange(type, num) {
|
||||
return formatNum('已兑换', type, num)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化库存
|
||||
* @param {'exact' | any} type 格式类型:exact=精确值,其它=大致数量
|
||||
* @param {number} num 销量
|
||||
* @return {string} 格式化后的销量字符串
|
||||
*/
|
||||
export function formatStock(type, num) {
|
||||
return formatNum('库存', type, num)
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化数字
|
||||
* @param {string} prefix 前缀
|
||||
* @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量
|
||||
* @param {number} num 销量
|
||||
* @return {string} 格式化后的销量字符串
|
||||
*/
|
||||
export function formatNum(prefix, type, num) {
|
||||
num = (num || 0);
|
||||
// 情况一:精确数值
|
||||
if (type === 'exact') {
|
||||
return prefix + num;
|
||||
}
|
||||
// 情况二:小于等于 10
|
||||
if (num < 10) {
|
||||
return `${prefix}≤10`;
|
||||
}
|
||||
// 情况三:大于 10,除第一位外,其它位都显示为0
|
||||
// 例如:100 - 199 显示为 100+
|
||||
// 9000 - 9999 显示为 9000+
|
||||
const numStr = num.toString();
|
||||
const first = numStr[0];
|
||||
const other = '0'.repeat(numStr.length - 1);
|
||||
return `${prefix}${first}${other}+`;
|
||||
}
|
||||
|
||||
// 格式化价格
|
||||
export function formatPrice(e) {
|
||||
return e.length === 1 ? e[0] : e.join('~');
|
||||
}
|
||||
|
||||
// 视频格式后缀列表
|
||||
const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']
|
||||
|
||||
/**
|
||||
* 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接
|
||||
*
|
||||
* @param {string[]} urlList 链接列表
|
||||
* @return {{src: string, type: 'video' | 'image' }[]} 转换后的链接列表
|
||||
*/
|
||||
export function formatGoodsSwiper(urlList) {
|
||||
return urlList?.filter(url => url).map((url, key) => {
|
||||
const isVideo = VIDEO_SUFFIX_LIST.some(suffix => url.includes(suffix));
|
||||
const type = isVideo ? 'video' : 'image'
|
||||
const src = $url.cdn(url);
|
||||
return { type, src }
|
||||
}) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态的颜色
|
||||
*
|
||||
* @param order 订单
|
||||
* @return {string} 颜色的 class 名称
|
||||
*/
|
||||
export function formatOrderColor(order) {
|
||||
if (order.status === 0) {
|
||||
return 'info-color';
|
||||
}
|
||||
if (order.status === 10
|
||||
|| order.status === 20
|
||||
|| (order.status === 30 && !order.commentStatus)) {
|
||||
return 'warning-color';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return 'success-color';
|
||||
}
|
||||
return 'danger-color';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatOrderStatus(order) {
|
||||
if (order.status === 0) {
|
||||
return '待付款';
|
||||
}
|
||||
if (order.status === 10 && order.deliveryType === 1) {
|
||||
return '待发货';
|
||||
}
|
||||
if (order.status === 10 && order.deliveryType === 2) {
|
||||
return '待核销';
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '待收货';
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '待评价';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '已完成';
|
||||
}
|
||||
return '已关闭';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatWorkerOrderStatus(order) {
|
||||
if (order.status === 0) {
|
||||
order.statusStr = '待付款';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
order.statusStr = '待服务';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 20) {
|
||||
order.statusStr = '服务中';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 30) {
|
||||
order.statusStr = '已完成';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
order.statusStr = '已取消';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatBlindOrderStatus(order) {
|
||||
if (order.status === 0) {
|
||||
order.statusStr = '待付款';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
order.statusStr = '待抢单';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 20) {
|
||||
order.statusStr = '服务中';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 30) {
|
||||
order.statusStr = '已完成';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
order.statusStr = '已取消';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatMyOrderStatus(order) {
|
||||
if (order.status === 0) {
|
||||
order.statusStr = '待付款';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
order.statusStr = '待服务';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 20) {
|
||||
order.statusStr = '服务中';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
order.statusStr = '待评价';
|
||||
return order.statusStr;
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
order.statusStr = '已完成';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
order.statusStr = '已取消';
|
||||
return order.statusStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态的描述
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatOrderStatusDescription(order) {
|
||||
if (order.status === 0) {
|
||||
return `请在 ${ formatDate(order.payExpireTime) } 前完成支付`;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
return '商家未发货,请耐心等待';
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '商家已发货,请耐心等待';
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '已收货,快去评价一下吧';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '交易完成,感谢您的支持';
|
||||
}
|
||||
return '订单取消';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态的描述
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatMyOrderStatusDescription(order) {
|
||||
if (order.status === 0) {
|
||||
return `请在 ${ formatDate(order.payExpireTime) } 前完成支付`;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
return '店员未接单,请耐心等待';
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '店员已接单,请打开微信搜索权限,等待添加';
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '店员已完成服务,快去评价一下吧';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '店员已完成服务,感谢您的支持';
|
||||
}
|
||||
return '订单已取消,有缘自会相遇';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化订单状态的描述
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatWorkerOrderStatusDescription(order) {
|
||||
if (order.status === 0) {
|
||||
return `老板未付款,可以主动联系老板下单`;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
return '老板已付款,请尽快添加老板,开始服务';
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '已抢单成功,快去添加老板,开始服务';
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '已完成服务,快去邀请老板评价一下吧';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '已完成服务,做的很好';
|
||||
}
|
||||
return '交易关闭';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化订单状态的描述
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function formatBlindOrderStatusDescription(order) {
|
||||
if (order.status === 0) {
|
||||
return `老板未付款,可以主动联系老板下单`;
|
||||
}
|
||||
if (order.status === 10) {
|
||||
return '老板已付款,抢单成功后添加老板,开始服务';
|
||||
}
|
||||
if (order.status === 20) {
|
||||
return '已抢单成功,快去添加老板,开始服务';
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) {
|
||||
return '已完成服务,快去邀请老板评价一下吧';
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) {
|
||||
return '已完成服务,做的很好';
|
||||
}
|
||||
return '交易关闭';
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订单的 button 操作按钮数组
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function handleOrderButtons(order) {
|
||||
order.buttons = []
|
||||
if (order.type === 3) { // 查看拼团
|
||||
order.buttons.push('combination');
|
||||
}
|
||||
if (order.status === 20) { // 确认收货
|
||||
order.buttons.push('confirm');
|
||||
}
|
||||
if (order.logisticsId > 0) { // 查看物流
|
||||
order.buttons.push('express');
|
||||
}
|
||||
if (order.status === 0) { // 取消订单 / 发起支付
|
||||
order.buttons.push('cancel');
|
||||
order.buttons.push('pay');
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) { // 发起评价
|
||||
order.buttons.push('comment');
|
||||
}
|
||||
if (order.status === 40) { // 删除订单
|
||||
order.buttons.push('delete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订单的 button 操作按钮数组
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function handleWorkerOrderButtons(order) {
|
||||
order.buttons = []
|
||||
if (order.status === 10) { // 待服务
|
||||
order.buttons.push('unpay');
|
||||
order.buttons.push('agree');
|
||||
}
|
||||
if (order.status === 20) { // 服务中
|
||||
order.buttons.push('unpay');
|
||||
order.buttons.push('confirm');
|
||||
}
|
||||
if (order.status === 0) { // 取消订单 / 发起支付
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('invite');
|
||||
}
|
||||
if (order.status === 30) { // 已完成
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('invite');
|
||||
}
|
||||
if (order.status === 40) { // 取消订单
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('invite');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订单的 button 操作按钮数组
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function handleBlindOrderButtons(order, userId) {
|
||||
order.buttons = []
|
||||
if (order.status === 10) { // 待服务
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('agree');
|
||||
//order.buttons.push('invite');
|
||||
}
|
||||
if (order.status === 20) { // 服务中
|
||||
// 抢单后
|
||||
if(order.workerUserId == userId){
|
||||
order.buttons.push('unpay');
|
||||
order.buttons.push('confirm');
|
||||
}else{
|
||||
order.buttons.push('other');
|
||||
}
|
||||
}
|
||||
if (order.status === 0) { // 取消订单 / 发起支付
|
||||
order.buttons.push('detail');
|
||||
//order.buttons.push('invite');
|
||||
}
|
||||
if (order.status === 30) { // 已完成
|
||||
order.buttons.push('detail');
|
||||
if(order.workerUserId == userId){
|
||||
order.buttons.push('invite2');
|
||||
}
|
||||
}
|
||||
if (order.status === 40) { // 取消订单
|
||||
order.buttons.push('detail');
|
||||
//order.buttons.push('invite');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订单的 button 操作按钮数组
|
||||
*
|
||||
* @param order 订单
|
||||
*/
|
||||
export function handleMyOrderButtons(order) {
|
||||
order.buttons = []
|
||||
if (order.type === 3) { // 查看拼团
|
||||
order.buttons.push('combination');
|
||||
}
|
||||
if (order.status === 10) { // 待服务
|
||||
order.buttons.push('unpay');
|
||||
order.buttons.push('buy');
|
||||
}
|
||||
if (order.status === 20) { // 服务中
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('buy');
|
||||
}
|
||||
if (order.logisticsId > 0) { // 查看物流
|
||||
order.buttons.push('express');
|
||||
}
|
||||
if (order.status === 0) { // 取消订单 / 发起支付
|
||||
order.buttons.push('cancel');
|
||||
order.buttons.push('pay');
|
||||
}
|
||||
if (order.status === 30 && !order.commentStatus) { // 发起评价
|
||||
order.buttons.push('comment');
|
||||
order.buttons.push('buy');
|
||||
}
|
||||
if (order.status === 30 && order.commentStatus) { // 发起评价
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('buy');
|
||||
}
|
||||
if (order.status === 40) { // 删除订单
|
||||
//order.buttons.push('delete');
|
||||
order.buttons.push('detail');
|
||||
order.buttons.push('buy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化售后状态
|
||||
*
|
||||
* @param afterSale 售后
|
||||
*/
|
||||
export function formatAfterSaleStatus(afterSale) {
|
||||
if (afterSale.status === 10) {
|
||||
return '申请售后';
|
||||
}
|
||||
if (afterSale.status === 20) {
|
||||
return '商品待退货';
|
||||
}
|
||||
if (afterSale.status === 30) {
|
||||
return '商家待收货';
|
||||
}
|
||||
if (afterSale.status === 40) {
|
||||
return '等待退款';
|
||||
}
|
||||
if (afterSale.status === 50) {
|
||||
return '取消成功';
|
||||
}
|
||||
if (afterSale.status === 61) {
|
||||
return '买家取消';
|
||||
}
|
||||
if (afterSale.status === 62) {
|
||||
return '商家拒绝';
|
||||
}
|
||||
if (afterSale.status === 63) {
|
||||
return '商家拒收货';
|
||||
}
|
||||
return '未知状态';
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化售后状态的描述
|
||||
*
|
||||
* @param afterSale 售后
|
||||
*/
|
||||
export function formatAfterSaleStatusDescription(afterSale) {
|
||||
if (afterSale.status === 10) {
|
||||
return '退款申请待商家处理';
|
||||
}
|
||||
if (afterSale.status === 20) {
|
||||
return '请退货并填写物流信息';
|
||||
}
|
||||
if (afterSale.status === 30) {
|
||||
return '退货退款申请待商家处理';
|
||||
}
|
||||
if (afterSale.status === 40) {
|
||||
return '等待退款';
|
||||
}
|
||||
if (afterSale.status === 50) {
|
||||
return '退款成功';
|
||||
}
|
||||
if (afterSale.status === 61) {
|
||||
return '退款关闭';
|
||||
}
|
||||
if (afterSale.status === 62) {
|
||||
return `商家不同意退款申请,拒绝原因:${afterSale.auditReason}`;
|
||||
}
|
||||
if (afterSale.status === 63) {
|
||||
return `商家拒绝收货,不同意退款,拒绝原因:${afterSale.auditReason}`;
|
||||
}
|
||||
return '未知状态';
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理售后的 button 操作按钮数组
|
||||
*
|
||||
* @param afterSale 售后
|
||||
*/
|
||||
export function handleAfterSaleButtons(afterSale) {
|
||||
afterSale.buttons = [];
|
||||
if ([10, 20, 30].includes(afterSale.status)) { // 取消订单
|
||||
afterSale.buttons.push('cancel');
|
||||
}
|
||||
if (afterSale.status === 20) { // 退货信息
|
||||
afterSale.buttons.push('delivery');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时
|
||||
* @param toTime 截止时间
|
||||
* @param fromTime 起始时间,默认当前时间
|
||||
* @return {{s: string, ms: number, h: string, m: string}} 持续时间
|
||||
*/
|
||||
export function useDurationTime(toTime, fromTime = '') {
|
||||
toTime = getDayjsTime(toTime);
|
||||
if (fromTime === '') {
|
||||
fromTime = dayjs();
|
||||
}
|
||||
let duration = ref(toTime - fromTime);
|
||||
if (duration.value > 0) {
|
||||
setTimeout(() => {
|
||||
if (duration.value > 0) {
|
||||
duration.value -= 1000;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
let durationTime = dayjs.duration(duration.value);
|
||||
return {
|
||||
h: (durationTime.months() * 30 * 24 + durationTime.days() * 24 + durationTime.hours())
|
||||
.toString()
|
||||
.padStart(2, '0'),
|
||||
m: durationTime.minutes().toString().padStart(2, '0'),
|
||||
s: durationTime.seconds().toString().padStart(2, '0'),
|
||||
ms: durationTime.$ms,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 Dayjs
|
||||
* @param {any} time 时间
|
||||
* @return {dayjs.Dayjs}
|
||||
*/
|
||||
function getDayjsTime(time) {
|
||||
time = time.toString();
|
||||
if (time.indexOf('-') > 0) {
|
||||
// 'date'
|
||||
return dayjs(time);
|
||||
}
|
||||
if (time.length > 10) {
|
||||
// 'timestamp'
|
||||
return dayjs(parseInt(time));
|
||||
}
|
||||
if (time.length === 10) {
|
||||
// 'unixTime'
|
||||
return dayjs.unix(parseInt(time));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分转成元
|
||||
*
|
||||
* @param price 分,例如说 100 分
|
||||
* @returns {string} 元,例如说 1.00 元
|
||||
*/
|
||||
export function fen2yuan(price) {
|
||||
return (price / 100.0).toFixed(2)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从商品 SKU 数组中,转换出商品属性的数组
|
||||
*
|
||||
* 类似结构:[{
|
||||
* id: // 属性的编号
|
||||
* name: // 属性的名字
|
||||
* values: [{
|
||||
* id: // 属性值的编号
|
||||
* name: // 属性值的名字
|
||||
* }]
|
||||
* }]
|
||||
*
|
||||
* @param skus 商品 SKU 数组
|
||||
*/
|
||||
export function convertProductPropertyList(skus) {
|
||||
let result = [];
|
||||
for (const sku of skus) {
|
||||
if (!sku.properties) {
|
||||
continue
|
||||
}
|
||||
for (const property of sku.properties) {
|
||||
// ① 先处理属性
|
||||
let resultProperty = result.find(item => item.id === property.propertyId)
|
||||
if (!resultProperty) {
|
||||
resultProperty = {
|
||||
id: property.propertyId,
|
||||
name: property.propertyName,
|
||||
values: []
|
||||
}
|
||||
result.push(resultProperty)
|
||||
}
|
||||
// ② 再处理属性值
|
||||
let resultValue = resultProperty.values.find(item => item.id === property.valueId)
|
||||
if (!resultValue) {
|
||||
resultProperty.values.push({
|
||||
id: property.valueId,
|
||||
name: property.valueName
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化满减送活动的规则
|
||||
*
|
||||
* @param activity 活动信息
|
||||
* @param rule 优惠规格
|
||||
* @returns {string} 规格字符串
|
||||
*/
|
||||
export function formatRewardActivityRule(activity, rule) {
|
||||
if (activity.conditionType === 10) {
|
||||
return `满 ${fen2yuan(rule.limit)} 元减 ${fen2yuan(rule.discountPrice)} 元`;
|
||||
}
|
||||
if (activity.conditionType === 20) {
|
||||
return `满 ${rule.limit} 件减 ${fen2yuan(rule.discountPrice)} 元`;
|
||||
}
|
||||
return '';
|
||||
}
|
141
sheep/hooks/useModal.js
Normal file
141
sheep/hooks/useModal.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import $store from '@/sheep/store';
|
||||
import $helper from '@/sheep/helper';
|
||||
import dayjs from 'dayjs';
|
||||
import { ref } from 'vue';
|
||||
import test from '@/sheep/helper/test.js';
|
||||
import AuthUtil from '@/sheep/api/member/auth';
|
||||
|
||||
// 打开授权弹框
|
||||
export function showAuthModal(type = 'smsLogin') {
|
||||
const modal = $store('modal');
|
||||
if (modal.auth !== '') {
|
||||
// 注意:延迟修改,保证下面的 closeAuthModal 先执行掉
|
||||
setTimeout(() => {
|
||||
modal.$patch((state) => {
|
||||
state.auth = type;
|
||||
});
|
||||
}, 500);
|
||||
closeAuthModal();
|
||||
} else {
|
||||
modal.$patch((state) => {
|
||||
state.auth = type;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭授权弹框
|
||||
export function closeAuthModal() {
|
||||
$store('modal').$patch((state) => {
|
||||
state.auth = '';
|
||||
});
|
||||
}
|
||||
|
||||
// 打开分享弹框
|
||||
export function showShareModal() {
|
||||
$store('modal').$patch((state) => {
|
||||
state.share = true;
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭分享弹框
|
||||
export function closeShareModal() {
|
||||
$store('modal').$patch((state) => {
|
||||
state.share = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 打开快捷菜单
|
||||
export function showMenuTools() {
|
||||
$store('modal').$patch((state) => {
|
||||
state.menu = true;
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭快捷菜单
|
||||
export function closeMenuTools() {
|
||||
$store('modal').$patch((state) => {
|
||||
state.menu = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 发送短信验证码 60秒
|
||||
export function getSmsCode(event, mobile) {
|
||||
const modalStore = $store('modal');
|
||||
const lastSendTimer = modalStore.lastTimer[event];
|
||||
if (typeof lastSendTimer === 'undefined') {
|
||||
$helper.toast('短信发送事件错误');
|
||||
return;
|
||||
}
|
||||
|
||||
const duration = dayjs().unix() - lastSendTimer;
|
||||
const canSend = duration >= 60;
|
||||
if (!canSend) {
|
||||
$helper.toast('请稍后再试');
|
||||
return;
|
||||
}
|
||||
// 只有 mobile 非空时才校验。因为部分场景(修改密码),不需要输入手机
|
||||
if (mobile && !test.mobile(mobile)) {
|
||||
$helper.toast('手机号码格式不正确');
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送验证码 + 更新上次发送验证码时间
|
||||
let scene = -1;
|
||||
switch (event) {
|
||||
case 'resetPassword':
|
||||
scene = 4;
|
||||
break;
|
||||
case 'changePassword':
|
||||
scene = 3;
|
||||
break;
|
||||
case 'changeMobile':
|
||||
scene = 2;
|
||||
break;
|
||||
case 'smsLogin':
|
||||
scene = 1;
|
||||
break;
|
||||
}
|
||||
AuthUtil.sendSmsCode(mobile, scene).then((res) => {
|
||||
if (res.code === 0) {
|
||||
modalStore.$patch((state) => {
|
||||
state.lastTimer[event] = dayjs().unix();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取短信验证码倒计时 -- 60秒
|
||||
export function getSmsTimer(event, mobile = '') {
|
||||
const modalStore = $store('modal');
|
||||
const lastSendTimer = modalStore.lastTimer[event];
|
||||
|
||||
if (typeof lastSendTimer === 'undefined') {
|
||||
$helper.toast('短信发送事件错误');
|
||||
return;
|
||||
}
|
||||
|
||||
const duration = ref(dayjs().unix() - lastSendTimer - 60);
|
||||
const canSend = duration.value >= 0;
|
||||
|
||||
if (canSend) {
|
||||
return '获取验证码';
|
||||
}
|
||||
|
||||
if (!canSend) {
|
||||
setTimeout(() => {
|
||||
duration.value++;
|
||||
}, 1000);
|
||||
return -duration.value.toString() + ' 秒';
|
||||
}
|
||||
}
|
||||
|
||||
// 记录广告弹框历史
|
||||
export function saveAdvHistory(adv) {
|
||||
const modal = $store('modal');
|
||||
|
||||
modal.$patch((state) => {
|
||||
if (!state.advHistory.includes(adv.imgUrl)) {
|
||||
state.advHistory.push(adv.imgUrl);
|
||||
}
|
||||
});
|
||||
}
|
163
sheep/hooks/useWebSocket.js
Normal file
163
sheep/hooks/useWebSocket.js
Normal file
@@ -0,0 +1,163 @@
|
||||
import { onBeforeUnmount, reactive, ref, getCurrentInstance } from 'vue';
|
||||
import { baseUrl, websocketPath } from '@/sheep/config';
|
||||
import { copyValueToTarget } from '@/sheep/util';
|
||||
|
||||
/**
|
||||
* WebSocket 创建 hook
|
||||
* @param opt 连接配置
|
||||
* @return {{options: *}}
|
||||
*/
|
||||
export function useWebSocket(opt) {
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const getAccessToken = () => {
|
||||
return uni.getStorageSync('token');
|
||||
};
|
||||
|
||||
const getUrl = () => {
|
||||
return (baseUrl + websocketPath).replace('http', 'ws') + '?token=' + getAccessToken();
|
||||
};
|
||||
|
||||
const options = reactive({
|
||||
url: getUrl(), // ws 地址
|
||||
isReconnecting: false, // 正在重新连接
|
||||
reconnectInterval: 3000, // 重连间隔,单位毫秒
|
||||
heartBeatInterval: 5000, // 心跳间隔,单位毫秒
|
||||
pingTimeoutDuration: 1000, // 超过这个时间,后端没有返回pong,则判定后端断线了。
|
||||
heartBeatTimer: null, // 心跳计时器
|
||||
destroy: false, // 是否销毁
|
||||
pingTimeout: null, // 心跳检测定时器
|
||||
reconnectTimeout: null, // 重连定时器ID的属性
|
||||
onConnected: () => {
|
||||
}, // 连接成功时触发
|
||||
onClosed: () => {
|
||||
}, // 连接关闭时触发
|
||||
onMessage: (data) => {
|
||||
}, // 收到消息
|
||||
});
|
||||
const SocketTask = ref(null); // SocketTask 由 uni.connectSocket() 接口创建
|
||||
|
||||
const initEventListeners = () => {
|
||||
// 监听 WebSocket 连接打开事件
|
||||
SocketTask.value.onOpen(() => {
|
||||
console.log('WebSocket 连接成功');
|
||||
// 连接成功时触发
|
||||
options.onConnected();
|
||||
// 开启心跳检查
|
||||
startHeartBeat();
|
||||
});
|
||||
// 监听 WebSocket 接受到服务器的消息事件
|
||||
SocketTask.value.onMessage((res) => {
|
||||
try {
|
||||
if (res.data === 'pong') {
|
||||
// 收到心跳重置心跳超时检查
|
||||
resetPingTimeout();
|
||||
} else {
|
||||
options.onMessage(JSON.parse(res.data));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
// 监听 WebSocket 连接关闭事件
|
||||
SocketTask.value.onClose((event) => {
|
||||
// 情况一:实例销毁
|
||||
if (options.destroy) {
|
||||
options.onClosed();
|
||||
} else { // 情况二:连接失败重连
|
||||
// 停止心跳检查
|
||||
stopHeartBeat();
|
||||
// 重连
|
||||
reconnect();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = (message) => {
|
||||
if (SocketTask.value && !options.destroy) {
|
||||
SocketTask.value.send({ data: message });
|
||||
}
|
||||
};
|
||||
// 开始心跳检查
|
||||
const startHeartBeat = () => {
|
||||
options.heartBeatTimer = setInterval(() => {
|
||||
sendMessage('ping');
|
||||
options.pingTimeout = setTimeout(() => {
|
||||
// 如果在超时时间内没有收到 pong,则认为连接断开
|
||||
reconnect();
|
||||
}, options.pingTimeoutDuration);
|
||||
}, options.heartBeatInterval);
|
||||
};
|
||||
// 停止心跳检查
|
||||
const stopHeartBeat = () => {
|
||||
clearInterval(options.heartBeatTimer);
|
||||
resetPingTimeout();
|
||||
};
|
||||
|
||||
// WebSocket 重连
|
||||
const reconnect = () => {
|
||||
if (options.destroy || !SocketTask.value) {
|
||||
// 如果WebSocket已被销毁或尚未完全关闭,不进行重连
|
||||
return;
|
||||
}
|
||||
|
||||
// 重连中
|
||||
options.isReconnecting = true;
|
||||
|
||||
// 清除现有的重连标志,以避免多次重连
|
||||
if (options.reconnectTimeout) {
|
||||
clearTimeout(options.reconnectTimeout);
|
||||
}
|
||||
|
||||
// 设置重连延迟
|
||||
options.reconnectTimeout = setTimeout(() => {
|
||||
// 检查组件是否仍在运行和WebSocket是否关闭
|
||||
if (!options.destroy) {
|
||||
// 重置重连标志
|
||||
options.isReconnecting = false;
|
||||
// 初始化新的WebSocket连接
|
||||
initSocket();
|
||||
}
|
||||
}, options.reconnectInterval);
|
||||
};
|
||||
|
||||
const resetPingTimeout = () => {
|
||||
if (options.pingTimeout) {
|
||||
clearTimeout(options.pingTimeout);
|
||||
options.pingTimeout = null; // 清除超时ID
|
||||
}
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
options.destroy = true;
|
||||
stopHeartBeat();
|
||||
if (options.reconnectTimeout) {
|
||||
clearTimeout(options.reconnectTimeout);
|
||||
}
|
||||
if (SocketTask.value) {
|
||||
SocketTask.value.close();
|
||||
SocketTask.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const initSocket = () => {
|
||||
options.destroy = false;
|
||||
copyValueToTarget(options, opt);
|
||||
SocketTask.value = uni.connectSocket({
|
||||
url: getUrl(),
|
||||
complete: () => {
|
||||
},
|
||||
success: () => {
|
||||
},
|
||||
});
|
||||
initEventListeners();
|
||||
};
|
||||
|
||||
initSocket();
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
close();
|
||||
});
|
||||
return { options };
|
||||
}
|
Reference in New Issue
Block a user