u
This commit is contained in:
1 parent
bcc4722efc
commit
f48536774f
403 files changed
+34776
No files matched your search
@@ -0,0 +1,16 @@
|
||||
export function toJson(obj: any): string {
|
||||
try {
|
||||
return JSON.stringify(obj);
|
||||
} catch (error) {
|
||||
console.error("JSON 转换失败", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
export function toObject<T>(json: string): T | null {
|
||||
try {
|
||||
return JSON.parse(json) as T;
|
||||
} catch (error) {
|
||||
console.error("JSON 解析失败", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,28 @@
|
||||
function legacyCopyText(text: string) {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.value = text;
|
||||
textarea.style.position = "fixed"; // 避免滚动跳转
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand("copy");
|
||||
document.body.removeChild(textarea);
|
||||
return successful;
|
||||
} catch (err) {
|
||||
console.error("传统复制失败:", err);
|
||||
document.body.removeChild(textarea);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
export function copyContent(text: string) {
|
||||
// 现代浏览器方案
|
||||
if (navigator.clipboard) {
|
||||
return navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => true)
|
||||
.catch(() => legacyCopyText(text));
|
||||
}
|
||||
// 传统方案
|
||||
return Promise.resolve(legacyCopyText(text));
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
export function formatChatTime(dateStr: string | undefined): string {
|
||||
if (!dateStr) return "";
|
||||
|
||||
const now = new Date();
|
||||
const inputDate = new Date(dateStr.replace(" ", "T"));
|
||||
|
||||
// [修复点] 计算自然日天数差(忽略时间部分)
|
||||
const nowMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const inputMidnight = new Date(inputDate.getFullYear(), inputDate.getMonth(), inputDate.getDate());
|
||||
const diffDays = Math.round((nowMidnight.getTime() - inputMidnight.getTime()) / (1000 * 3600 * 24));
|
||||
|
||||
// 获取时间信息(补零处理)
|
||||
const hour = String(inputDate.getHours()).padStart(2, "0");
|
||||
const minute = String(inputDate.getMinutes()).padStart(2, "0");
|
||||
const weekDays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||
|
||||
// [修复逻辑] 按自然日差判断
|
||||
if (diffDays === 0) return `${hour}:${minute}`;
|
||||
if (diffDays === 1) return "昨天";
|
||||
if (diffDays <= 7) return weekDays[inputDate.getDay()];
|
||||
|
||||
// 超过一周显示年月日
|
||||
return `${inputDate.getFullYear()}/${String(inputDate.getMonth() + 1).padStart(2, "0")}/${String(inputDate.getDate()).padStart(2, "0")}`;
|
||||
}
|
||||
export function getDatePart(dateTimeStr: string): string {
|
||||
if (!dateTimeStr) return "";
|
||||
return dateTimeStr.split(" ")[0];
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import { getAliyunKeyApi, insertFileApi } from "@/service/file";
|
||||
import { Message } from "@arco-design/web-vue";
|
||||
import { showUploadProgress, updateUploadProgress, closeUploadProgress } from "@/components/upload-progress/index";
|
||||
|
||||
export interface UploadImageResponse {
|
||||
name: string;
|
||||
size: string;
|
||||
url: string;
|
||||
blob: Blob;
|
||||
}
|
||||
|
||||
export const uploadImage = (
|
||||
options: {
|
||||
quality?: number;
|
||||
mimeType?: string;
|
||||
} = {}
|
||||
): Promise<UploadImageResponse> => {
|
||||
const { quality = 0.8, mimeType = "image/webp" } = options;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof document === "undefined") {
|
||||
reject(new Error("Require browser environment"));
|
||||
return;
|
||||
}
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "image/*";
|
||||
input.style.display = "none";
|
||||
|
||||
const processImage = (file: File): Promise<UploadImageResponse> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
const objectURL = URL.createObjectURL(file);
|
||||
|
||||
img.onload = () => {
|
||||
try {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) throw new Error("Canvas init failed");
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
URL.revokeObjectURL(objectURL);
|
||||
|
||||
canvas.toBlob(
|
||||
(blob) => {
|
||||
if (!blob) {
|
||||
reject(new Error("Compression failed"));
|
||||
return;
|
||||
}
|
||||
resolve({ name: file.name, size: blob.size.toString(), url: URL.createObjectURL(blob), blob: blob });
|
||||
},
|
||||
mimeType,
|
||||
quality
|
||||
);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(objectURL);
|
||||
reject(new Error("Image load failed"));
|
||||
};
|
||||
|
||||
img.src = objectURL;
|
||||
});
|
||||
};
|
||||
|
||||
input.onchange = async (event) => {
|
||||
const file = (event.target as HTMLInputElement).files?.[0];
|
||||
if (!file) {
|
||||
reject(new Error("No file selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!file.type.startsWith("image/")) {
|
||||
throw new Error("Invalid image file");
|
||||
}
|
||||
|
||||
const result = await processImage(file);
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
} finally {
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
};
|
||||
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
});
|
||||
};
|
||||
export interface UploadVideoResponse {
|
||||
name: string;
|
||||
size: string;
|
||||
url: string;
|
||||
blob: Blob;
|
||||
}
|
||||
|
||||
export const uploadVideo = (): Promise<UploadVideoResponse> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof document === "undefined") {
|
||||
reject(new Error("Require browser environment"));
|
||||
return;
|
||||
}
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "video/*";
|
||||
input.style.display = "none";
|
||||
|
||||
input.onchange = (event) => {
|
||||
const file = (event.target as HTMLInputElement).files?.[0];
|
||||
if (!file) {
|
||||
reject(new Error("No file selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.type.startsWith("video/")) {
|
||||
reject(new Error("Invalid video file"));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve({
|
||||
name: file.name,
|
||||
size: file.size.toString(),
|
||||
url: URL.createObjectURL(file),
|
||||
blob: file
|
||||
});
|
||||
|
||||
document.body.removeChild(input);
|
||||
};
|
||||
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
});
|
||||
};
|
||||
|
||||
export interface UploadToAliyunResponse {
|
||||
url: string;
|
||||
fileName: string;
|
||||
fileSize: string;
|
||||
}
|
||||
|
||||
export function uploadToAliyun(blob: Blob, fileName: string, fileSize: string, onProgress?: (percent: number) => void): Promise<UploadToAliyunResponse> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 在开始上传时显示上传进度
|
||||
showUploadProgress("uploading");
|
||||
|
||||
getAliyunKeyApi()
|
||||
.then(async (res) => {
|
||||
try {
|
||||
const aliyunKey = res.data;
|
||||
const formData = new FormData();
|
||||
|
||||
const fileExt = fileName.includes(".") ? fileName.slice(fileName.lastIndexOf(".")) : "";
|
||||
const uniqueName = `sl_${Date.now()}_${Math.random().toString(36).slice(2)}${fileExt}`;
|
||||
|
||||
formData.append("key", `${aliyunKey.dir}${uniqueName}`);
|
||||
formData.append("OSSAccessKeyId", aliyunKey.accessid);
|
||||
formData.append("policy", aliyunKey.policy);
|
||||
formData.append("Signature", aliyunKey.signature);
|
||||
formData.append("file", blob, uniqueName);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", aliyunKey.host, true);
|
||||
|
||||
// 监听上传进度
|
||||
xhr.upload.onprogress = (event) => {
|
||||
if (event.lengthComputable && onProgress) {
|
||||
const percent = (event.loaded / event.total) * 100;
|
||||
onProgress(parseFloat(percent.toFixed(2))); // 保留两位小数
|
||||
updateUploadProgress(percent); // 更新进度条
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onload = async () => {
|
||||
if (xhr.status === 204 || xhr.status === 200) {
|
||||
const allFilePath = `${aliyunKey.host}/${aliyunKey.dir}${uniqueName}`;
|
||||
|
||||
await insertFileApi({
|
||||
fileName,
|
||||
fileSize,
|
||||
fileMapName: allFilePath
|
||||
});
|
||||
|
||||
updateUploadProgress(100); // 上传完毕
|
||||
Message.success("上传成功");
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress(); // 关闭上传进度条
|
||||
}, 500);
|
||||
|
||||
resolve({
|
||||
url: allFilePath,
|
||||
fileName,
|
||||
fileSize
|
||||
});
|
||||
} else {
|
||||
updateUploadProgress(0, "failed"); // 上传失败
|
||||
Message.error("上传失败,请稍后再试");
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress(); // 关闭上传进度条
|
||||
}, 500);
|
||||
|
||||
reject(new Error(`OSS上传失败: ${xhr.responseText}`));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
updateUploadProgress(0, "failed"); // 上传失败
|
||||
Message.error("上传失败,请稍后再试");
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress(); // 关闭上传进度条
|
||||
}, 500);
|
||||
reject(new Error(`OSS上传异常: ${xhr.responseText}`));
|
||||
};
|
||||
|
||||
xhr.send(formData);
|
||||
} catch (error) {
|
||||
updateUploadProgress(0, "failed"); // 上传失败
|
||||
Message.error("上传失败,请稍后再试");
|
||||
|
||||
setTimeout(() => {
|
||||
closeUploadProgress(); // 关闭上传进度条
|
||||
}, 500);
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import axiosInstance from "@/interceptors/http";
|
||||
|
||||
type Response<T = unknown> = {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T;
|
||||
};
|
||||
|
||||
export function httpGet<T>(url: string, params?: Record<string, any>): Promise<Response<T>> {
|
||||
return axiosInstance.get(url, { params });
|
||||
}
|
||||
|
||||
export function httpPostJson<T>(url: string, data?: Record<string, any>): Promise<Response<T>> {
|
||||
return axiosInstance.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function httpPostForm<T>(url: string, data?: any): Promise<Response<T>> {
|
||||
return axiosInstance.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { RouteLocationRaw } from "vue-router";
|
||||
import { useRouterStore } from "@/store/router";
|
||||
|
||||
export const navigateTo = (params: RouteLocationRaw) => {
|
||||
const routerStore = useRouterStore();
|
||||
routerStore.toPath(params);
|
||||
};
|
||||
Reference in new issue
Block a user