feat: 龙里小程序第一次上传
This commit is contained in:
commit
f23d598328
59 files changed
+11482
No files matched your search
@@ -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,
|
||||
};
|
||||
@@ -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] });
|
||||
};
|
||||
Reference in new issue
Block a user