@rxdrag/website-lib-core 0.0.128 → 0.0.129

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxdrag/website-lib-core",
3
- "version": "0.0.128",
3
+ "version": "0.0.129",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts"
@@ -23,9 +23,9 @@
23
23
  "@types/react-dom": "^19.1.0",
24
24
  "eslint": "^9.39.2",
25
25
  "typescript": "^5",
26
- "@rxdrag/tiptap-preview": "0.0.3",
27
26
  "@rxdrag/eslint-config-custom": "0.2.13",
28
- "@rxdrag/tsconfig": "0.2.1"
27
+ "@rxdrag/tsconfig": "0.2.1",
28
+ "@rxdrag/tiptap-preview": "0.0.3"
29
29
  },
30
30
  "dependencies": {
31
31
  "@iconify/utils": "^3.0.2",
@@ -1,533 +1,540 @@
1
- import { EnvVariables } from "./types";
2
- import { queryOneTheme } from "./lib/queryOneTheme";
3
- import {
4
- ListConditions,
5
- queryEntityList,
6
- queryFeaturedProducts,
7
- queryLangs,
8
- queryLatestPosts,
9
- queryOnePostBySlug,
10
- queryOnePostCategoryBySlug,
11
- queryOneProductBySlug,
12
- queryOneProductCategoryBySlug,
13
- queryOneUser,
14
- queryPostCategories,
15
- queryPosts,
16
- queryProductCategories,
17
- queryProducts,
18
- queryTagCategories,
19
- queryTags,
20
- queryUserPosts,
21
- fulltextSearch,
22
- queryOneEntity,
23
- createUploadCredentials,
24
- UploadOptions,
25
- UploadSession,
26
- } from "./lib";
27
- import { IQueryOptions } from "@rxdrag/entify-lib";
28
- import { queryAllProducts } from "./lib/queryAllProducts";
29
- import { queryPostSlugs } from "./lib/queryPostSlugs";
30
- import { queryUserIds } from "./lib/queryUserIds";
31
- import { IEntify, PostPatinateOptions, PostsOptions } from "./IEntify";
32
- import { queryWebsite } from "./lib/queryWebsite";
33
- import {
34
- ImageSize,
35
- ImageSizes,
36
- Product,
37
- WebsitePart,
38
- WebsitePartBoolExp,
39
- } from "@rxdrag/rxcms-models";
40
- import { queryOneMedia } from "./lib/queryOneMedia";
41
- import { FileFieldType, ImageResize } from "../astro/media";
42
- import { queryBulletin } from "./lib/queryBulletin";
43
-
44
- export class Entify implements IEntify {
45
- private static instance: Entify | null = null;
46
-
47
- private constructor(protected envVariables: EnvVariables, protected imageSizes: ImageSizes) { }
48
- private flattenTree<T>(nodes: T[] | undefined): T[] {
49
- if (!nodes) return [];
50
- let result: T[] = [];
51
- for (const node of nodes) {
52
- result.push(node);
53
- const children = (node as { children?: T[] }).children;
54
- if (children && children.length > 0) {
55
- result = result.concat(this.flattenTree(children));
56
- }
57
- }
58
- return result;
59
- }
60
-
61
- /**
62
- * 获取 Entify 单例实例(无参数,获取已创建的实例)
63
- */
64
- public static getInstance(): Entify;
65
- /**
66
- * 获取 Entify 单例实例(带参数,创建或更新实例)
67
- */
68
- public static getInstance(envVariables: EnvVariables, imageSizes: ImageSizes): Entify;
69
- public static getInstance(envVariables?: EnvVariables, imageSizes?: ImageSizes): Entify {
70
- // 无参数调用:获取已创建的实例
71
- if (envVariables === undefined && imageSizes === undefined) {
72
- if (!Entify.instance) {
73
- throw new Error(
74
- 'Entify instance not initialized. Please call getInstance(envVariables, imageSizes) first.'
75
- );
76
- }
77
- return Entify.instance;
78
- }
79
-
80
- // 有参数调用:创建或更新实例
81
- if (!Entify.instance) {
82
- Entify.instance = new Entify(envVariables!, imageSizes!);
83
- } else {
84
- Entify.instance.envVariables = envVariables!;
85
- Entify.instance.imageSizes = imageSizes!;
86
- }
87
- return Entify.instance;
88
- }
89
-
90
- private currentLangAbbr?: string;
91
-
92
- /**
93
- * 设置当前语言
94
- */
95
- public setLangAbbr(langAbbr: string): void {
96
- this.currentLangAbbr = langAbbr;
97
- }
98
-
99
- /**
100
- * 获取当前语言,如果未设置则抛出异常
101
- */
102
- private ensureLangAbbr(): string {
103
- if (!this.currentLangAbbr) {
104
- throw new Error(
105
- 'langAbbr is not set. Please call setLangAbbr() before using language-dependent methods.'
106
- );
107
- }
108
- return this.currentLangAbbr;
109
- }
110
-
111
- public setImageSizes(imageSizes: ImageSizes): void {
112
- this.imageSizes = imageSizes;
113
- }
114
-
115
- public getImageSizes(): ImageSizes {
116
- return this.imageSizes;
117
- }
118
-
119
- public async queryEntityList<
120
- T,
121
- WhereExp = unknown,
122
- OrderBy = unknown,
123
- DistinctExp = unknown
124
- >(options: IQueryOptions<T, WhereExp, OrderBy, DistinctExp>) {
125
- return await queryEntityList<T, WhereExp, OrderBy, DistinctExp>(
126
- options,
127
- this.envVariables
128
- );
129
- }
130
-
131
- public async queryOneEntity<T extends WebsitePart = WebsitePart>(
132
- options: IQueryOptions<T, WebsitePartBoolExp>
133
- ) {
134
- return await queryOneEntity<T, WebsitePartBoolExp>(
135
- options,
136
- this.envVariables
137
- );
138
- }
139
-
140
- public async getWebsite() {
141
- return queryWebsite(this.envVariables);
142
- }
143
-
144
- public async getTheme() {
145
- return await queryOneTheme(this.envVariables);
146
- }
147
-
148
- public async getLangs() {
149
- return (await queryLangs(this.envVariables))?.items;
150
- }
151
-
152
- public async getBaseLang() {
153
- const website = await this.getWebsite();
154
- return website?.baseLang;
155
- }
156
-
157
- public async getMedia(ref: string | undefined | null, fileField: FileFieldType = 'thumbnail', resize?: ImageResize) {
158
- const fields = [fileField];
159
- return await queryOneMedia(ref, fields, this.envVariables);
160
- }
161
-
162
- public async getBulletin() {
163
- const langAbbr = this.ensureLangAbbr();
164
- return await queryBulletin(this.envVariables, langAbbr);
165
- }
166
-
167
-
168
- public async getFeaturedProducts(
169
- count: number | undefined,
170
- imageSize: ImageSize | undefined,
171
- addonFields: (keyof Product)[] | undefined
172
- ) {
173
- const langAbbr = this.ensureLangAbbr();
174
- if (!imageSize) {
175
- imageSize = this.imageSizes?.productThumbnial;
176
- }
177
- return await queryFeaturedProducts(
178
- count,
179
- imageSize,
180
- this.envVariables,
181
- addonFields,
182
- langAbbr
183
- );
184
- }
185
-
186
- public async getLatestPosts(count: number | undefined, coverSize: ImageSize | undefined) {
187
- const langAbbr = this.ensureLangAbbr();
188
- if (!coverSize) {
189
- coverSize = this.imageSizes?.postThumbnial;
190
- }
191
- return await queryLatestPosts(count, coverSize, this.envVariables, langAbbr);
192
- }
193
-
194
-
195
- public async getPosts(options: PostsOptions) {
196
- const langAbbr = this.ensureLangAbbr();
197
- const { category, coverSize, page, pageSize } = options;
198
- const conditions: ListConditions = { category, page, pageSize };
199
- const result = await queryPosts(conditions, coverSize, this.envVariables, langAbbr);
200
- return result?.items;
201
- }
202
-
203
- public async getPostListPaths(options: PostPatinateOptions) {
204
- const langAbbr = this.ensureLangAbbr();
205
- const { category, pageSize } = options;
206
- // 获取总文章数据,只需要获取总数
207
- // TODO: 目前数据查询有点重复,后续要结合后端优化
208
- const postsData = await queryPosts(
209
- { category, page: 1, pageSize: 10 },
210
- undefined,
211
- this.envVariables,
212
- langAbbr
213
- );
214
-
215
- const totalItems = postsData?.total ?? 0;
216
-
217
- return this.createPagination(totalItems, pageSize);
218
- }
219
-
220
- public async getPostPaths() {
221
- // 获取所有文章的 slug
222
- const slugs = await this.getPostSlugs();
223
-
224
- // 为每个 slug 创建路径
225
- return slugs.map((slug) => ({
226
- params: { slug },
227
- props: { slug },
228
- }));
229
- }
230
-
231
- public async getPostBySlug(slug: string, coverSize: ImageSize | undefined) {
232
- const langAbbr = this.ensureLangAbbr();
233
- if (!coverSize) {
234
- coverSize = this.imageSizes.post;
235
- }
236
- return await queryOnePostBySlug(slug, coverSize, this.envVariables, langAbbr);
237
- }
238
-
239
- /**
240
- * 获取所有文章的 slug,用于生成静态页面
241
- * 对于大量文章,这个方法会进行优化,只获取 slug 字段
242
- */
243
- public async getPostSlugs() {
244
- const langAbbr = this.ensureLangAbbr();
245
- // 只获取 slug 字段,减少数据传输量
246
- const result = await queryPostSlugs(this.envVariables, langAbbr);
247
-
248
- return result?.items?.map((post) => post.slug) || [];
249
- }
250
-
251
- public async getPostCategories() {
252
- const langAbbr = this.ensureLangAbbr();
253
- return await queryPostCategories(this.envVariables, langAbbr);
254
- }
255
-
256
- public async getPostCategoryBySlug(slug: string) {
257
- const langAbbr = this.ensureLangAbbr();
258
- return await queryOnePostCategoryBySlug(slug, this.envVariables, langAbbr);
259
- }
260
-
261
- public async getTagCategories() {
262
- const langAbbr = this.ensureLangAbbr();
263
- return await queryTagCategories(this.envVariables, langAbbr);
264
- }
265
-
266
- public async getTags(categoryId: string | undefined) {
267
- const langAbbr = this.ensureLangAbbr();
268
- return await queryTags(this.envVariables, categoryId, langAbbr);
269
- }
270
-
271
- public async getProducts(
272
- conditions: ListConditions,
273
- imageSize: ImageSize | undefined,
274
- addonFields: (keyof Product)[] | undefined
275
- ) {
276
- const langAbbr = this.ensureLangAbbr();
277
- if (!imageSize) {
278
- imageSize = this.imageSizes.productThumbnial;
279
- }
280
- return (
281
- await queryProducts(conditions, imageSize, this.envVariables, addonFields, langAbbr)
282
- )?.items;
283
- }
284
-
285
- public async getProductListPaths(options: {
286
- category?: string;
287
- pageSize: number;
288
- }) {
289
- const langAbbr = this.ensureLangAbbr();
290
- const { category, pageSize } = options;
291
- // 获取总产品数据,只需要获取总数
292
- const productsData = await queryProducts(
293
- { category, page: 1, pageSize: 10 },
294
- undefined,
295
- this.envVariables,
296
- undefined,
297
- langAbbr
298
- );
299
-
300
- const totalItems = productsData?.total ?? 0;
301
-
302
- return this.createPagination(totalItems, pageSize);
303
- }
304
-
305
- public async getCategoredProductListPaths() {
306
- const langAbbr = this.ensureLangAbbr();
307
- // 获取所有产品分类
308
- const categoryTree = await this.getProductCategories();
309
- const categories = this.flattenTree(categoryTree);
310
-
311
- // 为每个分类生成路径
312
- const paths = [];
313
-
314
- // 如果没有分类,至少创建一个默认路径
315
- if (!categories || categories.length === 0) {
316
- paths.push({
317
- params: { slug: "default", page: "1" },
318
- props: {
319
- currentPage: 1,
320
- totalPages: 1,
321
- pageSize: 12,
322
- totalItems: 0,
323
- slug: "default",
324
- },
325
- });
326
- return paths;
327
- }
328
-
329
- for (const category of categories) {
330
- // 跳过没有 slug 的分类
331
- if (!category.slug) continue;
332
-
333
- // 获取该分类下的分页路径
334
- const categoryPaths = await this.getProductListPaths({
335
- pageSize: 12,
336
- category: category.slug,
337
- });
338
-
339
- // 如果该分类没有产品,至少创建一个第一页
340
- if (categoryPaths.length === 0) {
341
- paths.push({
342
- params: { slug: String(category.slug), page: "1" },
343
- props: {
344
- currentPage: 1,
345
- totalPages: 1,
346
- pageSize: 12,
347
- totalItems: 0,
348
- slug: String(category.slug),
349
- },
350
- });
351
- continue;
352
- }
353
-
354
- // 为每个分页路径添加分类 slug
355
- for (const path of categoryPaths) {
356
- paths.push({
357
- params: { ...path.params, slug: String(category.slug) },
358
- props: { ...path.props, slug: String(category.slug) },
359
- });
360
- }
361
- }
362
-
363
- return paths;
364
- }
365
-
366
- /**
367
- * 获取所有文章分类的分页路径
368
- * 用于生成静态页面
369
- * @returns 所有文章分类的分页路径
370
- */
371
- public async getCategoredPostListPaths() {
372
- const langAbbr = this.ensureLangAbbr();
373
- // 获取所有文章分类
374
- const categoryTree = await this.getPostCategories();
375
- const categories = this.flattenTree(categoryTree);
376
-
377
- // 为每个分类生成路径
378
- const paths = [];
379
-
380
- // 如果没有分类,至少创建一个默认路径
381
- if (!categories || categories.length === 0) {
382
- paths.push({
383
- params: { slug: "default", page: "1" },
384
- props: {
385
- currentPage: 1,
386
- totalPages: 1,
387
- pageSize: 10,
388
- totalItems: 0,
389
- slug: "default",
390
- },
391
- });
392
- return paths;
393
- }
394
-
395
- for (const category of categories) {
396
- // 跳过没有 slug 的分类
397
- if (!category.slug) continue;
398
-
399
- // 获取该分类下的分页路径
400
- const categoryPaths = await this.getPostListPaths({
401
- pageSize: 10,
402
- category: category.slug,
403
- });
404
-
405
- // 如果该分类没有文章,至少创建一个第一页
406
- if (categoryPaths.length === 0) {
407
- paths.push({
408
- params: { slug: String(category.slug), page: "1" },
409
- props: {
410
- currentPage: 1,
411
- totalPages: 1,
412
- pageSize: 10,
413
- totalItems: 0,
414
- slug: String(category.slug),
415
- },
416
- });
417
- continue;
418
- }
419
-
420
- // 为每个分页路径添加分类 slug
421
- for (const path of categoryPaths) {
422
- paths.push({
423
- params: { ...path.params, slug: String(category.slug) },
424
- props: { ...path.props, slug: String(category.slug) },
425
- });
426
- }
427
- }
428
-
429
- return paths;
430
- }
431
-
432
- public async getProductBySlug(slug: string, imageSize: ImageSize | undefined) {
433
- const langAbbr = this.ensureLangAbbr();
434
- if (!imageSize) {
435
- imageSize = this.imageSizes.product;
436
- }
437
- return await queryOneProductBySlug(slug, imageSize, this.envVariables, langAbbr);
438
- }
439
-
440
- public async getProductPaths() {
441
- // 获取所有产品的 slug
442
- const slugs = await this.getProductSlugs();
443
-
444
- // 为每个 slug 创建路径
445
- return slugs.map((slug) => ({
446
- params: { slug },
447
- props: { slug },
448
- }));
449
- }
450
-
451
- /**
452
- * 获取所有产品的 slug,用于生成静态页面
453
- * 对于大量产品,这个方法会进行优化,只获取 slug 字段
454
- */
455
- public async getProductSlugs() {
456
- const langAbbr = this.ensureLangAbbr();
457
- // 只获取 slug 字段,减少数据传输量
458
- const result = await queryAllProducts(this.envVariables, langAbbr);
459
- return result?.items?.map((product) => product.slug) || [];
460
- }
461
-
462
- public async getProductIds() {
463
- const langAbbr = this.ensureLangAbbr();
464
- const result = await queryAllProducts(this.envVariables, langAbbr);
465
- return result?.items?.map((product) => product.id) || [];
466
- }
467
-
468
- public async getProductCategories() {
469
- const langAbbr = this.ensureLangAbbr();
470
- return await queryProductCategories(this.envVariables, langAbbr);
471
- }
472
-
473
- public async getProductCategoryBySlug(slug: string) {
474
- const langAbbr = this.ensureLangAbbr();
475
- return await queryOneProductCategoryBySlug(slug, this.envVariables, langAbbr);
476
- }
477
-
478
- public async getOneUser(id: string) {
479
- return await queryOneUser(id, this.envVariables);
480
- }
481
-
482
- public async getUserPosts(userId: string) {
483
- const langAbbr = this.ensureLangAbbr();
484
- return (await queryUserPosts({ userId }, this.envVariables, langAbbr))?.items;
485
- }
486
-
487
- public async fulltextSearch(keyword: string, imageSize: ImageSize | undefined) {
488
- const langAbbr = this.ensureLangAbbr();
489
- if (!imageSize) {
490
- imageSize = this.imageSizes.postThumbnial;
491
- }
492
- return await fulltextSearch(keyword, imageSize, this.envVariables, langAbbr);
493
- }
494
-
495
- public async getUserPaths() {
496
- const result = await queryUserIds(this.envVariables);
497
- return (
498
- result?.items?.map((user) => ({
499
- params: { id: user.id },
500
- props: { id: user.id },
501
- })) || []
502
- );
503
- }
504
-
505
- public createPagination(totalItems: number, pageSize: number) {
506
- const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
507
-
508
- // 为每个页面创建路径和获取对应的文章数据
509
- const paths = [];
510
-
511
- for (let i = 0; i < totalPages; i++) {
512
- const currentPage = i + 1;
513
-
514
- paths.push({
515
- params: { page: String(currentPage) },
516
- props: {
517
- currentPage,
518
- totalPages,
519
- pageSize,
520
- totalItems,
521
- },
522
- });
523
- }
524
-
525
- return paths;
526
- }
527
-
528
- public async createUploadCredentials(
529
- options: UploadOptions
530
- ): Promise<UploadSession | null> {
531
- return await createUploadCredentials(options, this.envVariables);
532
- }
533
- }
1
+ import { EnvVariables } from "./types";
2
+ import { queryOneTheme } from "./lib/queryOneTheme";
3
+ import {
4
+ ListConditions,
5
+ queryEntityList,
6
+ queryFeaturedProducts,
7
+ queryLangs,
8
+ queryLatestPosts,
9
+ queryOnePostBySlug,
10
+ queryOnePostCategoryBySlug,
11
+ queryOneProductBySlug,
12
+ queryOneProductCategoryBySlug,
13
+ queryOneUser,
14
+ queryPostCategories,
15
+ queryPosts,
16
+ queryProductCategories,
17
+ queryProducts,
18
+ queryTagCategories,
19
+ queryTags,
20
+ queryUserPosts,
21
+ fulltextSearch,
22
+ queryOneEntity,
23
+ createUploadCredentials,
24
+ UploadOptions,
25
+ UploadSession,
26
+ } from "./lib";
27
+ import { IQueryOptions } from "@rxdrag/entify-lib";
28
+ import { queryAllProducts } from "./lib/queryAllProducts";
29
+ import { queryPostSlugs } from "./lib/queryPostSlugs";
30
+ import { queryUserIds } from "./lib/queryUserIds";
31
+ import { IEntify, PostPatinateOptions, PostsOptions } from "./IEntify";
32
+ import { queryWebsite } from "./lib/queryWebsite";
33
+ import {
34
+ ImageSize,
35
+ ImageSizes,
36
+ Product,
37
+ WebsitePart,
38
+ WebsitePartBoolExp,
39
+ } from "@rxdrag/rxcms-models";
40
+ import { queryOneMedia } from "./lib/queryOneMedia";
41
+ import { FileFieldType, ImageResize } from "../astro/media";
42
+ import { queryBulletin } from "./lib/queryBulletin";
43
+
44
+ export class Entify implements IEntify {
45
+ private static instance: Entify | null = null;
46
+
47
+ private constructor(protected envVariables: EnvVariables, protected imageSizes: ImageSizes) { }
48
+ private flattenTree<T>(nodes: T[] | undefined): T[] {
49
+ if (!nodes) return [];
50
+ let result: T[] = [];
51
+ for (const node of nodes) {
52
+ result.push(node);
53
+ const children = (node as { children?: T[] }).children;
54
+ if (children && children.length > 0) {
55
+ result = result.concat(this.flattenTree(children));
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+
61
+ /**
62
+ * 获取 Entify 单例实例(无参数,获取已创建的实例)
63
+ */
64
+ public static getInstance(): Entify;
65
+ /**
66
+ * 获取 Entify 单例实例(带参数,创建或更新实例)
67
+ */
68
+ public static getInstance(envVariables: EnvVariables, imageSizes: ImageSizes): Entify;
69
+ public static getInstance(envVariables?: EnvVariables, imageSizes?: ImageSizes): Entify {
70
+ // 无参数调用:获取已创建的实例
71
+ if (envVariables === undefined && imageSizes === undefined) {
72
+ if (!Entify.instance) {
73
+ throw new Error(
74
+ 'Entify instance not initialized. Please call getInstance(envVariables, imageSizes) first.'
75
+ );
76
+ }
77
+ return Entify.instance;
78
+ }
79
+
80
+ // 有参数调用:创建或更新实例
81
+ if (!Entify.instance) {
82
+ Entify.instance = new Entify(envVariables!, imageSizes!);
83
+ } else {
84
+ Entify.instance.envVariables = envVariables!;
85
+ Entify.instance.imageSizes = imageSizes!;
86
+ }
87
+ return Entify.instance;
88
+ }
89
+
90
+ private currentLangAbbr?: string;
91
+
92
+ /**
93
+ * 设置当前语言
94
+ */
95
+ public setLangAbbr(langAbbr: string): void {
96
+ this.currentLangAbbr = langAbbr;
97
+ }
98
+
99
+ /**
100
+ * 获取当前设置的语言缩写
101
+ */
102
+ public getLang(): string {
103
+ return this.ensureLangAbbr();
104
+ }
105
+
106
+ /**
107
+ * 获取当前语言,如果未设置则抛出异常
108
+ */
109
+ private ensureLangAbbr(): string {
110
+ if (!this.currentLangAbbr) {
111
+ throw new Error(
112
+ 'langAbbr is not set. Please call setLangAbbr() before using language-dependent methods.'
113
+ );
114
+ }
115
+ return this.currentLangAbbr;
116
+ }
117
+
118
+ public setImageSizes(imageSizes: ImageSizes): void {
119
+ this.imageSizes = imageSizes;
120
+ }
121
+
122
+ public getImageSizes(): ImageSizes {
123
+ return this.imageSizes;
124
+ }
125
+
126
+ public async queryEntityList<
127
+ T,
128
+ WhereExp = unknown,
129
+ OrderBy = unknown,
130
+ DistinctExp = unknown
131
+ >(options: IQueryOptions<T, WhereExp, OrderBy, DistinctExp>) {
132
+ return await queryEntityList<T, WhereExp, OrderBy, DistinctExp>(
133
+ options,
134
+ this.envVariables
135
+ );
136
+ }
137
+
138
+ public async queryOneEntity<T extends WebsitePart = WebsitePart>(
139
+ options: IQueryOptions<T, WebsitePartBoolExp>
140
+ ) {
141
+ return await queryOneEntity<T, WebsitePartBoolExp>(
142
+ options,
143
+ this.envVariables
144
+ );
145
+ }
146
+
147
+ public async getWebsite() {
148
+ return queryWebsite(this.envVariables);
149
+ }
150
+
151
+ public async getTheme() {
152
+ return await queryOneTheme(this.envVariables);
153
+ }
154
+
155
+ public async getLangs() {
156
+ return (await queryLangs(this.envVariables))?.items;
157
+ }
158
+
159
+ public async getBaseLang() {
160
+ const website = await this.getWebsite();
161
+ return website?.baseLang;
162
+ }
163
+
164
+ public async getMedia(ref: string | undefined | null, fileField: FileFieldType = 'thumbnail', resize?: ImageResize) {
165
+ const fields = [fileField];
166
+ return await queryOneMedia(ref, fields, this.envVariables);
167
+ }
168
+
169
+ public async getBulletin() {
170
+ const langAbbr = this.ensureLangAbbr();
171
+ return await queryBulletin(this.envVariables, langAbbr);
172
+ }
173
+
174
+
175
+ public async getFeaturedProducts(
176
+ count: number | undefined,
177
+ imageSize: ImageSize | undefined,
178
+ addonFields: (keyof Product)[] | undefined
179
+ ) {
180
+ const langAbbr = this.ensureLangAbbr();
181
+ if (!imageSize) {
182
+ imageSize = this.imageSizes?.productThumbnial;
183
+ }
184
+ return await queryFeaturedProducts(
185
+ count,
186
+ imageSize,
187
+ this.envVariables,
188
+ addonFields,
189
+ langAbbr
190
+ );
191
+ }
192
+
193
+ public async getLatestPosts(count: number | undefined, coverSize: ImageSize | undefined) {
194
+ const langAbbr = this.ensureLangAbbr();
195
+ if (!coverSize) {
196
+ coverSize = this.imageSizes?.postThumbnial;
197
+ }
198
+ return await queryLatestPosts(count, coverSize, this.envVariables, langAbbr);
199
+ }
200
+
201
+
202
+ public async getPosts(options: PostsOptions) {
203
+ const langAbbr = this.ensureLangAbbr();
204
+ const { category, coverSize, page, pageSize } = options;
205
+ const conditions: ListConditions = { category, page, pageSize };
206
+ const result = await queryPosts(conditions, coverSize, this.envVariables, langAbbr);
207
+ return result?.items;
208
+ }
209
+
210
+ public async getPostListPaths(options: PostPatinateOptions) {
211
+ const langAbbr = this.ensureLangAbbr();
212
+ const { category, pageSize } = options;
213
+ // 获取总文章数据,只需要获取总数
214
+ // TODO: 目前数据查询有点重复,后续要结合后端优化
215
+ const postsData = await queryPosts(
216
+ { category, page: 1, pageSize: 10 },
217
+ undefined,
218
+ this.envVariables,
219
+ langAbbr
220
+ );
221
+
222
+ const totalItems = postsData?.total ?? 0;
223
+
224
+ return this.createPagination(totalItems, pageSize);
225
+ }
226
+
227
+ public async getPostPaths() {
228
+ // 获取所有文章的 slug
229
+ const slugs = await this.getPostSlugs();
230
+
231
+ // 为每个 slug 创建路径
232
+ return slugs.map((slug) => ({
233
+ params: { slug },
234
+ props: { slug },
235
+ }));
236
+ }
237
+
238
+ public async getPostBySlug(slug: string, coverSize: ImageSize | undefined) {
239
+ const langAbbr = this.ensureLangAbbr();
240
+ if (!coverSize) {
241
+ coverSize = this.imageSizes.post;
242
+ }
243
+ return await queryOnePostBySlug(slug, coverSize, this.envVariables, langAbbr);
244
+ }
245
+
246
+ /**
247
+ * 获取所有文章的 slug,用于生成静态页面
248
+ * 对于大量文章,这个方法会进行优化,只获取 slug 字段
249
+ */
250
+ public async getPostSlugs() {
251
+ const langAbbr = this.ensureLangAbbr();
252
+ // 只获取 slug 字段,减少数据传输量
253
+ const result = await queryPostSlugs(this.envVariables, langAbbr);
254
+
255
+ return result?.items?.map((post) => post.slug) || [];
256
+ }
257
+
258
+ public async getPostCategories() {
259
+ const langAbbr = this.ensureLangAbbr();
260
+ return await queryPostCategories(this.envVariables, langAbbr);
261
+ }
262
+
263
+ public async getPostCategoryBySlug(slug: string) {
264
+ const langAbbr = this.ensureLangAbbr();
265
+ return await queryOnePostCategoryBySlug(slug, this.envVariables, langAbbr);
266
+ }
267
+
268
+ public async getTagCategories() {
269
+ const langAbbr = this.ensureLangAbbr();
270
+ return await queryTagCategories(this.envVariables, langAbbr);
271
+ }
272
+
273
+ public async getTags(categoryId: string | undefined) {
274
+ const langAbbr = this.ensureLangAbbr();
275
+ return await queryTags(this.envVariables, categoryId, langAbbr);
276
+ }
277
+
278
+ public async getProducts(
279
+ conditions: ListConditions,
280
+ imageSize: ImageSize | undefined,
281
+ addonFields?: (keyof Product)[] | undefined
282
+ ) {
283
+ const langAbbr = this.ensureLangAbbr();
284
+ if (!imageSize) {
285
+ imageSize = this.imageSizes.productThumbnial;
286
+ }
287
+ return (
288
+ await queryProducts(conditions, imageSize, this.envVariables, addonFields, langAbbr)
289
+ )?.items;
290
+ }
291
+
292
+ public async getProductListPaths(options: {
293
+ category?: string;
294
+ pageSize: number;
295
+ }) {
296
+ const langAbbr = this.ensureLangAbbr();
297
+ const { category, pageSize } = options;
298
+ // 获取总产品数据,只需要获取总数
299
+ const productsData = await queryProducts(
300
+ { category, page: 1, pageSize: 10 },
301
+ undefined,
302
+ this.envVariables,
303
+ undefined,
304
+ langAbbr
305
+ );
306
+
307
+ const totalItems = productsData?.total ?? 0;
308
+
309
+ return this.createPagination(totalItems, pageSize);
310
+ }
311
+
312
+ public async getCategoredProductListPaths() {
313
+ const langAbbr = this.ensureLangAbbr();
314
+ // 获取所有产品分类
315
+ const categoryTree = await this.getProductCategories();
316
+ const categories = this.flattenTree(categoryTree);
317
+
318
+ // 为每个分类生成路径
319
+ const paths = [];
320
+
321
+ // 如果没有分类,至少创建一个默认路径
322
+ if (!categories || categories.length === 0) {
323
+ paths.push({
324
+ params: { slug: "default", page: "1" },
325
+ props: {
326
+ currentPage: 1,
327
+ totalPages: 1,
328
+ pageSize: 12,
329
+ totalItems: 0,
330
+ slug: "default",
331
+ },
332
+ });
333
+ return paths;
334
+ }
335
+
336
+ for (const category of categories) {
337
+ // 跳过没有 slug 的分类
338
+ if (!category.slug) continue;
339
+
340
+ // 获取该分类下的分页路径
341
+ const categoryPaths = await this.getProductListPaths({
342
+ pageSize: 12,
343
+ category: category.slug,
344
+ });
345
+
346
+ // 如果该分类没有产品,至少创建一个第一页
347
+ if (categoryPaths.length === 0) {
348
+ paths.push({
349
+ params: { slug: String(category.slug), page: "1" },
350
+ props: {
351
+ currentPage: 1,
352
+ totalPages: 1,
353
+ pageSize: 12,
354
+ totalItems: 0,
355
+ slug: String(category.slug),
356
+ },
357
+ });
358
+ continue;
359
+ }
360
+
361
+ // 为每个分页路径添加分类 slug
362
+ for (const path of categoryPaths) {
363
+ paths.push({
364
+ params: { ...path.params, slug: String(category.slug) },
365
+ props: { ...path.props, slug: String(category.slug) },
366
+ });
367
+ }
368
+ }
369
+
370
+ return paths;
371
+ }
372
+
373
+ /**
374
+ * 获取所有文章分类的分页路径
375
+ * 用于生成静态页面
376
+ * @returns 所有文章分类的分页路径
377
+ */
378
+ public async getCategoredPostListPaths() {
379
+ const langAbbr = this.ensureLangAbbr();
380
+ // 获取所有文章分类
381
+ const categoryTree = await this.getPostCategories();
382
+ const categories = this.flattenTree(categoryTree);
383
+
384
+ // 为每个分类生成路径
385
+ const paths = [];
386
+
387
+ // 如果没有分类,至少创建一个默认路径
388
+ if (!categories || categories.length === 0) {
389
+ paths.push({
390
+ params: { slug: "default", page: "1" },
391
+ props: {
392
+ currentPage: 1,
393
+ totalPages: 1,
394
+ pageSize: 10,
395
+ totalItems: 0,
396
+ slug: "default",
397
+ },
398
+ });
399
+ return paths;
400
+ }
401
+
402
+ for (const category of categories) {
403
+ // 跳过没有 slug 的分类
404
+ if (!category.slug) continue;
405
+
406
+ // 获取该分类下的分页路径
407
+ const categoryPaths = await this.getPostListPaths({
408
+ pageSize: 10,
409
+ category: category.slug,
410
+ });
411
+
412
+ // 如果该分类没有文章,至少创建一个第一页
413
+ if (categoryPaths.length === 0) {
414
+ paths.push({
415
+ params: { slug: String(category.slug), page: "1" },
416
+ props: {
417
+ currentPage: 1,
418
+ totalPages: 1,
419
+ pageSize: 10,
420
+ totalItems: 0,
421
+ slug: String(category.slug),
422
+ },
423
+ });
424
+ continue;
425
+ }
426
+
427
+ // 为每个分页路径添加分类 slug
428
+ for (const path of categoryPaths) {
429
+ paths.push({
430
+ params: { ...path.params, slug: String(category.slug) },
431
+ props: { ...path.props, slug: String(category.slug) },
432
+ });
433
+ }
434
+ }
435
+
436
+ return paths;
437
+ }
438
+
439
+ public async getProductBySlug(slug: string, imageSize: ImageSize | undefined) {
440
+ const langAbbr = this.ensureLangAbbr();
441
+ if (!imageSize) {
442
+ imageSize = this.imageSizes.product;
443
+ }
444
+ return await queryOneProductBySlug(slug, imageSize, this.envVariables, langAbbr);
445
+ }
446
+
447
+ public async getProductPaths() {
448
+ // 获取所有产品的 slug
449
+ const slugs = await this.getProductSlugs();
450
+
451
+ // 为每个 slug 创建路径
452
+ return slugs.map((slug) => ({
453
+ params: { slug },
454
+ props: { slug },
455
+ }));
456
+ }
457
+
458
+ /**
459
+ * 获取所有产品的 slug,用于生成静态页面
460
+ * 对于大量产品,这个方法会进行优化,只获取 slug 字段
461
+ */
462
+ public async getProductSlugs() {
463
+ const langAbbr = this.ensureLangAbbr();
464
+ // 只获取 slug 字段,减少数据传输量
465
+ const result = await queryAllProducts(this.envVariables, langAbbr);
466
+ return result?.items?.map((product) => product.slug) || [];
467
+ }
468
+
469
+ public async getProductIds() {
470
+ const langAbbr = this.ensureLangAbbr();
471
+ const result = await queryAllProducts(this.envVariables, langAbbr);
472
+ return result?.items?.map((product) => product.id) || [];
473
+ }
474
+
475
+ public async getProductCategories() {
476
+ const langAbbr = this.ensureLangAbbr();
477
+ return await queryProductCategories(this.envVariables, langAbbr);
478
+ }
479
+
480
+ public async getProductCategoryBySlug(slug: string) {
481
+ const langAbbr = this.ensureLangAbbr();
482
+ return await queryOneProductCategoryBySlug(slug, this.envVariables, langAbbr);
483
+ }
484
+
485
+ public async getOneUser(id: string) {
486
+ return await queryOneUser(id, this.envVariables);
487
+ }
488
+
489
+ public async getUserPosts(userId: string) {
490
+ const langAbbr = this.ensureLangAbbr();
491
+ return (await queryUserPosts({ userId }, this.envVariables, langAbbr))?.items;
492
+ }
493
+
494
+ public async fulltextSearch(keyword: string, imageSize: ImageSize | undefined) {
495
+ const langAbbr = this.ensureLangAbbr();
496
+ if (!imageSize) {
497
+ imageSize = this.imageSizes.postThumbnial;
498
+ }
499
+ return await fulltextSearch(keyword, imageSize, this.envVariables, langAbbr);
500
+ }
501
+
502
+ public async getUserPaths() {
503
+ const result = await queryUserIds(this.envVariables);
504
+ return (
505
+ result?.items?.map((user) => ({
506
+ params: { id: user.id },
507
+ props: { id: user.id },
508
+ })) || []
509
+ );
510
+ }
511
+
512
+ public createPagination(totalItems: number, pageSize: number) {
513
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
514
+
515
+ // 为每个页面创建路径和获取对应的文章数据
516
+ const paths = [];
517
+
518
+ for (let i = 0; i < totalPages; i++) {
519
+ const currentPage = i + 1;
520
+
521
+ paths.push({
522
+ params: { page: String(currentPage) },
523
+ props: {
524
+ currentPage,
525
+ totalPages,
526
+ pageSize,
527
+ totalItems,
528
+ },
529
+ });
530
+ }
531
+
532
+ return paths;
533
+ }
534
+
535
+ public async createUploadCredentials(
536
+ options: UploadOptions
537
+ ): Promise<UploadSession | null> {
538
+ return await createUploadCredentials(options, this.envVariables);
539
+ }
540
+ }
@@ -65,6 +65,12 @@ export interface IEntify {
65
65
  */
66
66
  setLangAbbr(langAbbr: string): void;
67
67
 
68
+ /**
69
+ * 获取当前设置的语言缩写
70
+ * @returns 语言缩写,如 "en-US" 或 "zh-CN"
71
+ */
72
+ getLang(): string;
73
+
68
74
  getBaseLang(): Promise<Lang | undefined>;
69
75
 
70
76
  getMedia(ref: string | undefined | null, fileField?: FileFieldType, resize?: ImageResize): Promise<Media | undefined>;
@@ -7,7 +7,7 @@ import { queryEntityList } from "./queryEntityList";
7
7
  import { EnvVariables } from "../types";
8
8
  import { newQueryProductsMediaOptions } from "./newQueryProductsMediaOptions";
9
9
 
10
- export async function queryProductsInMenu(envVariables: EnvVariables) {
10
+ export async function queryProductsInMenu(envVariables: EnvVariables, langAbbr: string) {
11
11
  const result = await queryEntityList(
12
12
  new ProductQueryOptions(
13
13
  [ProductFields.id, ProductFields.slug, ProductFields.title],
@@ -15,7 +15,7 @@ export async function queryProductsInMenu(envVariables: EnvVariables) {
15
15
  where: {
16
16
  lang: {
17
17
  abbr: {
18
- _eq: envVariables.language,
18
+ _eq: langAbbr,
19
19
  },
20
20
  },
21
21
  _or: [
@@ -1,5 +1,5 @@
1
- export type EnvVariables = {
2
- websiteId?: string;
3
- entifyServerUrl?: string;
4
- entifyGuestToken?: string;
5
- };
1
+ export type EnvVariables = {
2
+ websiteId?: string;
3
+ entifyServerUrl?: string;
4
+ entifyGuestToken?: string;
5
+ };