2026-05-07 11:05:30 +08:00
|
|
|
|
import { getDeviceInfo } from "@/utils/system";
|
|
|
|
|
|
import {
|
|
|
|
|
|
saveRecord,
|
|
|
|
|
|
viewRecord,
|
|
|
|
|
|
createShareToken,
|
|
|
|
|
|
createTracking,
|
|
|
|
|
|
} from "@/api/system";
|
|
|
|
|
|
|
|
|
|
|
|
export const generateObjectId = (
|
|
|
|
|
|
m = Math,
|
|
|
|
|
|
d = Date,
|
|
|
|
|
|
h = 16,
|
|
|
|
|
|
s = (s) => m.floor(s).toString(h),
|
|
|
|
|
|
) => s(d.now() / 1000) + " ".repeat(h).replace(/./g, () => s(m.random() * h));
|
|
|
|
|
|
|
|
|
|
|
|
export const uploadImage = (filePath) => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
uni.uploadFile({
|
|
|
|
|
|
url: "https://api.ai-meng.com/api/common/upload",
|
|
|
|
|
|
filePath: filePath,
|
|
|
|
|
|
name: "file",
|
|
|
|
|
|
header: {
|
|
|
|
|
|
"x-app-id": "69665538a49b8ae3be50fe5d",
|
|
|
|
|
|
},
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.statusCode < 400) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const keyJson = JSON.parse(res.data);
|
|
|
|
|
|
if (keyJson?.data?.result === "auditFailed") {
|
|
|
|
|
|
reject("图片不符合发布规范,请稍作修改后再试");
|
|
|
|
|
|
} else {
|
2026-05-20 23:28:57 +08:00
|
|
|
|
resolve(keyJson?.data.key);
|
2026-05-07 11:05:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
reject(e);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(new Error("Upload failed"));
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-20 23:32:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 统一的选择并上传图片的方法
|
|
|
|
|
|
* @param {Object} options 配置项
|
|
|
|
|
|
* @param {number} options.count 最多可选择的图片数量,默认为 1
|
|
|
|
|
|
* @param {string} options.loadingTitle 上传中提示文案,默认为 '上传中...'
|
|
|
|
|
|
* @returns {Promise<string[]>} 返回一个包含所有上传成功图片 URL 的数组 Promise
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const chooseAndUploadImage = (options = {}) => {
|
|
|
|
|
|
const { count = 1, loadingTitle = '上传中...' } = options;
|
|
|
|
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
uni.chooseImage({
|
|
|
|
|
|
count,
|
|
|
|
|
|
success: async (res) => {
|
|
|
|
|
|
const filePaths = res.tempFilePaths;
|
|
|
|
|
|
if (!filePaths || filePaths.length === 0) {
|
|
|
|
|
|
return resolve([]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uni.showLoading({ title: loadingTitle });
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 并发上传所有选中的图片
|
|
|
|
|
|
const uploadPromises = filePaths.map(path => uploadImage(path));
|
|
|
|
|
|
const urls = await Promise.all(uploadPromises);
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
resolve(urls);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "上传失败",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
console.error("图片上传异常:", error);
|
|
|
|
|
|
reject(error);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
// 用户取消选择不作为异常抛出,返回空数组
|
|
|
|
|
|
if (err.errMsg && err.errMsg.indexOf('cancel') !== -1) {
|
|
|
|
|
|
resolve([]);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-07 11:05:30 +08:00
|
|
|
|
export const saveRecordRequest = async (path, targetId, scene, imageUrl) => {
|
|
|
|
|
|
if (!imageUrl) {
|
|
|
|
|
|
imageUrl = await uploadImage(path);
|
|
|
|
|
|
}
|
|
|
|
|
|
const deviceInfo = getDeviceInfo();
|
|
|
|
|
|
saveRecord({
|
|
|
|
|
|
scene,
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
imageUrl,
|
|
|
|
|
|
deviceInfo,
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const saveViewRequest = async (shareToken, scene, targetId = "") => {
|
|
|
|
|
|
const deviceInfo = getDeviceInfo();
|
|
|
|
|
|
viewRecord({
|
|
|
|
|
|
shareToken,
|
|
|
|
|
|
scene,
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
deviceInfo,
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const saveRemoteImageToLocal = (imageUrl) => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
uni.downloadFile({
|
|
|
|
|
|
url: imageUrl,
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.statusCode === 200) {
|
|
|
|
|
|
uni.saveImageToPhotosAlbum({
|
|
|
|
|
|
filePath: res.tempFilePath,
|
|
|
|
|
|
success: () => {
|
|
|
|
|
|
resolve(true);
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(
|
|
|
|
|
|
new Error("Download failed with status code: " + res.statusCode),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getShareToken = async (scene, targetId = "") => {
|
|
|
|
|
|
const deviceInfo = getDeviceInfo();
|
|
|
|
|
|
const shareTokenRes = await createShareToken({
|
|
|
|
|
|
scene,
|
|
|
|
|
|
targetId,
|
|
|
|
|
|
...deviceInfo,
|
|
|
|
|
|
});
|
|
|
|
|
|
return shareTokenRes?.shareToken || "";
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-09 23:55:11 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 记录用户行为事件
|
|
|
|
|
|
* @param {Object} event 事件对象,包含事件名称和参数
|
|
|
|
|
|
* eventName 事件名称,例如 'switchCategory'
|
|
|
|
|
|
* eventType 事件类型,例如 'click',可选
|
|
|
|
|
|
* elementId 事件元素 ID,例如 'category_123',可选
|
|
|
|
|
|
* elementContent 事件元素内容,例如 '分类1',可选
|
|
|
|
|
|
* customParams 自定义参数,例如 { categoryId: 123 },可选
|
|
|
|
|
|
*/
|
2026-05-07 11:05:30 +08:00
|
|
|
|
export const trackRecord = (event) => {
|
|
|
|
|
|
const pages = getCurrentPages();
|
|
|
|
|
|
// 核心修复:在小程序刚启动或页面尚未压栈时,pages 可能为空数组
|
|
|
|
|
|
const currentPage = pages && pages.length > 0 ? pages[pages.length - 1] : null;
|
|
|
|
|
|
const route = currentPage ? currentPage.route : "app_launch";
|
|
|
|
|
|
|
|
|
|
|
|
const deviceInfo = getDeviceInfo();
|
|
|
|
|
|
createTracking({
|
|
|
|
|
|
...event,
|
|
|
|
|
|
page: route,
|
|
|
|
|
|
deviceInfo,
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const getThumbUrl = (url) => {
|
|
|
|
|
|
if (!url) return "";
|
|
|
|
|
|
return `${url}?imageView2/1/w/200/h/200/q/80`;
|
|
|
|
|
|
};
|