Files
console-dev/app/lib/cart-edit.js
T

118 lines
3.8 KiB
JavaScript

/** 购物车编辑的数据快照、并发校验与选中项恢复;所有比较以规范化的可提交字段为准。 */
// 合并原商品和编辑值,剔除展示字段,统一数量和可选配置的默认值。
function e(e, t = {}) {
let n = {
...e,
...t,
};
return {
product_id: Number(n.product_id),
config_options: n.config_options ?? {},
qty: Math.max(1, Number(n.qty ?? 1)),
customfield: n.customfield ?? {},
self_defined_field: n.self_defined_field ?? {},
};
}
// 隔离编辑页和列表页的可变对象;只用于接口提供的 JSON 数据。
function t(e) {
return JSON.parse(JSON.stringify(e));
}
function n(n) {
return n.map((n) => t(e(n)));
}
// 递归排序对象键并省略 undefined,使相同内容不受属性插入顺序影响。
function r(e) {
return Array.isArray(e)
? e.map(r)
: e && typeof e == `object`
? Object.fromEntries(
Object.entries(e)
.filter(([, e]) => e !== void 0)
.sort(([e], [t]) => e.localeCompare(t))
.map(([e, t]) => [e, r(t)]),
)
: e;
}
function i(t) {
return JSON.stringify(r(e(t)));
}
// 编辑期间后端若删除了已保存的自定义字段,不能静默丢字段继续保存。
function a(e, t) {
if (!e) return;
let n = e.item.self_defined_field ?? {};
if (Object.keys(n).some((e) => !t.includes(e)))
throw Error(`商品自定义字段已调整,请返回购物车删除该项后重新配置`);
}
// 选中态只在内存中跨购物车/编辑页传递,不把商品配置长期写入浏览器存储。
var o = null;
async function s(e) {
let t = new TextEncoder().encode(i(e));
return Array.from(
new Uint8Array(await crypto.subtle.digest(`SHA-256`, t)),
(e) => e.toString(16).padStart(2, `0`),
).join(``);
}
/** 记录离开购物车前各项指纹与选中位置;SHA-256 用于内容匹配,不承担身份认证。 */
async function c(e, t) {
let n = [...t];
o = {
fingerprints: await Promise.all(e.map(s)),
indices: n,
};
}
/** 一次性消费选中态;相同商品按其重复出现次序匹配,避免全部映射到同一位置。 */
async function l(e) {
let t = o;
if (!t) return null;
o = null;
let n = await Promise.all(e.map(s)),
r = new Set();
return t.indices.flatMap((e) => {
let i = t.fingerprints
.slice(0, e)
.filter((n) => n === t.fingerprints[e]).length,
a = n.flatMap((n, r) => (n === t.fingerprints[e] ? [r] : []))[i];
return a === void 0 || r.has(a) ? [] : (r.add(a), [a]);
});
}
/**
* 返回编辑保存函数:n 为 API 客户端,r 包含 position/item,u 回调检查报价是否仍有效。
* 保存前重取购物车并比较原始项,提交 expected_item 供后端进一步检测并发变更。
*/
function u(n, r) {
let a = r ? t(r.item) : null,
c = !1;
return async (l, u) => {
if (!r || !a) throw Error(`购物车编辑上下文缺失`);
if (c) throw Error(`正在保存,请勿重复操作`);
c = !0;
let d = t(e(a, l));
try {
let c = (await n.get(`/cart`)).list?.[r.position];
if (!c || i(c) !== i(a))
throw Error(`购物车商品已变化,请返回购物车刷新后重新编辑`);
if (!u()) throw Error(`配置已改变,请重新确认报价后保存`);
let l = await n.put(`/cart/${r.position}`, {
...d,
expected_item: e(a),
}),
f = a;
a = t({
...a,
...d,
});
// 成功后同步指纹,返回购物车时仍能识别刚编辑过的选中商品。
let p = o;
return (
p &&
p.fingerprints[r.position] === (await s(f)) &&
(p.fingerprints[r.position] = await s(a)),
l
);
} finally {
c = !1;
}
};
}
export { l as a, c as i, t as n, a as o, u as r, n as t };