feat: 龙里小程序第一次上传

This commit is contained in:
zoujing committed 2026-09-16 16:52:50 +08:00
commit f23d598328
59 files changed
+11482

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
/**
* 通用工具方法
*/
/** 深拷贝(mock 数据隔离,避免页面改坏源数据) */
export const clone = (val) => {
if (val === null || val === undefined) return val;
try {
return JSON.parse(JSON.stringify(val));
} catch (e) {
return val;
}
};
/** 去重 */
export const unique = (list) => list.filter((v, i, arr) => arr.indexOf(v) === i);
/**
* 条目图集:只读数据字段,禁止拼接路径。
* 主图 + 图集 URL 去重后返回。
*/
export const imageListOf = (item) => {
if (!item) return [];
const urls = [item.main_image_url, ...(item.gallery_urls || [])];
return unique(urls.filter((u) => typeof u === "string" && u.trim()));
};
/**
* 折扣率(前端计算,不落库)
* deal / original → 7.9 折;original 缺失或 ≥ 10 折时不展示
*/
export const discountLabel = (deal, original) => {
const d = Number(deal);
const o = Number(original);
if (!d || !o || o <= 0) return "";
const rate = Math.round((d / o) * 100) / 10;
if (!rate || rate >= 10) return "";
return `${rate.toFixed(1)} 折`;
};
/** 价格显示(整数元,去掉多余小数) */
export const priceText = (val) => {
if (val === null || val === undefined || val === "") return "";
const n = Number(val);
if (Number.isNaN(n)) return "";
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
};
/** 手机号中间四位打码 */
export const maskPhone = (phone) => {
const p = String(phone || "").replace(/\D/g, "");
if (p.length < 7) return phone || "";
return `${p.slice(0, 3)}****${p.slice(-4)}`;
};
/** 2026-09-12 → 2026.09.12 */
export const dotDate = (str) => String(str || "").replace(/-/g, ".");
/** 有效期区间文案:2026.09.12 - 09.18 */
export const dateRangeText = (from, to) => {
if (!from && !to) return "";
if (!from) return dotDate(to);
if (!to) return dotDate(from);
const f = dotDate(from);
const t = dotDate(to);
const sameMonth = f.slice(0, 7) === t.slice(0, 7);
return sameMonth ? `${f} - ${t.slice(-5)}` : `${f} - ${t}`;
};
/** 距今天数(正数=还剩 N 天) */
export const daysFromNow = (dateStr) => {
if (!dateStr) return null;
const target = new Date(`${String(dateStr).replace(/\./g, "-")}T23:59:59`);
if (Number.isNaN(target.getTime())) return null;
return Math.ceil((target.getTime() - Date.now()) / 86400000);
};
/** 简易节流 */
export const throttle = (fn, wait = 500) => {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last < wait) return;
last = now;
return fn.apply(this, args);
};
};
export default {
clone,
unique,
imageListOf,
discountLabel,
priceText,
maskPhone,
dotDate,
dateRangeText,
daysFromNow,
throttle,
};
+122
View File
@@ -0,0 +1,122 @@
/**
* 导航工具:登录回跳、Tab 切换、返回兜底
* 本项目未使用原生 tabBar(Tab 栏为自绘组件),因此 Tab 之间用 redirectTo 切换。
*/
import { ROUTES, STORAGE_KEYS } from "@/constant";
/** 当前页面路径(含参数) */
export const currentRoute = () => {
const pages = getCurrentPages();
if (!pages.length) return "";
const page = pages[pages.length - 1];
const query = page?.options
? Object.entries(page.options)
.filter(([, v]) => v !== undefined && v !== "")
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
.join("&")
: "";
return `/${page.route}${query ? `?${query}` : ""}`;
};
/** 是否已登录,未登录则跳登录页并带上 redirect */
export const requireLogin = (silent = false) => {
const token = uni.getStorageSync(STORAGE_KEYS.ACCESS_TOKEN) || "";
if (token) return true;
if (!silent) {
uni.showToast({ title: "请先登录", icon: "none" });
}
goLogin();
return false;
};
/** 跳登录页,redirect 为登录后的回跳地址 */
export const goLogin = (redirect) => {
const target = redirect || currentRoute();
const url = `${ROUTES.LOGIN}${target ? `?redirect=${encodeURIComponent(target)}` : ""}`;
uni.navigateTo({ url });
};
/** 登录成功后的回跳 */
export const goAfterLogin = (redirect) => {
if (redirect) {
const url = decodeURIComponent(redirect);
const pages = getCurrentPages();
const top = pages.length ? `/${pages[pages.length - 1].route}` : "";
if (top && url.startsWith(top)) {
uni.navigateBack();
return;
}
uni.redirectTo({
url,
fail: () => uni.reLaunch({ url: ROUTES.HOME }),
});
return;
}
uni.navigateBack({
fail: () => uni.reLaunch({ url: ROUTES.HOME }),
});
};
/** 返回上一层,无栈时回首页 */
export const goBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.reLaunch({ url: ROUTES.HOME });
}
};
/** Tab 切换 */
export const switchTabTo = (key, extraQuery = "") => {
const map = {
home: ROUTES.HOME,
benefit: ROUTES.BENEFIT,
mine: ROUTES.MINE,
};
const url = `${map[key] || ROUTES.HOME}${extraQuery}`;
const pages = getCurrentPages();
const top = pages.length ? `/${pages[pages.length - 1].route}` : "";
if (top === url.split("?")[0]) return;
uni.redirectTo({ url });
};
/** 跳板块列表页 */
export const goCatalog = (sectionKey) => {
uni.navigateTo({ url: `${ROUTES.CATALOG}?section=${sectionKey}` });
};
/** 跳内容详情页 */
export const goDetail = (collection, id) => {
uni.navigateTo({ url: `${ROUTES.DETAIL}?collection=${collection}&id=${id}` });
};
/** 跳协议页 type: privacy | terms */
export const goAgreement = (type) => {
uni.navigateTo({ url: `${ROUTES.AGREEMENT}?type=${type}` });
};
export const goAbout = () => uni.navigateTo({ url: ROUTES.ABOUT });
export const goAccountCancel = () => uni.navigateTo({ url: ROUTES.ACCOUNT_CANCEL });
/** 拨打电话 */
export const makeCall = (phone) => {
const number = String(phone || "").replace(/\D/g, "");
if (!number) return;
uni.makePhoneCall({ phoneNumber: number }).catch(() => {});
};
/** 复制文本 */
export const copyText = (text, tip = "已复制") => {
if (!text) return;
uni.setClipboardData({
data: String(text),
success: () => uni.showToast({ title: tip, icon: "none" }),
});
};
/** 看图 */
export const previewImages = (urls, current) => {
if (!urls || !urls.length) return;
uni.previewImage({ urls, current: current || urls[0] });
};