2025-11-24 22:37:11 +08:00

294 lines
10 KiB
JavaScript

"use strict";
const common_vendor = require("../../common/vendor.js");
const utils_request = require("../../utils/request.js");
const CACHE_KEY = "products_cache_v1";
const TTL_MS = 10 * 60 * 1e3;
const _sfc_main = {
__name: "index",
setup(__props) {
const products = common_vendor.ref([]);
const columns = common_vendor.ref([[], []]);
const colHeights = common_vendor.ref([0, 0]);
const loading = common_vendor.ref(false);
const keyword = common_vendor.ref("");
const minPrice = common_vendor.ref("");
const maxPrice = common_vendor.ref("");
const displayCount = common_vendor.computed(() => columns.value[0].length + columns.value[1].length);
const loadedMap = common_vendor.ref({});
function getKey(p) {
return String((p && p.id) ?? "") + "|" + String((p && p.image) ?? "");
}
function unwrap(list) {
if (Array.isArray(list))
return list;
const obj = list || {};
const data = obj.data || {};
const arr = obj.list || obj.items || data.list || data.items || data;
return Array.isArray(arr) ? arr : [];
}
function cleanUrl(u) {
const s = String(u || "").trim();
const m = s.match(/https?:\/\/[^\s'"`]+/);
if (m && m[0])
return m[0];
return s.replace(/[`'\"]/g, "").trim();
}
function apiGet(url, data = {}) {
const token = common_vendor.index.getStorageSync("token");
const fn = token ? utils_request.authRequest : utils_request.request;
return fn({ url, method: "GET", data });
}
function getCachedProducts() {
try {
const obj = common_vendor.index.getStorageSync(CACHE_KEY);
if (obj && Array.isArray(obj.data) && typeof obj.ts === "number") {
const fresh = Date.now() - obj.ts < TTL_MS;
if (fresh)
return obj.data;
}
} catch (_) {
}
return null;
}
function setCachedProducts(list) {
try {
common_vendor.index.setStorageSync(CACHE_KEY, { data: Array.isArray(list) ? list : [], ts: Date.now() });
} catch (_) {
}
}
function estimateHeight(p) {
const base = 220;
const len = String(p.title || "").length;
const lines = Math.min(2, Math.ceil(len / 12));
const titleH = lines * 36;
const stockH = p.stock !== null && p.stock !== void 0 ? 34 : 0;
const padding = 28;
return base + titleH + stockH + padding;
}
function distributeToColumns(list) {
const arr = Array.isArray(list) ? list : [];
const cols = Array.from({ length: 2 }, () => []);
const hs = [0, 0];
for (let i = 0; i < arr.length; i++) {
const h = estimateHeight(arr[i]);
const idx = hs[0] <= hs[1] ? 0 : 1;
cols[idx].push(arr[i]);
hs[idx] += h;
}
columns.value = cols;
colHeights.value = hs;
const presentKeys = new Set(arr.map(getKey));
const next = {};
const prev = loadedMap.value || {};
for (const k in prev) {
if (presentKeys.has(k))
next[k] = prev[k];
}
loadedMap.value = next;
}
function extractListAndTotal(payload) {
if (Array.isArray(payload))
return { list: payload, total: payload.length };
const obj = payload || {};
const data = obj.data || {};
const list = obj.list || obj.items || data.list || data.items || [];
const totalRaw = obj.total ?? data.total;
const total = typeof totalRaw === "number" ? totalRaw : Array.isArray(list) ? list.length : 0;
return { list: Array.isArray(list) ? list : [], total };
}
function normalizeProducts(list) {
const arr = unwrap(list);
return arr.map((i, idx) => ({
id: i.id ?? i.productId ?? i._id ?? i.sku_id ?? String(idx),
image: cleanUrl(i.main_image ?? i.imageUrl ?? i.image_url ?? i.image ?? i.img ?? i.pic ?? ""),
title: i.title ?? i.name ?? i.product_name ?? i.sku_name ?? "",
price: i.price_sale ?? i.price ?? i.price_min ?? i.amount ?? null,
points: i.points_required ?? i.points ?? i.integral ?? null,
stock: i.stock ?? i.inventory ?? i.quantity ?? null,
link: cleanUrl(i.linkUrl ?? i.link_url ?? i.link ?? i.url ?? "")
})).filter((i) => i.image || i.title);
}
function onProductTap(p) {
const imgs = (Array.isArray(products.value) ? products.value : []).map((x) => x.image).filter(Boolean);
const current = p && p.image;
if (current) {
common_vendor.index.previewImage({ urls: imgs.length ? imgs : [current], current });
return;
}
if (p.link && /^\/.+/.test(p.link)) {
common_vendor.index.navigateTo({ url: p.link });
}
}
function applyFilters() {
const k = String(keyword.value || "").trim().toLowerCase();
const min = Number(minPrice.value);
const max = Number(maxPrice.value);
const hasMin = !isNaN(min) && String(minPrice.value).trim() !== "";
const hasMax = !isNaN(max) && String(maxPrice.value).trim() !== "";
const list = Array.isArray(products.value) ? products.value : [];
const filtered = list.filter((p) => {
const title = String(p.title || "").toLowerCase();
if (k && !title.includes(k))
return false;
const priceNum = typeof p.price === "number" ? p.price : Number(p.price);
if (hasMin) {
if (isNaN(priceNum))
return false;
if (priceNum < min)
return false;
}
if (hasMax) {
if (isNaN(priceNum))
return false;
if (priceNum > max)
return false;
}
return true;
});
distributeToColumns(filtered);
}
function onSearchConfirm() {
applyFilters();
}
function onApplyFilters() {
applyFilters();
}
async function loadProducts() {
try {
const cached = getCachedProducts();
if (cached) {
products.value = cached;
distributeToColumns(cached);
return;
}
const first = await apiGet("/api/app/products", { page: 1 });
const { list: firstList, total } = extractListAndTotal(first);
const pageSize = 20;
const totalPages = Math.max(1, Math.ceil((typeof total === "number" ? total : 0) / pageSize));
if (totalPages <= 1) {
const normalized2 = normalizeProducts(firstList);
products.value = normalized2;
distributeToColumns(normalized2);
setCachedProducts(normalized2);
return;
}
const tasks = [];
for (let p = 2; p <= totalPages; p++) {
tasks.push(apiGet("/api/app/products", { page: p }));
}
const results = await Promise.allSettled(tasks);
const restLists = results.map((r) => {
if (r.status === "fulfilled") {
const { list } = extractListAndTotal(r.value);
return Array.isArray(list) ? list : [];
}
return [];
});
const merged = [firstList, ...restLists].flat();
const normalized = normalizeProducts(merged);
products.value = normalized;
distributeToColumns(normalized);
setCachedProducts(normalized);
} catch (e) {
products.value = [];
columns.value = [[], []];
colHeights.value = [0, 0];
const presentKeys = /* @__PURE__ */ new Set([]);
const next = {};
const prev = loadedMap.value || {};
for (const k in prev) {
if (presentKeys.has(k))
next[k] = prev[k];
}
loadedMap.value = next;
}
}
function isLoaded(p) {
return !!(loadedMap.value && loadedMap.value[getKey(p)]);
}
function onImageLoad(p) {
const k = getKey(p);
if (!k)
return;
loadedMap.value = { ...loadedMap.value || {}, [k]: true };
}
function onImageError(p) {
const k = getKey(p);
if (!k)
return;
const prev = { ...loadedMap.value || {} };
delete prev[k];
loadedMap.value = prev;
}
common_vendor.onShow(async () => {
const token = common_vendor.index.getStorageSync("token");
const phoneBound = !!common_vendor.index.getStorageSync("phone_bound");
if (!token || !phoneBound) {
common_vendor.index.showModal({
title: "提示",
content: "请先登录并绑定手机号",
confirmText: "去登录",
success: (res) => {
if (res.confirm) {
common_vendor.index.navigateTo({ url: "/pages/login/index" });
}
}
});
return;
}
loading.value = true;
await loadProducts();
loading.value = false;
});
return (_ctx, _cache) => {
return common_vendor.e({
a: loading.value
}, loading.value ? {} : common_vendor.e({
b: common_vendor.o(onSearchConfirm),
c: keyword.value,
d: common_vendor.o(($event) => keyword.value = $event.detail.value),
e: minPrice.value,
f: common_vendor.o(($event) => minPrice.value = $event.detail.value),
g: maxPrice.value,
h: common_vendor.o(($event) => maxPrice.value = $event.detail.value),
i: common_vendor.o(onApplyFilters),
j: displayCount.value
}, displayCount.value ? {
k: common_vendor.f(columns.value, (col, ci, i0) => {
return {
a: common_vendor.f(col, (p, k1, i1) => {
return common_vendor.e({
a: isLoaded(p) ? 1 : "",
b: p.image,
c: common_vendor.o(($event) => onImageLoad(p), p.id),
d: common_vendor.o(($event) => onImageError(p), p.id),
e: !isLoaded(p)
}, !isLoaded(p) ? {} : {}, {
f: p.price !== null
}, p.price !== null ? {
g: common_vendor.t(p.price)
} : {}, {
h: p.points !== null
}, p.points !== null ? {
i: common_vendor.t(p.points)
} : {}, {
j: common_vendor.t(p.title),
k: p.stock !== null
}, p.stock !== null ? {
l: common_vendor.t(p.stock)
} : {}, {
m: p.id,
n: common_vendor.o(($event) => onProductTap(p), p.id)
});
}),
b: ci
};
})
} : {}));
};
}
};
const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["__scopeId", "data-v-7db6cc15"]]);
wx.createPage(MiniProgramPage);
//# sourceMappingURL=../../../.sourcemap/mp-weixin/pages/shop/index.js.map