@rxdrag/website-lib-core 0.0.127 → 0.0.128

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.
Files changed (138) hide show
  1. package/README.md +1 -1
  2. package/index.ts +1 -1
  3. package/package.json +4 -4
  4. package/src/astro/animation.ts +146 -146
  5. package/src/astro/background.ts +82 -86
  6. package/src/astro/base.ts +7 -0
  7. package/src/astro/business.ts +13 -0
  8. package/src/astro/grid/consts.ts +80 -80
  9. package/src/astro/grid/index.ts +2 -2
  10. package/src/astro/grid/types.ts +35 -35
  11. package/src/astro/image.ts +239 -239
  12. package/src/astro/index.ts +12 -10
  13. package/src/astro/link.ts +20 -21
  14. package/src/astro/media.ts +123 -123
  15. package/src/astro/nav.ts +13 -13
  16. package/src/astro/section/index.ts +7 -12
  17. package/src/component-logic/index.ts +1 -1
  18. package/src/component-logic/link-client.ts +32 -32
  19. package/src/component-logic/link.ts +61 -61
  20. package/src/design-tokens.ts +160 -160
  21. package/src/entify/Entify.ts +533 -530
  22. package/src/entify/IEntify.ts +151 -177
  23. package/src/entify/index.ts +4 -4
  24. package/src/entify/lib/collectCategoryIds.ts +20 -20
  25. package/src/entify/lib/fulltextSearch.ts +63 -62
  26. package/src/entify/lib/langFields.ts +14 -14
  27. package/src/entify/lib/listToTree.ts +23 -23
  28. package/src/entify/lib/newAvatarQueryOptions.ts +4 -4
  29. package/src/entify/lib/newOgImageQueryOptions.ts +5 -5
  30. package/src/entify/lib/newQueryPostOptions.ts +42 -45
  31. package/src/entify/lib/newQueryProductOptions.ts +96 -98
  32. package/src/entify/lib/newQueryProductsMediaOptions.ts +28 -28
  33. package/src/entify/lib/queryAllProducts.ts +40 -40
  34. package/src/entify/lib/queryBulletin.ts +28 -16
  35. package/src/entify/lib/queryEntityList.ts +41 -41
  36. package/src/entify/lib/queryFeaturedProducts.ts +69 -68
  37. package/src/entify/lib/queryLangs.ts +36 -60
  38. package/src/entify/lib/queryLatestPosts.ts +92 -91
  39. package/src/entify/lib/queryOneEntity.ts +63 -63
  40. package/src/entify/lib/queryOneMedia.ts +27 -27
  41. package/src/entify/lib/queryOnePostById.ts +21 -21
  42. package/src/entify/lib/queryOnePostBySlug.ts +40 -39
  43. package/src/entify/lib/queryOnePostCategoryBySlug.ts +35 -35
  44. package/src/entify/lib/queryOneProductById.ts +20 -20
  45. package/src/entify/lib/queryOneProductBySlug.ts +53 -52
  46. package/src/entify/lib/queryOneProductCategoryBySlug.ts +50 -49
  47. package/src/entify/lib/queryOneTheme.ts +54 -72
  48. package/src/entify/lib/queryOneUser.ts +38 -38
  49. package/src/entify/lib/queryPostCategories.ts +67 -67
  50. package/src/entify/lib/queryPostSlugs.ts +37 -37
  51. package/src/entify/lib/queryPosts.ts +175 -174
  52. package/src/entify/lib/queryProductCategories.ts +59 -59
  53. package/src/entify/lib/queryProducts.ts +145 -144
  54. package/src/entify/lib/queryProductsInMenu.ts +45 -45
  55. package/src/entify/lib/queryTagCategories.ts +58 -58
  56. package/src/entify/lib/queryTags.ts +57 -57
  57. package/src/entify/lib/queryUserIds.ts +24 -24
  58. package/src/entify/lib/queryUserPosts.ts +80 -79
  59. package/src/entify/lib/queryWebSiteSettings.ts +28 -28
  60. package/src/entify/lib/queryWebsite.ts +43 -43
  61. package/src/entify/lib/sendEmail.ts +7 -7
  62. package/src/entify/lib/toQueryOptions.ts +19 -19
  63. package/src/entify/lib/upsertEntity.ts +8 -8
  64. package/src/entify/types/index.ts +1 -1
  65. package/src/entify/types/utils.ts +11 -6
  66. package/src/entify/types/variables.ts +5 -6
  67. package/src/entify/view-model/funcs.ts +230 -230
  68. package/src/entify/view-model/index.ts +1 -1
  69. package/src/entify/view-model/models.ts +135 -135
  70. package/src/global.d.ts +7 -7
  71. package/src/index.ts +8 -8
  72. package/src/lib/formatDate.ts +15 -15
  73. package/src/lib/index.ts +3 -3
  74. package/src/lib/pagination.ts +114 -114
  75. package/src/lib/utils.ts +135 -135
  76. package/src/react/components/Analytics/eventHandlers.ts +173 -173
  77. package/src/react/components/Analytics/index.tsx +21 -21
  78. package/src/react/components/Analytics/singleton.ts +214 -214
  79. package/src/react/components/Analytics/tracking.ts +221 -221
  80. package/src/react/components/Analytics/types.ts +60 -60
  81. package/src/react/components/Analytics/utils.ts +95 -95
  82. package/src/react/components/AttachmentIcon/index.tsx +53 -53
  83. package/src/react/components/BackgroundHlsVideoPlayer.tsx +97 -97
  84. package/src/react/components/BackgroundVideoPlayer.tsx +32 -32
  85. package/src/react/components/Bulletin.tsx +30 -30
  86. package/src/react/components/ContactForm/ContactForm.tsx +289 -289
  87. package/src/react/components/ContactForm/Input.tsx +48 -48
  88. package/src/react/components/ContactForm/Input2.tsx +59 -59
  89. package/src/react/components/ContactForm/Submit.tsx +48 -48
  90. package/src/react/components/ContactForm/TelInput.tsx +215 -215
  91. package/src/react/components/ContactForm/Textarea.tsx +48 -48
  92. package/src/react/components/ContactForm/Textarea2.tsx +89 -89
  93. package/src/react/components/ContactForm/funcs.ts +64 -64
  94. package/src/react/components/ContactForm/index.ts +7 -7
  95. package/src/react/components/ContactForm/types.ts +68 -68
  96. package/src/react/components/GoogleConsent/CookieItemPanel.tsx +80 -80
  97. package/src/react/components/GoogleConsent/CumtomizedModal.tsx +148 -148
  98. package/src/react/components/GoogleConsent/GoogleConsent.tsx +100 -100
  99. package/src/react/components/GoogleConsent/gtags.ts +67 -67
  100. package/src/react/components/GoogleConsent/index.ts +2 -2
  101. package/src/react/components/GoogleConsent/types.ts +18 -18
  102. package/src/react/components/GoogleConsent//345/217/202/350/200/203.md +4 -4
  103. package/src/react/components/Icon/index.tsx +19 -19
  104. package/src/react/components/Medias/MainMedia.tsx +257 -257
  105. package/src/react/components/Medias/Thumbnail.tsx +62 -62
  106. package/src/react/components/Medias/VideoPlayer.tsx +114 -114
  107. package/src/react/components/Medias/index.tsx +271 -271
  108. package/src/react/components/ProductCard/ProductCard.tsx +24 -24
  109. package/src/react/components/ProductCard/ProductCta/index.tsx +28 -28
  110. package/src/react/components/ProductCard/ProductCta/style.css +3 -3
  111. package/src/react/components/ProductCard/ProductDescription/index.tsx +12 -12
  112. package/src/react/components/ProductCard/ProductDescription/style.css +5 -5
  113. package/src/react/components/ProductCard/ProductMedia/index.tsx +35 -35
  114. package/src/react/components/ProductCard/ProductMedia/style.css +5 -5
  115. package/src/react/components/ProductCard/ProductTitle/index.tsx +7 -7
  116. package/src/react/components/ProductCard/ProductTitle/style.css +3 -3
  117. package/src/react/components/ProductCard/ProductView.tsx +36 -36
  118. package/src/react/components/ProductCard/index.ts +4 -4
  119. package/src/react/components/ProductCard/useQueryProduct.ts +32 -32
  120. package/src/react/components/ReactModalTrigger.tsx +28 -28
  121. package/src/react/components/ReactVideoPlayer.tsx +29 -52
  122. package/src/react/components/RichTextOutline/index.tsx +75 -75
  123. package/src/react/components/RichTextOutline/useAnchorScroll.ts +23 -23
  124. package/src/react/components/Scroller.tsx +39 -39
  125. package/src/react/components/SearchInput.tsx +21 -21
  126. package/src/react/components/Share/index.tsx +86 -86
  127. package/src/react/components/Share/socials.tsx +79 -77
  128. package/src/react/components/Share//350/265/204/346/226/231.md +7 -7
  129. package/src/react/components/ToTop.tsx +72 -72
  130. package/src/react/components/VideoPlayIcon.tsx +43 -0
  131. package/src/react/components/all.ts +38 -38
  132. package/src/react/components/index.ts +16 -16
  133. package/src/robots.ts +4 -4
  134. package/src/entify/lib/newPageMetaOptions.ts +0 -18
  135. package/src/entify/lib/newQueryPageOptions.ts +0 -14
  136. package/src/entify/lib/queryOneIcon.ts +0 -27
  137. package/src/entify/lib/queryPageBySlug.ts +0 -43
  138. package/src/entify/lib/queryPageByType.ts +0 -44
