294 lines
10 KiB
JavaScript
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
|