@pisell/pisellos 2.1.38 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.ts +8 -1
- package/dist/core/index.js +116 -42
- package/dist/effects/index.d.ts +1 -0
- package/dist/effects/index.js +29 -6
- package/dist/modules/Account/index.js +2 -3
- package/dist/modules/BaseModule.d.ts +3 -0
- package/dist/modules/BaseModule.js +15 -0
- package/dist/modules/Customer/index.js +9 -10
- package/dist/modules/Customer/types.d.ts +2 -2
- package/dist/modules/Customer/types.js +2 -2
- package/dist/modules/Discount/index.js +1 -1
- package/dist/modules/Guests/index.js +9 -9
- package/dist/modules/Order/index.js +1 -1
- package/dist/modules/Payment/index.js +63 -73
- package/dist/modules/Payment/walletpass.js +4 -1
- package/dist/modules/Product/index.d.ts +1 -1
- package/dist/modules/Product/types.d.ts +19 -0
- package/dist/modules/ProductList/index.js +5 -14
- package/dist/modules/Resource/index.js +1 -1
- package/dist/modules/Rules/index.js +2 -3
- package/dist/modules/Schedule/types.d.ts +2 -0
- package/dist/plugins/request.d.ts +1 -0
- package/dist/server/index.d.ts +152 -0
- package/dist/server/index.js +946 -0
- package/dist/server/modules/index.d.ts +16 -0
- package/dist/server/modules/index.js +21 -0
- package/dist/server/modules/menu/index.d.ts +63 -0
- package/dist/server/modules/menu/index.js +476 -0
- package/dist/server/modules/menu/types.d.ts +68 -0
- package/dist/server/modules/menu/types.js +16 -0
- package/dist/server/modules/products/index.d.ts +141 -0
- package/dist/server/modules/products/index.js +768 -0
- package/dist/server/modules/products/types.d.ts +94 -0
- package/dist/server/modules/products/types.js +43 -0
- package/dist/server/modules/quotation/index.d.ts +47 -0
- package/dist/server/modules/quotation/index.js +367 -0
- package/dist/server/modules/quotation/types.d.ts +50 -0
- package/dist/server/modules/quotation/types.js +20 -0
- package/dist/server/modules/schedule/index.d.ts +62 -0
- package/dist/server/modules/schedule/index.js +431 -0
- package/dist/server/modules/schedule/types.d.ts +1 -0
- package/dist/server/modules/schedule/types.js +2 -0
- package/dist/server/modules/schedule/utils.d.ts +32 -0
- package/dist/server/modules/schedule/utils.js +747 -0
- package/dist/server/types.d.ts +64 -0
- package/dist/server/types.js +1 -0
- package/dist/server/utils/index.d.ts +5 -0
- package/dist/server/utils/index.js +6 -0
- package/dist/server/utils/product.d.ts +18 -0
- package/dist/server/utils/product.js +339 -0
- package/dist/server/utils/schedule.d.ts +14 -0
- package/dist/server/utils/schedule.js +108 -0
- package/dist/server/utils/time.d.ts +18 -0
- package/dist/server/utils/time.js +53 -0
- package/dist/solution/BookingByStep/index.d.ts +1 -17
- package/dist/solution/BookingByStep/index.js +23 -468
- package/dist/solution/BookingByStep/utils/capacity.d.ts +2 -7
- package/dist/solution/BookingByStep/utils/capacity.js +8 -24
- package/dist/solution/BookingTicket/index.d.ts +12 -0
- package/dist/solution/BookingTicket/index.js +122 -79
- package/dist/solution/BookingTicket/utils/scan/index.d.ts +4 -0
- package/dist/solution/BookingTicket/utils/scan/index.js +25 -16
- package/dist/solution/BuyTickets/index.js +7 -8
- package/dist/solution/Checkout/index.d.ts +1 -46
- package/dist/solution/Checkout/index.js +530 -850
- package/dist/solution/ShopDiscount/index.js +9 -10
- package/dist/types/index.d.ts +27 -0
- package/lib/core/index.d.ts +8 -1
- package/lib/core/index.js +48 -1
- package/lib/effects/index.d.ts +1 -0
- package/lib/effects/index.js +13 -0
- package/lib/modules/Account/index.js +2 -3
- package/lib/modules/BaseModule.d.ts +3 -0
- package/lib/modules/BaseModule.js +9 -0
- package/lib/modules/Customer/index.js +9 -10
- package/lib/modules/Customer/types.d.ts +2 -2
- package/lib/modules/Customer/types.js +2 -2
- package/lib/modules/Discount/index.js +1 -1
- package/lib/modules/Guests/index.js +9 -9
- package/lib/modules/Order/index.js +1 -1
- package/lib/modules/Payment/index.js +56 -43
- package/lib/modules/Payment/walletpass.js +3 -1
- package/lib/modules/Product/index.d.ts +1 -1
- package/lib/modules/Product/types.d.ts +19 -0
- package/lib/modules/ProductList/index.js +4 -13
- package/lib/modules/Resource/index.js +1 -1
- package/lib/modules/Rules/index.js +2 -3
- package/lib/modules/Schedule/types.d.ts +2 -0
- package/lib/plugins/request.d.ts +1 -0
- package/lib/server/index.d.ts +152 -0
- package/lib/server/index.js +555 -0
- package/lib/server/modules/index.d.ts +16 -0
- package/lib/server/modules/index.js +47 -0
- package/lib/server/modules/menu/index.d.ts +63 -0
- package/lib/server/modules/menu/index.js +234 -0
- package/lib/server/modules/menu/types.d.ts +68 -0
- package/lib/server/modules/menu/types.js +33 -0
- package/lib/server/modules/products/index.d.ts +141 -0
- package/lib/server/modules/products/index.js +434 -0
- package/lib/server/modules/products/types.d.ts +94 -0
- package/lib/server/modules/products/types.js +35 -0
- package/lib/server/modules/quotation/index.d.ts +47 -0
- package/lib/server/modules/quotation/index.js +177 -0
- package/lib/server/modules/quotation/types.d.ts +50 -0
- package/lib/server/modules/quotation/types.js +33 -0
- package/lib/server/modules/schedule/index.d.ts +62 -0
- package/lib/server/modules/schedule/index.js +231 -0
- package/lib/server/modules/schedule/types.d.ts +1 -0
- package/lib/server/modules/schedule/types.js +23 -0
- package/lib/server/modules/schedule/utils.d.ts +32 -0
- package/lib/server/modules/schedule/utils.js +451 -0
- package/lib/server/types.d.ts +64 -0
- package/lib/server/types.js +17 -0
- package/lib/server/utils/index.d.ts +5 -0
- package/lib/server/utils/index.js +25 -0
- package/lib/server/utils/product.d.ts +18 -0
- package/lib/server/utils/product.js +262 -0
- package/lib/server/utils/schedule.d.ts +14 -0
- package/lib/server/utils/schedule.js +88 -0
- package/lib/server/utils/time.d.ts +18 -0
- package/lib/server/utils/time.js +70 -0
- package/lib/solution/BookingByStep/index.d.ts +1 -17
- package/lib/solution/BookingByStep/index.js +40 -312
- package/lib/solution/BookingByStep/utils/capacity.d.ts +2 -7
- package/lib/solution/BookingByStep/utils/capacity.js +8 -21
- package/lib/solution/BookingTicket/index.d.ts +12 -0
- package/lib/solution/BookingTicket/index.js +25 -6
- package/lib/solution/BookingTicket/utils/scan/index.d.ts +4 -0
- package/lib/solution/BookingTicket/utils/scan/index.js +7 -1
- package/lib/solution/BuyTickets/index.js +7 -8
- package/lib/solution/Checkout/index.d.ts +1 -46
- package/lib/solution/Checkout/index.js +92 -289
- package/lib/solution/ShopDiscount/index.js +10 -11
- package/lib/types/index.d.ts +27 -0
- package/package.json +2 -2
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/server/modules/products/index.ts
|
|
20
|
+
var products_exports = {};
|
|
21
|
+
__export(products_exports, {
|
|
22
|
+
ProductsModule: () => ProductsModule
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(products_exports);
|
|
25
|
+
var import_BaseModule = require("../../../modules/BaseModule");
|
|
26
|
+
var import_lodash_es = require("lodash-es");
|
|
27
|
+
var import_types = require("./types");
|
|
28
|
+
var import_product = require("../../utils/product");
|
|
29
|
+
var INDEXDB_STORE_NAME = "products";
|
|
30
|
+
var ProductsModule = class extends import_BaseModule.BaseModule {
|
|
31
|
+
constructor(name, version) {
|
|
32
|
+
super(name, version);
|
|
33
|
+
this.defaultName = "products";
|
|
34
|
+
this.defaultVersion = "1.0.0";
|
|
35
|
+
// IndexDBManager 实例
|
|
36
|
+
this.otherParams = {};
|
|
37
|
+
// 商品价格缓存:Map<日期, 应用了价格的商品列表>
|
|
38
|
+
this.productsPriceCache = /* @__PURE__ */ new Map();
|
|
39
|
+
// 缓存配置
|
|
40
|
+
this.CACHE_MAX_DAYS = 7;
|
|
41
|
+
// 最多缓存7天
|
|
42
|
+
// 商品格式化器列表
|
|
43
|
+
this.formatters = [];
|
|
44
|
+
// 是否已注册内置价格格式化器
|
|
45
|
+
this.isPriceFormatterRegistered = false;
|
|
46
|
+
}
|
|
47
|
+
async initialize(core, options) {
|
|
48
|
+
var _a;
|
|
49
|
+
this.core = core;
|
|
50
|
+
this.store = options.store;
|
|
51
|
+
this.otherParams = options.otherParams || {};
|
|
52
|
+
if (Array.isArray((_a = options.initialState) == null ? void 0 : _a.list)) {
|
|
53
|
+
this.store.list = options.initialState.list;
|
|
54
|
+
this.syncProductsMap();
|
|
55
|
+
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
56
|
+
} else {
|
|
57
|
+
this.store.list = [];
|
|
58
|
+
this.store.map = /* @__PURE__ */ new Map();
|
|
59
|
+
}
|
|
60
|
+
this.request = core.getPlugin("request");
|
|
61
|
+
const appPlugin = core.getPlugin("app");
|
|
62
|
+
if (appPlugin) {
|
|
63
|
+
const app = appPlugin.getApp();
|
|
64
|
+
this.dbManager = app.dbManager;
|
|
65
|
+
if (this.dbManager) {
|
|
66
|
+
console.log("[Products] IndexDB Manager 已初始化");
|
|
67
|
+
} else {
|
|
68
|
+
console.warn("[Products] IndexDB Manager 未找到");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
this.registerBuiltinPriceFormatter();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 加载商品价格(原始方法,不带缓存)
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
async loadProductsPrice({
|
|
78
|
+
ids = [],
|
|
79
|
+
customer_id,
|
|
80
|
+
schedule_date
|
|
81
|
+
}) {
|
|
82
|
+
const productsData = await this.request.post(
|
|
83
|
+
`/product/query/price`,
|
|
84
|
+
{
|
|
85
|
+
ids,
|
|
86
|
+
customer_id,
|
|
87
|
+
schedule_date
|
|
88
|
+
},
|
|
89
|
+
{ useCache: true }
|
|
90
|
+
);
|
|
91
|
+
return productsData.data;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 获取应用了价格的商品列表(带缓存)
|
|
95
|
+
* 使用日期作为缓存 key,同一天的商品价格会被缓存
|
|
96
|
+
* 缓存的是已经应用了价格的完整商品列表,避免重复转换
|
|
97
|
+
* @param schedule_date 日期
|
|
98
|
+
* @param extraContext 额外的上下文数据(可选,由 Server 层传入)
|
|
99
|
+
* @returns 应用了价格的商品列表
|
|
100
|
+
*/
|
|
101
|
+
async getProductsWithPrice(schedule_date, extraContext) {
|
|
102
|
+
const cacheKey = schedule_date;
|
|
103
|
+
if (this.productsPriceCache.has(cacheKey)) {
|
|
104
|
+
console.log(`[ProductsModule] 💰 商品价格缓存命中: ${cacheKey}`);
|
|
105
|
+
return this.productsPriceCache.get(cacheKey);
|
|
106
|
+
}
|
|
107
|
+
console.log(`[ProductsModule] 🌐 获取商品并应用价格: ${cacheKey}`);
|
|
108
|
+
const result = await this.prepareProductsWithPrice(schedule_date, extraContext);
|
|
109
|
+
this.productsPriceCache.set(cacheKey, result);
|
|
110
|
+
console.log(`[ProductsModule] ✅ 商品价格已缓存: ${cacheKey}, 共 ${result.length} 个商品`);
|
|
111
|
+
this.cleanExpiredPriceCache();
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 准备带价格的商品数据(通过格式化器流程处理)
|
|
116
|
+
* @param schedule_date 日期
|
|
117
|
+
* @param extraContext 额外的上下文数据(可选)
|
|
118
|
+
* @returns 完整处理后的商品列表
|
|
119
|
+
* @private
|
|
120
|
+
*/
|
|
121
|
+
async prepareProductsWithPrice(schedule_date, extraContext) {
|
|
122
|
+
const allProducts = await this.getProducts();
|
|
123
|
+
const priceData = await this.loadProductsPrice({
|
|
124
|
+
ids: allProducts.map((product) => product.id),
|
|
125
|
+
schedule_date
|
|
126
|
+
});
|
|
127
|
+
const context = {
|
|
128
|
+
schedule_date,
|
|
129
|
+
priceData,
|
|
130
|
+
...extraContext
|
|
131
|
+
// 合并 Server 层传入的额外数据(如 scheduleList)
|
|
132
|
+
};
|
|
133
|
+
const processedProducts = await this.applyFormatters(allProducts, context);
|
|
134
|
+
await this.core.effects.emit(
|
|
135
|
+
import_types.ProductsHooks.onProductsPriceApplied,
|
|
136
|
+
processedProducts
|
|
137
|
+
);
|
|
138
|
+
return processedProducts;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 应用所有已注册的格式化器
|
|
142
|
+
* @param products 商品列表
|
|
143
|
+
* @param context 上下文信息
|
|
144
|
+
* @returns 格式化后的商品列表
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
async applyFormatters(products, context) {
|
|
148
|
+
let result = products;
|
|
149
|
+
if (this.formatters.length === 0) {
|
|
150
|
+
console.warn("[ProductsModule] ⚠️ 没有注册任何格式化器");
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
for (let i = 0; i < this.formatters.length; i++) {
|
|
154
|
+
const formatter = this.formatters[i];
|
|
155
|
+
try {
|
|
156
|
+
console.log(`[ProductsModule] 📝 应用格式化器 ${i + 1}/${this.formatters.length}`);
|
|
157
|
+
result = await formatter(result, context);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(`[ProductsModule] ❌ 格式化器 ${i + 1} 执行失败:`, error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log(`[ProductsModule] ✅ 所有格式化器已应用,共 ${this.formatters.length} 个`);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* 注册内置的价格格式化器
|
|
167
|
+
* 这个格式化器负责将价格数据应用到商品上
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
registerBuiltinPriceFormatter() {
|
|
171
|
+
if (this.isPriceFormatterRegistered) {
|
|
172
|
+
console.warn("[ProductsModule] 内置价格格式化器已注册,跳过");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const priceFormatter = (products, context) => {
|
|
176
|
+
if (!context.priceData || context.priceData.length === 0) {
|
|
177
|
+
console.log("[ProductsModule] 💰 没有价格数据,跳过价格应用");
|
|
178
|
+
return products;
|
|
179
|
+
}
|
|
180
|
+
console.log(`[ProductsModule] 💰 应用价格数据到 ${products.length} 个商品`);
|
|
181
|
+
return (0, import_product.applyPriceDataToProducts)(products, context.priceData);
|
|
182
|
+
};
|
|
183
|
+
const detailValueFormatter = (products, context) => {
|
|
184
|
+
return (0, import_product.applyDetailValueToProducts)(products, context);
|
|
185
|
+
};
|
|
186
|
+
this.formatters.unshift(priceFormatter);
|
|
187
|
+
this.formatters.push(detailValueFormatter);
|
|
188
|
+
this.isPriceFormatterRegistered = true;
|
|
189
|
+
console.log("[ProductsModule] ✅ 内置价格格式化器已注册(第 1 个)");
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 注册商品格式化器
|
|
193
|
+
* 格式化器会按注册顺序依次执行(内置价格格式化器始终是第一个)
|
|
194
|
+
* @param formatter 格式化函数
|
|
195
|
+
* @param prepend 是否插入到队列开头(默认 false,追加到末尾)
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* productsModule.registerFormatter((products, context) => {
|
|
199
|
+
* return products.map(p => ({
|
|
200
|
+
* ...p,
|
|
201
|
+
* custom_field: 'custom_value'
|
|
202
|
+
* }));
|
|
203
|
+
* });
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
registerFormatter(formatter, prepend = false) {
|
|
207
|
+
if (prepend) {
|
|
208
|
+
const insertIndex = this.isPriceFormatterRegistered ? 1 : 0;
|
|
209
|
+
this.formatters.splice(insertIndex, 0, formatter);
|
|
210
|
+
console.log(`[ProductsModule] 📌 已在位置 ${insertIndex + 1} 插入格式化器,当前共 ${this.formatters.length} 个`);
|
|
211
|
+
} else {
|
|
212
|
+
this.formatters.push(formatter);
|
|
213
|
+
console.log(`[ProductsModule] 📌 已注册格式化器,当前共 ${this.formatters.length} 个`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* 清空所有格式化器(包括内置价格格式化器)
|
|
218
|
+
* @param keepBuiltin 是否保留内置价格格式化器(默认 true)
|
|
219
|
+
*/
|
|
220
|
+
clearFormatters(keepBuiltin = true) {
|
|
221
|
+
if (keepBuiltin && this.isPriceFormatterRegistered) {
|
|
222
|
+
this.formatters = [this.formatters[0]];
|
|
223
|
+
console.log("[ProductsModule] 🧹 已清空自定义格式化器,保留内置价格格式化器");
|
|
224
|
+
} else {
|
|
225
|
+
this.formatters = [];
|
|
226
|
+
this.isPriceFormatterRegistered = false;
|
|
227
|
+
console.log("[ProductsModule] 🧹 已清空所有格式化器");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 清理过期的价格缓存
|
|
232
|
+
* 当缓存数量超过限制时,删除最老的缓存
|
|
233
|
+
* @private
|
|
234
|
+
*/
|
|
235
|
+
cleanExpiredPriceCache() {
|
|
236
|
+
if (this.productsPriceCache.size > this.CACHE_MAX_DAYS) {
|
|
237
|
+
const keys = Array.from(this.productsPriceCache.keys());
|
|
238
|
+
const oldestKey = keys[0];
|
|
239
|
+
this.productsPriceCache.delete(oldestKey);
|
|
240
|
+
console.log(`[ProductsModule] 🧹 清理过期商品价格缓存: ${oldestKey}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 清空商品价格缓存
|
|
245
|
+
* 可用于手动刷新价格数据
|
|
246
|
+
*/
|
|
247
|
+
clearPriceCache() {
|
|
248
|
+
this.productsPriceCache.clear();
|
|
249
|
+
console.log("[ProductsModule] 🗑️ 商品价格缓存已清空");
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 加载完整商品列表通过接口(包含所有详细数据)
|
|
253
|
+
* @param params 查询参数
|
|
254
|
+
*/
|
|
255
|
+
async loadProductsByServer(params) {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
const {
|
|
258
|
+
category_ids = [],
|
|
259
|
+
product_ids = [],
|
|
260
|
+
collection = [],
|
|
261
|
+
customer_id,
|
|
262
|
+
cacheId
|
|
263
|
+
} = params || {};
|
|
264
|
+
try {
|
|
265
|
+
const productsData = await this.request.post(
|
|
266
|
+
`/product/query`,
|
|
267
|
+
{
|
|
268
|
+
open_bundle: 1,
|
|
269
|
+
exclude_extension_type: [
|
|
270
|
+
"product_party",
|
|
271
|
+
"product_event",
|
|
272
|
+
"product_series_event",
|
|
273
|
+
"product_package_ticket",
|
|
274
|
+
"ticket",
|
|
275
|
+
"event_item"
|
|
276
|
+
],
|
|
277
|
+
force_ignore_cache_flag: 1,
|
|
278
|
+
// 获取所有详细信息
|
|
279
|
+
with: [
|
|
280
|
+
"category",
|
|
281
|
+
"collection",
|
|
282
|
+
"resourceRelation",
|
|
283
|
+
// 套餐
|
|
284
|
+
"bundleGroup.bundleItem",
|
|
285
|
+
// 单规格
|
|
286
|
+
"optionGroup.optionItem",
|
|
287
|
+
// 组合规格
|
|
288
|
+
"variantGroup.variantItem"
|
|
289
|
+
],
|
|
290
|
+
open_deposit: 1,
|
|
291
|
+
with_count: ["bundleGroup", "optionGroup"],
|
|
292
|
+
status: "published",
|
|
293
|
+
num: 500,
|
|
294
|
+
skip: 1,
|
|
295
|
+
category_ids,
|
|
296
|
+
ids: product_ids,
|
|
297
|
+
collection,
|
|
298
|
+
front_end_cache_id: cacheId,
|
|
299
|
+
application_code: (_a = this.otherParams) == null ? void 0 : _a.channel
|
|
300
|
+
},
|
|
301
|
+
{ useCache: true }
|
|
302
|
+
);
|
|
303
|
+
await this.saveProductsToIndexDB(((_b = productsData == null ? void 0 : productsData.data) == null ? void 0 : _b.list) || []);
|
|
304
|
+
await this.core.effects.emit(
|
|
305
|
+
import_types.ProductsHooks.onProductsLoaded,
|
|
306
|
+
productsData.data.list
|
|
307
|
+
);
|
|
308
|
+
return productsData.data.list;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error("[Products] 加载商品数据失败:", error);
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* 获取商品列表(从缓存)
|
|
316
|
+
*/
|
|
317
|
+
async getProducts() {
|
|
318
|
+
return (0, import_lodash_es.cloneDeep)(this.store.list);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 根据ID获取单个商品(从内存缓存)
|
|
322
|
+
* 使用 Map 快速查询,时间复杂度 O(1)
|
|
323
|
+
*/
|
|
324
|
+
async getProductById(id) {
|
|
325
|
+
const product = this.store.map.get(id);
|
|
326
|
+
return product ? (0, import_lodash_es.cloneDeep)(product) : void 0;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* 清空缓存
|
|
330
|
+
*/
|
|
331
|
+
async clear() {
|
|
332
|
+
this.store.list = [];
|
|
333
|
+
this.store.map.clear();
|
|
334
|
+
if (this.dbManager) {
|
|
335
|
+
try {
|
|
336
|
+
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
337
|
+
console.log("[Products] IndexDB 缓存已清空");
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error("[Products] 清空 IndexDB 缓存失败:", error);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
console.log("[Products] 缓存已清空");
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 从 IndexDB 加载商品数据
|
|
346
|
+
* @private
|
|
347
|
+
*/
|
|
348
|
+
async loadProductsFromIndexDB() {
|
|
349
|
+
if (!this.dbManager) {
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
const products = await this.dbManager.getAll(INDEXDB_STORE_NAME);
|
|
354
|
+
return products || [];
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error("[Products] 从 IndexDB 读取数据失败:", error);
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 保存商品数据到 IndexDB(平铺存储)
|
|
362
|
+
* @private
|
|
363
|
+
*/
|
|
364
|
+
async saveProductsToIndexDB(products) {
|
|
365
|
+
if (!this.dbManager) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
await this.dbManager.clear(INDEXDB_STORE_NAME);
|
|
370
|
+
const savePromises = products.map(
|
|
371
|
+
(product) => this.dbManager.add(INDEXDB_STORE_NAME, product)
|
|
372
|
+
);
|
|
373
|
+
await Promise.all(savePromises);
|
|
374
|
+
console.log(
|
|
375
|
+
`[Products] 已将 ${products.length} 个商品平铺保存到 IndexDB`
|
|
376
|
+
);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error("[Products] 保存数据到 IndexDB 失败:", error);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* 同步更新商品 Map 缓存
|
|
383
|
+
* 将 list 中的商品同步到 map,以 id 为 key
|
|
384
|
+
* @private
|
|
385
|
+
*/
|
|
386
|
+
syncProductsMap() {
|
|
387
|
+
this.store.map.clear();
|
|
388
|
+
for (const product of this.store.list) {
|
|
389
|
+
this.store.map.set(product.id, product);
|
|
390
|
+
}
|
|
391
|
+
console.log(`[Products] Map 缓存已同步,共 ${this.store.map.size} 个商品`);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 预加载模块数据(统一接口)
|
|
395
|
+
* 在模块注册后自动调用
|
|
396
|
+
*/
|
|
397
|
+
async preload() {
|
|
398
|
+
console.log("[Products] 开始预加载数据...");
|
|
399
|
+
try {
|
|
400
|
+
const cachedData = await this.loadProductsFromIndexDB();
|
|
401
|
+
if (cachedData && cachedData.length > 0) {
|
|
402
|
+
console.log(`[Products] 从 IndexDB 加载了 ${cachedData.length} 个商品`);
|
|
403
|
+
this.store.list = (0, import_lodash_es.cloneDeep)(cachedData);
|
|
404
|
+
this.syncProductsMap();
|
|
405
|
+
this.core.effects.emit(
|
|
406
|
+
import_types.ProductsHooks.onProductsChanged,
|
|
407
|
+
this.store.list
|
|
408
|
+
);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
console.log("[Products] IndexDB 中没有缓存数据,从服务器加载...");
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.warn("[Products] 从 IndexDB 加载数据失败:", error);
|
|
414
|
+
}
|
|
415
|
+
const products = await this.loadProductsByServer();
|
|
416
|
+
if (products && products.length > 0) {
|
|
417
|
+
await this.saveProductsToIndexDB(products);
|
|
418
|
+
this.store.list = (0, import_lodash_es.cloneDeep)(products);
|
|
419
|
+
this.syncProductsMap();
|
|
420
|
+
this.core.effects.emit(import_types.ProductsHooks.onProductsChanged, this.store.list);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 获取模块的路由定义
|
|
425
|
+
* Products 模块暂不提供路由,由 Server 层统一处理
|
|
426
|
+
*/
|
|
427
|
+
getRoutes() {
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
432
|
+
0 && (module.exports = {
|
|
433
|
+
ProductsModule
|
|
434
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ProductData } from '../../../modules/Product/types';
|
|
2
|
+
/**
|
|
3
|
+
* Products 模块状态
|
|
4
|
+
*/
|
|
5
|
+
export interface ProductsState {
|
|
6
|
+
/** 完整商品列表 */
|
|
7
|
+
list: ProductData[];
|
|
8
|
+
/** 商品 Map 缓存(以 id 为 key,加速查询) */
|
|
9
|
+
map: Map<number, ProductData>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 商品格式化后数据
|
|
13
|
+
*/
|
|
14
|
+
export interface FormattedProductData extends ProductData {
|
|
15
|
+
/** 是否打开详情弹窗 */
|
|
16
|
+
isOpenDetailModal?: boolean;
|
|
17
|
+
/** 购物车详情值 */
|
|
18
|
+
cartDetailValue?: any;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 加载报价单商品价格参数
|
|
22
|
+
*/
|
|
23
|
+
export interface LoadProductsPriceParams {
|
|
24
|
+
ids: number[];
|
|
25
|
+
schedule_date: string;
|
|
26
|
+
customer_id?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 加载报价单商品价格返回数据
|
|
30
|
+
*/
|
|
31
|
+
export interface LoadProductsPriceData {
|
|
32
|
+
id: number;
|
|
33
|
+
price: string;
|
|
34
|
+
base_price: string;
|
|
35
|
+
variant: {
|
|
36
|
+
id: number;
|
|
37
|
+
product_id: number;
|
|
38
|
+
base_price: number;
|
|
39
|
+
price: number;
|
|
40
|
+
}[];
|
|
41
|
+
bundle_group: {
|
|
42
|
+
id: number;
|
|
43
|
+
bundle_item: {
|
|
44
|
+
id: number;
|
|
45
|
+
product_id: number;
|
|
46
|
+
group_id: number;
|
|
47
|
+
bundle_product_id: number;
|
|
48
|
+
bundle_variant_id: number;
|
|
49
|
+
price: number;
|
|
50
|
+
price_type: string;
|
|
51
|
+
price_type_ext: string;
|
|
52
|
+
}[];
|
|
53
|
+
}[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 加载报价单商品价格返回数据
|
|
57
|
+
*/
|
|
58
|
+
export interface LoadProductsPriceResponse {
|
|
59
|
+
code: number;
|
|
60
|
+
data: LoadProductsPriceData[];
|
|
61
|
+
message: string;
|
|
62
|
+
status: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Products 模块状态
|
|
66
|
+
*/
|
|
67
|
+
export interface ProductsState {
|
|
68
|
+
/** 完整商品列表 */
|
|
69
|
+
list: ProductData[];
|
|
70
|
+
/** 商品 Map 缓存(以 id 为 key,加速查询) */
|
|
71
|
+
map: Map<number, ProductData>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Products 模块钩子
|
|
75
|
+
*/
|
|
76
|
+
export declare enum ProductsHooks {
|
|
77
|
+
onProductsLoaded = "products:onProductsLoaded",
|
|
78
|
+
onProductsChanged = "products:onProductsChanged",
|
|
79
|
+
onProductSelected = "products:onProductSelected",
|
|
80
|
+
onProductsPriceApplied = "products:onProductsPriceApplied"
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 商品格式化器上下文
|
|
84
|
+
*/
|
|
85
|
+
export interface ProductFormatterContext {
|
|
86
|
+
schedule_date: string;
|
|
87
|
+
priceData?: LoadProductsPriceData[];
|
|
88
|
+
scheduleModule?: any;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 商品格式化器类型
|
|
92
|
+
* 用于对商品数据进行处理和格式化(包括价格应用、字段扩展等)
|
|
93
|
+
*/
|
|
94
|
+
export type ProductFormatter = (products: ProductData[], context: ProductFormatterContext) => ProductData[] | Promise<ProductData[]>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/server/modules/products/types.ts
|
|
20
|
+
var types_exports = {};
|
|
21
|
+
__export(types_exports, {
|
|
22
|
+
ProductsHooks: () => ProductsHooks
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(types_exports);
|
|
25
|
+
var ProductsHooks = /* @__PURE__ */ ((ProductsHooks2) => {
|
|
26
|
+
ProductsHooks2["onProductsLoaded"] = "products:onProductsLoaded";
|
|
27
|
+
ProductsHooks2["onProductsChanged"] = "products:onProductsChanged";
|
|
28
|
+
ProductsHooks2["onProductSelected"] = "products:onProductSelected";
|
|
29
|
+
ProductsHooks2["onProductsPriceApplied"] = "products:onProductsPriceApplied";
|
|
30
|
+
return ProductsHooks2;
|
|
31
|
+
})(ProductsHooks || {});
|
|
32
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
33
|
+
0 && (module.exports = {
|
|
34
|
+
ProductsHooks
|
|
35
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Module, PisellCore, ModuleOptions } from '../../../types';
|
|
2
|
+
import { BaseModule } from '../../../modules/BaseModule';
|
|
3
|
+
import { QuotationData } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* 报价单模块 - 用于管理报价单数据和计算商品价格
|
|
6
|
+
*/
|
|
7
|
+
export declare class QuotationModule extends BaseModule implements Module {
|
|
8
|
+
protected defaultName: string;
|
|
9
|
+
protected defaultVersion: string;
|
|
10
|
+
private request;
|
|
11
|
+
private store;
|
|
12
|
+
private dbManager;
|
|
13
|
+
constructor(name?: string, version?: string);
|
|
14
|
+
initialize(core: PisellCore, options?: ModuleOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* 加载报价单列表(从服务器)
|
|
17
|
+
* TODO: 接口地址待定
|
|
18
|
+
*/
|
|
19
|
+
loadQuotationList(): Promise<QuotationData[]>;
|
|
20
|
+
/**
|
|
21
|
+
* 设置报价单列表
|
|
22
|
+
*/
|
|
23
|
+
setQuotationList(quotationList: QuotationData[]): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* 获取报价单列表
|
|
26
|
+
*/
|
|
27
|
+
getQuotationList(): QuotationData[];
|
|
28
|
+
/**
|
|
29
|
+
* 清空缓存
|
|
30
|
+
*/
|
|
31
|
+
clear(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* 从 IndexDB 加载报价单数据
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
private loadQuotationFromIndexDB;
|
|
37
|
+
/**
|
|
38
|
+
* 保存报价单数据到 IndexDB
|
|
39
|
+
* @private
|
|
40
|
+
*/
|
|
41
|
+
private saveQuotationToIndexDB;
|
|
42
|
+
/**
|
|
43
|
+
* 预加载模块数据(统一接口)
|
|
44
|
+
* 在模块注册后自动调用
|
|
45
|
+
*/
|
|
46
|
+
preload(): Promise<void>;
|
|
47
|
+
}
|