@@ -1,530 +1,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
- Media,
37
- Page,
38
- PageType,
39
- Product,
40
- WebsitePart,
41
- WebsitePartBoolExp,
42
- } from "@rxdrag/rxcms-models";
43
- import { queryPageBySlug } from "./lib/queryPageBySlug";
44
- import { queryPageByType } from "./lib/queryPageByType";
45
- import { queryOneMedia } from "./lib/queryOneMedia";
46
- import { DEFAULT_LARGE, DEFAULT_MEDIUM, DEFAULT_SAMLL, DEFUALT_HEIGHT, DEFUALT_WIDTH, FileFieldType, ImageResize } from "../astro/media";
47
- import { queryOneIcon } from "./lib/queryOneIcon";
48
- import { queryBulletin } from "./lib/queryBulletin";
49
-
50
- export class Entify implements IEntify {
51
- private static instance: Entify | null = null;
52
-
53
- private constructor(protected envVariables: EnvVariables, protected imageSizes: ImageSizes) { }
54
- private flattenTree<T>(nodes: T[] | undefined): T[] {
55
- if (!nodes) return [];
56
- let result: T[] = [];
57
- for (const node of nodes) {
58
- result.push(node);
59
- const children = (node as { children?: T[] }).children;
60
- if (children && children.length > 0) {
61
- result = result.concat(this.flattenTree(children));
62
- }
63
- }
64
- return result;
65
- }
66
-
67
- public static getInstance(envVariables: EnvVariables, imageSizes: ImageSizes): Entify {
68
- if (!Entify.instance) {
69
- Entify.instance = new Entify(envVariables, imageSizes);
70
- }
71
- return Entify.instance;
72
- }
73
-
74
- public setImageSizes(imageSizes: ImageSizes): void {
75
- this.imageSizes = imageSizes;
76
- }
77
-
78
- public getImageSizes(): ImageSizes {
79
- return this.imageSizes;
80
- }
81
-
82
- public async queryEntityList<
83
- T,
84
- WhereExp = unknown,
85
- OrderBy = unknown,
86
- DistinctExp = unknown
87
- >(options: IQueryOptions<T, WhereExp, OrderBy, DistinctExp>) {
88
- return await queryEntityList<T, WhereExp, OrderBy, DistinctExp>(
89
- options,
90
- this.envVariables
91
- );
92
- }
93
-
94
- public async queryOneEntity<T extends WebsitePart = WebsitePart>(
95
- options: IQueryOptions<T, WebsitePartBoolExp>
96
- ) {
97
- return await queryOneEntity<T, WebsitePartBoolExp>(
98
- options,
99
- this.envVariables
100
- );
101
- }
102
-
103
- public getLang() {
104
- return this.envVariables.language || "en-US";
105
- }
106
-
107
- public async getWebsite() {
108
- return queryWebsite(this.envVariables);
109
- }
110
-
111
- public async getTheme() {
112
- return await queryOneTheme(this.envVariables);
113
- }
114
-
115
- public async getLangs() {
116
- return (await queryLangs(this.envVariables))?.items;
117
- }
118
-
119
- public async getMedia(ref: string | undefined | null, fileField?: FileFieldType, resize?: ImageResize): Promise<Media | undefined> {
120
- const fields: string[] = []
121
- switch (fileField) {
122
- case "resize":
123
- fields.push(`resize(width: ${resize?.width || DEFUALT_WIDTH}, height: ${resize?.height || DEFUALT_HEIGHT})`)
124
- break;
125
- case "small":
126
- fields.push(`small(width: ${this.imageSizes.small?.width || DEFAULT_SAMLL.width}, height: ${this.imageSizes.small?.height || DEFAULT_SAMLL.height})`)
127
- break;
128
- case "medium":
129
- fields.push(`medium(width: ${this.imageSizes.medium?.width || DEFAULT_MEDIUM.width}, height: ${this.imageSizes.medium?.height || DEFAULT_MEDIUM.height})`)
130
- break;
131
- case "large":
132
- fields.push(`large(width: ${this.imageSizes.large?.width || DEFAULT_LARGE.width}, height: ${this.imageSizes.large?.height || DEFAULT_LARGE.height})`)
133
- break;
134
- }
135
- return await queryOneMedia(ref, fields, this.envVariables);
136
- }
137
-
138
- public async getIcon(name: string | undefined | null) {
139
- return await queryOneIcon(name, this.envVariables);
140
- }
141
-
142
- public async getBulletin() {
143
- return await queryBulletin(this.envVariables);
144
- }
145
-
146
-
147
- public async getFeaturedProducts(
148
- count?: number,
149
- imageSize?: ImageSize,
150
- addonFields?: (keyof Product)[]
151
- ) {
152
- if (!imageSize) {
153
- imageSize = this.imageSizes?.productThumbnial;
154
- }
155
- return await queryFeaturedProducts(
156
- count,
157
- imageSize,
158
- this.envVariables,
159
- addonFields
160
- );
161
- }
162
-
163
- public async getLatestPosts(count?: number, coverSize?: ImageSize) {
164
- if (!coverSize) {
165
- coverSize = this.imageSizes?.postThumbnial;
166
- }
167
- return await queryLatestPosts(count, coverSize, this.envVariables);
168
- }
169
-
170
- public async getHomePage(): Promise<Page | undefined> {
171
- return await this.getPageByType(PageType.Home);
172
- }
173
- public async getPostPage(): Promise<Page | undefined> {
174
- return await this.getPageByType(PageType.Post);
175
- }
176
- public async getPostListPage(): Promise<Page | undefined> {
177
- return await this.getPageByType(PageType.PostList);
178
- }
179
- public async getPostCategoryPage(): Promise<Page | undefined> {
180
- return await this.getPageByType(PageType.PostCategory);
181
- }
182
- public async getProductPage(): Promise<Page | undefined> {
183
- return await this.getPageByType(PageType.Product);
184
- }
185
- public async getProductListPage(): Promise<Page | undefined> {
186
- return await this.getPageByType(PageType.ProductList);
187
- }
188
- public async getProductCategoryPage(): Promise<Page | undefined> {
189
- return await this.getPageByType(PageType.ProductCategory);
190
- }
191
- public async getSearchPage(): Promise<Page | undefined> {
192
- return await this.getPageByType(PageType.SearchList);
193
- }
194
- public async getProfilePage(): Promise<Page | undefined> {
195
- return await this.getPageByType(PageType.Profile);
196
- }
197
- public async getThanksPage(): Promise<Page | undefined> {
198
- return await this.getPageByType(PageType.Thanks);
199
- }
200
- public async get404Page(): Promise<Page | undefined> {
201
- return await this.getPageByType(PageType.Page404);
202
- }
203
- public async getErrorPage(): Promise<Page | undefined> {
204
- return await this.getPageByType(PageType.PageError);
205
- }
206
- public async getPageBySlug(slug: string): Promise<Page | undefined> {
207
- return await queryPageBySlug(slug, this.envVariables);
208
- }
209
-
210
- public async getPageByType(pageType: PageType): Promise<Page | undefined> {
211
- return await queryPageByType(pageType, this.envVariables);
212
- }
213
-
214
- public async getPosts(options: PostsOptions) {
215
- const { category, coverSize, page, pageSize } = options;
216
- const conditions: ListConditions = { category, page, pageSize };
217
- const result = await queryPosts(conditions, coverSize, this.envVariables);
218
- return result?.items;
219
- }
220
-
221
- public async getPostListPaths(options: PostPatinateOptions) {
222
- const { category, pageSize } = options;
223
- // 获取总文章数据,只需要获取总数
224
- // TODO: 目前数据查询有点重复,后续要结合后端优化
225
- const postsData = await queryPosts(
226
- { category, page: 1, pageSize: 10 },
227
- undefined,
228
- this.envVariables
229
- );
230
-
231
- const totalItems = postsData?.total ?? 0;
232
-
233
- return this.createPagination(totalItems, pageSize);
234
- }
235
-
236
- public async getPostPaths() {
237
- // 获取所有文章的 slug
238
- const slugs = await this.getPostSlugs();
239
-
240
- // 为每个 slug 创建路径
241
- return slugs.map((slug) => ({
242
- params: { slug },
243
- props: { slug },
244
- }));
245
- }
246
-
247
- public async getPostBySlug(slug: string, coverSize: ImageSize | undefined) {
248
- if (!coverSize) {
249
- coverSize = this.imageSizes.post;
250
- }
251
- return await queryOnePostBySlug(slug, coverSize, this.envVariables);
252
- }
253
-
254
- /**
255
- * 获取所有文章的 slug,用于生成静态页面
256
- * 对于大量文章,这个方法会进行优化,只获取 slug 字段
257
- */
258
- public async getPostSlugs() {
259
- // 只获取 slug 字段,减少数据传输量
260
- const result = await queryPostSlugs(this.envVariables);
261
-
262
- return result?.items?.map((post) => post.slug) || [];
263
- }
264
-
265
- public async getPostCategories() {
266
- return await queryPostCategories(this.envVariables);
267
- }
268
-
269
- public async getPostCategoryBySlug(slug: string) {
270
- return await queryOnePostCategoryBySlug(slug, this.envVariables);
271
- }
272
-
273
- public async getTagCategories() {
274
- return await queryTagCategories(this.envVariables);
275
- }
276
-
277
- public async getTags(categoryId?: string) {
278
- return await queryTags(this.envVariables, categoryId);
279
- }
280
-
281
- public async getProducts(
282
- conditions: ListConditions,
283
- imageSize: ImageSize | undefined,
284
- addonFields?: (keyof Product)[]
285
- ) {
286
- if (!imageSize) {
287
- imageSize = this.imageSizes.productThumbnial;
288
- }
289
- return (
290
- await queryProducts(conditions, imageSize, this.envVariables, addonFields)
291
- )?.items;
292
- }
293
-
294
- public async getProductListPaths(options: {
295
- category?: string;
296
- pageSize: number;
297
- }) {
298
- const { category, pageSize } = options;
299
- // 获取总产品数据,只需要获取总数
300
- const productsData = await queryProducts(
301
- { category, page: 1, pageSize: 10 },
302
- undefined,
303
- this.envVariables
304
- );
305
-
306
- const totalItems = productsData?.total ?? 0;
307
-
308
- return this.createPagination(totalItems, pageSize);
309
- }
310
-
311
- public async getCategoredProductListPaths() {
312
- // 获取所有产品分类
313
- const categoryTree = await this.getProductCategories();
314
- const categories = this.flattenTree(categoryTree);
315
-
316
- // 为每个分类生成路径
317
- const paths = [];
318
-
319
- // 如果没有分类,至少创建一个默认路径
320
- if (!categories || categories.length === 0) {
321
- paths.push({
322
- params: { slug: "default", page: "1" },
323
- props: {
324
- currentPage: 1,
325
- totalPages: 1,
326
- pageSize: 12,
327
- totalItems: 0,
328
- slug: "default",
329
- },
330
- });
331
- return paths;
332
- }
333
-
334
- for (const category of categories) {
335
- // 跳过没有 slug 的分类
336
- if (!category.slug) continue;
337
-
338
- // 获取该分类下的分页路径
339
- const categoryPaths = await this.getProductListPaths({
340
- pageSize: 12,
341
- category: category.slug,
342
- });
343
-
344
- // 如果该分类没有产品,至少创建一个第一页
345
- if (categoryPaths.length === 0) {
346
- paths.push({
347
- params: { slug: String(category.slug), page: "1" },
348
- props: {
349
- currentPage: 1,
350
- totalPages: 1,
351
- pageSize: 12,
352
- totalItems: 0,
353
- slug: String(category.slug),
354
- },
355
- });
356
- continue;
357
- }
358
-
359
- // 为每个分页路径添加分类 slug
360
- for (const path of categoryPaths) {
361
- paths.push({
362
- params: { ...path.params, slug: String(category.slug) },
363
- props: { ...path.props, slug: String(category.slug) },
364
- });
365
- }
366
- }
367
-
368
- return paths;
369
- }
370
-
371
- /**
372
- * 获取所有文章分类的分页路径
373
- * 用于生成静态页面
374
- * @returns 所有文章分类的分页路径
375
- */
376
- public async getCategoredPostListPaths() {
377
- // 获取所有文章分类
378
- const categoryTree = await this.getPostCategories();
379
- const categories = this.flattenTree(categoryTree);
380
-
381
- // 为每个分类生成路径
382
- const paths = [];
383
-
384
- // 如果没有分类,至少创建一个默认路径
385
- if (!categories || categories.length === 0) {
386
- paths.push({
387
- params: { slug: "default", page: "1" },
388
- props: {
389
- currentPage: 1,
390
- totalPages: 1,
391
- pageSize: 10,
392
- totalItems: 0,
393
- slug: "default",
394
- },
395
- });
396
- return paths;
397
- }
398
-
399
- for (const category of categories) {
400
- // 跳过没有 slug 的分类
401
- if (!category.slug) continue;
402
-
403
- // 获取该分类下的分页路径
404
- const categoryPaths = await this.getPostListPaths({
405
- pageSize: 10,
406
- category: category.slug,
407
- });
408
-
409
- // 如果该分类没有文章,至少创建一个第一页
410
- if (categoryPaths.length === 0) {
411
- paths.push({
412
- params: { slug: String(category.slug), page: "1" },
413
- props: {
414
- currentPage: 1,
415
- totalPages: 1,
416
- pageSize: 10,
417
- totalItems: 0,
418
- slug: String(category.slug),
419
- },
420
- });
421
- continue;
422
- }
423
-
424
- // 为每个分页路径添加分类 slug
425
- for (const path of categoryPaths) {
426
- paths.push({
427
- params: { ...path.params, slug: String(category.slug) },
428
- props: { ...path.props, slug: String(category.slug) },
429
- });
430
- }
431
- }
432
-
433
- return paths;
434
- }
435
-
436
- public async getProductBySlug(slug: string, imageSize: ImageSize | undefined) {
437
- if (!imageSize) {
438
- imageSize = this.imageSizes.product;
439
- }
440
- return await queryOneProductBySlug(slug, imageSize, this.envVariables);
441
- }
442
-
443
- public async getProductPaths() {
444
- // 获取所有产品的 slug
445
- const slugs = await this.getProductSlugs();
446
-
447
- // 为每个 slug 创建路径
448
- return slugs.map((slug) => ({
449
- params: { slug },
450
- props: { slug },
451
- }));
452
- }
453
-
454
- /**
455
- * 获取所有产品的 slug,用于生成静态页面
456
- * 对于大量产品,这个方法会进行优化,只获取 slug 字段
457
- */
458
- public async getProductSlugs() {
459
- // 只获取 slug 字段,减少数据传输量
460
- const result = await queryAllProducts(this.envVariables);
461
- return result?.items?.map((product) => product.slug) || [];
462
- }
463
-
464
- public async getProductIds() {
465
- const result = await queryAllProducts(this.envVariables);
466
- return result?.items?.map((product) => product.id) || [];
467
- }
468
-
469
- public async getProductCategories() {
470
- return await queryProductCategories(this.envVariables);
471
- }
472
-
473
- public async getProductCategoryBySlug(slug: string) {
474
- return await queryOneProductCategoryBySlug(slug, this.envVariables);
475
- }
476
-
477
- public async getOneUser(id: string) {
478
- return await queryOneUser(id, this.envVariables);
479
- }
480
-
481
- public async getUserPosts(userId: string) {
482
- return (await queryUserPosts({ userId }, this.envVariables))?.items;
483
- }
484
-
485
- public async fulltextSearch(keyword: string, imageSize: ImageSize | undefined) {
486
- if (!imageSize) {
487
- imageSize = this.imageSizes.postThumbnial;
488
- }
489
- return await fulltextSearch(keyword, imageSize, this.envVariables);
490
- }
491
-
492
- public async getUserPaths() {
493
- const result = await queryUserIds(this.envVariables);
494
- return (
495
- result?.items?.map((user) => ({
496
- params: { id: user.id },
497
- props: { id: user.id },
498
- })) || []
499
- );
500
- }
501
-
502
- public createPagination(totalItems: number, pageSize: number) {
503
- const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
504
-
505
- // 为每个页面创建路径和获取对应的文章数据
506
- const paths = [];
507
-
508
- for (let i = 0; i < totalPages; i++) {
509
- const currentPage = i + 1;
510
-
511
- paths.push({
512
- params: { page: String(currentPage) },
513
- props: {
514
- currentPage,
515
- totalPages,
516
- pageSize,
517
- totalItems,
518
- },
519
- });
520
- }
521
-
522
- return paths;
523
- }
524
-
525
- public async createUploadCredentials(
526
- options: UploadOptions
527
- ): Promise<UploadSession | null> {
528
- return await createUploadCredentials(options, this.envVariables);
529
- }
530
- }
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
+ }