mrr.sj.front/utils/service.js

347 lines
11 KiB
JavaScript
Raw 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.

export class ServiceManager {
constructor() {
this.availableSlots = {}
this.serviceDurations = {}
this.blockedTimes = {}
}
// 设置工作日工作时间
setWorkingHours(day, startTime, endTime) {
if (!this.availableSlots[day]) {
this.availableSlots[day] = []
}
this.availableSlots[day].push([startTime, endTime])
}
// 添加服务
addService(serviceName, durationMinutes) {
this.serviceDurations[serviceName] = durationMinutes
}
// 添加不可预约时间
addBlockedTime(day, startTime, endTime) {
if (!this.blockedTimes[day]) {
this.blockedTimes[day] = []
}
this.blockedTimes[day].push([startTime, endTime])
}
// 获取可用时间段
getAvailableSlots(day, serviceName, existingAppointments = []) {
const duration = this.serviceDurations[serviceName]
if (!duration) throw new Error('服务不存在')
const available = this.availableSlots[day] || []
const blocked = this.blockedTimes[day] || []
const allUnavailable = [...existingAppointments, ...blocked]
// 合并重叠的不可用时间段
allUnavailable.sort((a, b) => this.compareTime(a[0], b[0]))
const mergedUnavailable = []
for (const [start, end] of allUnavailable) {
if (mergedUnavailable.length === 0) {
mergedUnavailable.push([start, end])
} else {
const [lastStart, lastEnd] = mergedUnavailable[mergedUnavailable.length - 1]
if (this.compareTime(start, lastEnd) <= 0) {
const newEnd = this.compareTime(end, lastEnd) > 0 ? end : lastEnd
mergedUnavailable[mergedUnavailable.length - 1][1] = newEnd
} else {
mergedUnavailable.push([start, end])
}
}
}
// 计算最终可用时间段
const finalAvailable = []
for (const [workStart, workEnd] of available) {
let currentStart = workStart
for (const [busyStart, busyEnd] of mergedUnavailable) {
if (this.compareTime(busyEnd, currentStart) <= 0) continue
if (this.compareTime(busyStart, workEnd) >= 0) break
if (this.compareTime(currentStart, busyStart) < 0) {
const slotEnd = this.compareTime(busyStart, workEnd) < 0 ? busyStart : workEnd
finalAvailable.push([currentStart, slotEnd])
}
currentStart = this.compareTime(busyEnd, workEnd) < 0 ? busyEnd : workEnd
}
if (this.compareTime(currentStart, workEnd) < 0) {
finalAvailable.push([currentStart, workEnd])
}
}
// 返回足够时长的时段
return finalAvailable.filter(([start, end]) => {
return this.timeDiffInMinutes(start, end) >= duration
})
}
// 时间比较方法
compareTime(time1, time2) {
const [h1, m1] = time1.split(':').map(Number)
const [h2, m2] = time2.split(':').map(Number)
if (h1 !== h2) return h1 - h2
return m1 - m2
}
// 计算时间差(分钟)
timeDiffInMinutes(start, end) {
const [h1, m1] = start.split(':').map(Number)
const [h2, m2] = end.split(':').map(Number)
return (h2 * 60 + m2) - (h1 * 60 + m1)
}
}
//oss压缩图片
export function handleImgSize(url, maxSizekb = 20, minRatioo = 0.01) {
return new Promise((resolve, reject) => {
// 1. 处理URL移除已有的OSS处理参数避免重复叠加
const [originalUrl, existingParams] = url.split('?');
const baseUrl = originalUrl; // 原始图片地址(无处理参数)
// 2. 下载原始图片,获取真实体积
uni.downloadFile({
url: baseUrl,
success: (downloadRes) => {
const tempFilePath = downloadRes.tempFilePath;
if (!tempFilePath) {
const error = new Error("下载失败,未获取到临时路径");
console.error(error);
reject(error);
return;
}
// 3. 读取原始图片体积
uni.getFileInfo({
filePath: tempFilePath,
success: (fileInfoRes) => {
const fileSizeByte = fileInfoRes.size;
const fileSizeKB = fileSizeByte / 1024;
console.log(
`原始体积:${fileSizeKB.toFixed(2)} KB | 限制体积:${maxSizekb} KB`
);
// 4. 若体积已达标直接返回原URL保留原有参数
if (fileSizeKB <= maxSizekb) {
console.log("体积达标,无需压缩");
const finalUrl = existingParams ?
`${baseUrl}?${existingParams}` : baseUrl;
resolve(finalUrl);
return;
}
// 5. 核心修正:根据体积反推合理的尺寸压缩比例
// 原理:体积 ≈ (尺寸比例k)² × 原体积 → k ≈ √(目标体积/原体积)
const targetRatio = maxSizekb / fileSizeKB; // 目标体积占原体积的比例
let sizeRatio = Math.sqrt(targetRatio); // 推导的尺寸压缩比例k
// 6. 限制尺寸比例下限(避免压缩过小导致模糊,可根据需求调整)
const minSizeRatio = minRatioo; // 最小缩放到原尺寸的30%p_30
if (sizeRatio < minSizeRatio) {
sizeRatio = minSizeRatio;
console.warn(`压缩比例过低,强制限制为${minSizeRatio}(避免模糊)`);
}
// 7. 转换为OSS的p参数百分比取整
const percentage = Math.round(sizeRatio * 100);
console.log(`计算压缩参数:尺寸缩放到${percentage}%p_${percentage}`);
// 8. 拼接最终URL保留原有参数叠加压缩参数
const ossParam = `x-oss-process=image/resize,p_${percentage}`;
const finalUrl = existingParams ?
`${baseUrl}?${existingParams}&${ossParam}` // 已有参数时用&拼接
:
`${baseUrl}?${ossParam}`; // 无参数时用?拼接
resolve(finalUrl);
},
fail: (err) => {
console.error("获取文件信息失败:", err);
reject(err);
}
});
},
fail: (err) => {
console.error("下载图片失败:", err);
reject(err);
}
});
});
}
//uniappy压缩图片
// export function handleImgSizeUni(url, maxSizekb = 128, minRatioo = 0.01) {
// return new Promise((resolve, reject) => {
// uni.getSavedFileInfo({
// filePath: url, //仅做示例用,非真正的文件路径
// success: function(fileInfoRes) {
// console.log(11111111,fileInfoRes)
// const fileSizeByte = fileInfoRes.size;
// const fileSizeKB = fileSizeByte / 1024;
// console.log(`原始体积:${fileSizeKB.toFixed(2)} KB`);
// if (fileSizeKB <= maxSizekb) {
// console.log("体积达标,无需压缩");
// resolve(url);
// return;
// }
// // 5. 核心修正:根据体积反推合理的尺寸压缩比例
// // 原理:体积 ≈ (尺寸比例k)² × 原体积 → k ≈ √(目标体积/原体积)
// const targetRatio = maxSizekb / fileSizeKB; // 目标体积占原体积的比例
// let sizeRatio = Math.sqrt(targetRatio); // 推导的尺寸压缩比例k
// // 6. 限制尺寸比例下限(避免压缩过小导致模糊,可根据需求调整)
// const minSizeRatio = minRatioo; // 最小缩放到原尺寸的30%p_30
// if (sizeRatio < minSizeRatio) {
// sizeRatio = minSizeRatio;
// console.warn(`压缩比例过低,强制限制为${minSizeRatio}(避免模糊)`);
// }
// const percentage = (Math.floor(sizeRatio * 10000) / 100) + '%';
// console.log("percentage",percentage);
// uni.compressImage({
// src: url,
// quality: 74,
// width: percentage,
// height: percentage,
// success: res2 => {
// console.log(res2.tempFilePath)
// uni.getSavedFileInfo({
// filePath: res2.tempFilePath, //仅做示例用,非真正的文件路径
// success: function (res3) {
// const fileSizeByte2 = res3.size;
// const fileSizeKB2 = fileSizeByte2 / 1024;
// console.log(`现在体积:${fileSizeKB2.toFixed(2)} KB`);
// }
// });
// resolve(res2.tempFilePath);
// },
// fail: (err) => {
// console.error("图片压缩失败:", err);
// reject(err);
// }
// })
// },
// fail: (err) => {
// console.error("获取文件信息失败:", err);
// reject(err);
// }
// });
// });
// }
export function handleImgSizeUni(url, maxSizekb = 30, minWidth = 20, minHeight = 20) {
console.log(url)
return new Promise((resolve, reject) => {
// 1. 获取图片基本信息(大小、宽高)
Promise.all([
// 获取文件大小
new Promise((res, rej) => {
uni.getFileInfo({
filePath: url,
success: res,
fail: rej
});
}),
// 获取图片宽高
new Promise((res, rej) => {
uni.getImageInfo({
src: url,
success: res,
fail: rej
});
})
]).then(([fileInfo, imgInfo]) => {
console.log(`imgInfo`,imgInfo);
const fileSizeKB = fileInfo.size / 1024;
console.log(`原始体积:${fileSizeKB.toFixed(2)} KB原始尺寸${imgInfo.width}x${imgInfo.height}`);
// 2. 体积达标直接返回
if (fileSizeKB <= maxSizekb) {
console.log("体积达标,无需压缩");
resolve(url);
return;
}
// 3. 压缩参数初始化(优先质量调整)
let quality = 70; // 初始高质量
let { width: originW, height: originH } = imgInfo;
let scale = 1; // 尺寸缩放比例1为不缩放
// 4. 循环调整参数直到体积达标最多尝试5次避免无限循环
const maxAttempts = 10;
let attempts = 0;
let tempFilePath ="";
const compressLoop = () => {
attempts++;
if (attempts > maxAttempts) {
console.warn("达到最大尝试次数,返回当前压缩结果",tempFilePath);
resolve(tempFilePath);
return;
}
// 计算当前缩放后的尺寸(保持比例)
const currentW = Math.max(originW * scale, minWidth); // 不小于最小宽度
const currentH = Math.max(originH * scale, minHeight); // 不小于最小高度
// 执行压缩
uni.compressImage({
src: url,
quality, // 质量参数0-100越高越清晰
width: currentW, // 具体像素值,避免百分比误差
height: currentH,
success: (res) => {
// 检查压缩后体积
uni.getFileInfo({
filePath: res.tempFilePath,
success: (compressedFile) => {
const compressedKB = compressedFile.size / 1024;
console.log(`${attempts}次压缩:质量=${quality},尺寸=${currentW}x${currentH},体积=${compressedKB.toFixed(2)} KB`);
tempFilePath = res.tempFilePath
if (compressedKB <= maxSizekb) {
// 体积达标,返回结果
resolve(res.tempFilePath);
} else {
// 体积不达标,调整参数继续尝试
if (quality > 20) {
// 优先降质量每次降10
quality -= 15;
} else {
// 质量较低时再缩小尺寸每次缩放到80%
scale *= 0.8;
}
compressLoop();
}
},
fail: (err) => {
console.error("获取压缩后文件信息失败:", err);
reject(err);
}
});
},
fail: (err) => {
console.error("图片压缩失败:", err);
reject(err);
}
});
};
// 开始压缩循环
compressLoop();
}).catch((err) => {
console.error("获取图片信息失败:", err);
reject(err);
});
});
}