@coffic/cosy-ui 0.9.3 → 0.9.5
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/app.css +1 -1
- package/dist/index-astro.ts +3 -19
- package/dist/index-collection.ts +1 -105
- package/dist/src-astro/collection/entities/BaseDoc.ts +28 -0
- package/dist/src-astro/collection/entities/BlogDoc.ts +221 -0
- package/dist/src-astro/collection/entities/CourseDoc.ts +254 -0
- package/dist/src-astro/collection/entities/ExperimentDoc.ts +169 -0
- package/dist/src-astro/collection/entities/LessonDoc.ts +203 -0
- package/dist/src-astro/collection/entities/MetaDoc.ts +115 -0
- package/dist/src-astro/{entities → collection/entities}/SidebarItem.ts +17 -17
- package/dist/src-astro/collection/entities/Tag.ts +42 -0
- package/dist/src-astro/collection/index.ts +11 -0
- package/dist/src-astro/collection/repos/BaseRepo.ts +276 -0
- package/dist/src-astro/collection/repos/BlogRepo.ts +226 -0
- package/dist/src-astro/collection/repos/CourseRepo.ts +137 -0
- package/dist/src-astro/collection/repos/ExperimentRepo.ts +121 -0
- package/dist/src-astro/collection/repos/LessonRepo.ts +128 -0
- package/dist/src-astro/collection/repos/MetaRepo.ts +89 -0
- package/dist/src-astro/empty-state/EmptyState.astro +100 -0
- package/dist/src-astro/empty-state/index.ts +2 -0
- package/dist/src-astro/empty-state/types.ts +70 -0
- package/dist/src-astro/layout-dashboard/DashboardLayout.astro +2 -1
- package/dist/src-astro/layout-dashboard/DashboardTopNavbar.astro +2 -26
- package/package.json +1 -1
- package/dist/src-astro/database/BaseDB.ts +0 -264
- package/dist/src-astro/database/BlogDB.ts +0 -198
- package/dist/src-astro/database/CourseDB.ts +0 -90
- package/dist/src-astro/database/ExperimentDB.ts +0 -106
- package/dist/src-astro/database/LessonDB.ts +0 -106
- package/dist/src-astro/database/MetaDB.ts +0 -74
- package/dist/src-astro/database/index.ts +0 -3
- package/dist/src-astro/entities/BaseDoc.ts +0 -207
- package/dist/src-astro/entities/BlogDoc.ts +0 -107
- package/dist/src-astro/entities/CourseDoc.ts +0 -102
- package/dist/src-astro/entities/ExperimentDoc.ts +0 -119
- package/dist/src-astro/entities/LessonDoc.ts +0 -153
- package/dist/src-astro/entities/MetaDoc.ts +0 -93
- package/dist/src-astro/entities/Tag.ts +0 -42
- /package/dist/src-astro/{entities → collection/entities}/Feature.ts +0 -0
@@ -1,106 +0,0 @@
|
|
1
|
-
import { type CollectionEntry } from 'astro:content';
|
2
|
-
import { BaseDB } from './BaseDB';
|
3
|
-
import LessonDoc from '../entities/LessonDoc';
|
4
|
-
import { cosyLogger } from '../cosy';
|
5
|
-
|
6
|
-
export const COLLECTION_LESSON = 'lessons' as const;
|
7
|
-
export type LessonEntry = CollectionEntry<typeof COLLECTION_LESSON>;
|
8
|
-
|
9
|
-
/**
|
10
|
-
* 课程数据库类,用于管理课程内容集合
|
11
|
-
*
|
12
|
-
* 目录结构:
|
13
|
-
* ```
|
14
|
-
* lessons/
|
15
|
-
* ├── build_your_own_web_toolbox/ # 课程目录
|
16
|
-
* │ ├── images/ # 课程图片资源
|
17
|
-
* │ ├── components/ # 课程组件
|
18
|
-
* │ ├── en/ # 英文版本
|
19
|
-
* │ │ ├── index.mdx # 课程首页
|
20
|
-
* │ │ ├── 1.mdx # 第一章
|
21
|
-
* │ │ └── 2.mdx # 第二章
|
22
|
-
* │ └── zh-cn/ # 中文版本
|
23
|
-
* │ ├── index.mdx # 课程首页
|
24
|
-
* │ ├── 1.mdx # 第一章
|
25
|
-
* │ └── 2.mdx # 第二章
|
26
|
-
* └── learn_astro/ # 另一个课程
|
27
|
-
* ├── en/
|
28
|
-
* │ ├── index.mdx
|
29
|
-
* │ ├── 1.mdx
|
30
|
-
* │ └── 2.mdx
|
31
|
-
* └── zh-cn/
|
32
|
-
* ├── index.mdx
|
33
|
-
* ├── 1.mdx
|
34
|
-
* └── 2.mdx
|
35
|
-
* ```
|
36
|
-
*
|
37
|
-
* 说明:
|
38
|
-
* - 每个课程(如 build_your_own_web_toolbox)是一个独立的目录
|
39
|
-
* - 课程目录可以包含多语言版本(en, zh-cn 等)
|
40
|
-
* - 每个语言版本包含完整的课程内容
|
41
|
-
* - 课程目录可以作为 git 子模块独立管理
|
42
|
-
*/
|
43
|
-
class LessonDB extends BaseDB<
|
44
|
-
typeof COLLECTION_LESSON,
|
45
|
-
LessonEntry,
|
46
|
-
LessonDoc
|
47
|
-
> {
|
48
|
-
protected collectionName = COLLECTION_LESSON;
|
49
|
-
|
50
|
-
protected createDoc(entry: LessonEntry): LessonDoc {
|
51
|
-
return new LessonDoc(entry);
|
52
|
-
}
|
53
|
-
|
54
|
-
/**
|
55
|
-
* 获取指定语言的所有课程
|
56
|
-
*
|
57
|
-
* @param {string} lang - 语言代码
|
58
|
-
* @returns {Promise<LessonDoc[]>} 返回指定语言的所有课程
|
59
|
-
*/
|
60
|
-
async allLessons(lang: string): Promise<LessonDoc[]> {
|
61
|
-
const docs = await this.getDocsByDepth(2);
|
62
|
-
return docs.filter((doc) => doc.getId().endsWith(lang));
|
63
|
-
}
|
64
|
-
|
65
|
-
/**
|
66
|
-
* 获取用于 Astro 静态路由生成的路径参数
|
67
|
-
*
|
68
|
-
* @param debug - 是否开启调试模式
|
69
|
-
* @returns 返回路径参数数组
|
70
|
-
*/
|
71
|
-
async getStaticPaths(debug: boolean = false) {
|
72
|
-
const docs = await this.getEntries();
|
73
|
-
|
74
|
-
if (debug) {
|
75
|
-
cosyLogger.array('所有文档', docs);
|
76
|
-
}
|
77
|
-
|
78
|
-
const paths = docs.map((doc) => {
|
79
|
-
const id = doc.id;
|
80
|
-
const lang = id.split('/')[1];
|
81
|
-
|
82
|
-
let slug = '';
|
83
|
-
if (id.endsWith(lang)) {
|
84
|
-
slug = id.replace(`${lang}`, '');
|
85
|
-
} else {
|
86
|
-
slug = id.replace(`${lang}/`, '');
|
87
|
-
}
|
88
|
-
|
89
|
-
return {
|
90
|
-
params: {
|
91
|
-
lang: lang,
|
92
|
-
slug: slug,
|
93
|
-
},
|
94
|
-
};
|
95
|
-
});
|
96
|
-
|
97
|
-
if (debug) {
|
98
|
-
cosyLogger.array('所有文档的路径', paths);
|
99
|
-
}
|
100
|
-
|
101
|
-
return paths;
|
102
|
-
}
|
103
|
-
}
|
104
|
-
|
105
|
-
// 创建并导出单例实例
|
106
|
-
export const lessonDB = new LessonDB();
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import MetaDoc from '../entities/MetaDoc';
|
2
|
-
import { cosyLogger } from '../cosy';
|
3
|
-
import { type CollectionEntry } from 'astro:content';
|
4
|
-
import { BaseDB } from './BaseDB';
|
5
|
-
|
6
|
-
export const COLLECTION_META = 'meta' as const;
|
7
|
-
export type MetaEntry = CollectionEntry<typeof COLLECTION_META>;
|
8
|
-
|
9
|
-
/**
|
10
|
-
* 元数据数据库类,用于管理网站的元数据内容集合(如"关于我们"等页面)
|
11
|
-
*
|
12
|
-
* 目录结构:
|
13
|
-
* ```
|
14
|
-
* meta/
|
15
|
-
* ├── zh-cn/ # 中文内容
|
16
|
-
* │ ├── about.md # 关于我们
|
17
|
-
* │ ├── advice.md # 建议
|
18
|
-
* └── en/ # 英文内容
|
19
|
-
* ├── about.md
|
20
|
-
* ├── privacy.md
|
21
|
-
* └── terms.md
|
22
|
-
* ```
|
23
|
-
*/
|
24
|
-
class MetaDB extends BaseDB<typeof COLLECTION_META, MetaEntry, MetaDoc> {
|
25
|
-
protected collectionName = COLLECTION_META;
|
26
|
-
|
27
|
-
protected createDoc(entry: MetaEntry): MetaDoc {
|
28
|
-
return new MetaDoc(entry);
|
29
|
-
}
|
30
|
-
|
31
|
-
/**
|
32
|
-
* 获取指定文档的兄弟文档
|
33
|
-
* 例如:对于 'zh-cn/about',会返回 'zh-cn' 下的文档
|
34
|
-
*
|
35
|
-
* @param targetId - 目标文档ID
|
36
|
-
* @returns 返回兄弟文档数组(包括目标文档本身)
|
37
|
-
*/
|
38
|
-
async getSiblings(targetId: string): Promise<MetaDoc[]> {
|
39
|
-
const target = await this.find(targetId);
|
40
|
-
if (!target) {
|
41
|
-
return [];
|
42
|
-
}
|
43
|
-
const docs = await this.getDocsByDepth(2);
|
44
|
-
return docs.filter((doc) => doc.getLang() === target.getLang());
|
45
|
-
}
|
46
|
-
|
47
|
-
/**
|
48
|
-
* 获取用于 Astro 静态路由生成的路径参数,专门配合 [lang]/meta/[slug].astro 使用
|
49
|
-
*
|
50
|
-
* @param debug - 是否开启调试模式
|
51
|
-
* @returns 返回路径参数数组
|
52
|
-
*/
|
53
|
-
async getStaticPaths(debug: boolean = false) {
|
54
|
-
const docs = await this.getDescendantDocs('');
|
55
|
-
|
56
|
-
const paths = docs.map((doc) => {
|
57
|
-
return {
|
58
|
-
params: {
|
59
|
-
lang: doc.getLang(),
|
60
|
-
slug: doc.getSlug(),
|
61
|
-
},
|
62
|
-
};
|
63
|
-
});
|
64
|
-
|
65
|
-
if (debug) {
|
66
|
-
cosyLogger.array('所有元数据文档的路径', paths);
|
67
|
-
}
|
68
|
-
|
69
|
-
return paths;
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
|
-
// 创建并导出单例实例
|
74
|
-
export const metaDB = new MetaDB();
|
@@ -1,207 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
render,
|
3
|
-
type RenderResult,
|
4
|
-
type CollectionEntry,
|
5
|
-
type DataEntryMap,
|
6
|
-
} from 'astro:content';
|
7
|
-
import { SidebarItemEntity, type SidebarProvider } from './SidebarItem';
|
8
|
-
import { cosyLogger, ERROR_PREFIX } from '../cosy';
|
9
|
-
|
10
|
-
/**
|
11
|
-
* 文档基类,提供所有文档类型共享的基本功能
|
12
|
-
*/
|
13
|
-
export abstract class BaseDoc<
|
14
|
-
Collection extends keyof DataEntryMap,
|
15
|
-
T extends CollectionEntry<Collection>,
|
16
|
-
> implements SidebarProvider
|
17
|
-
{
|
18
|
-
protected entry: T;
|
19
|
-
|
20
|
-
constructor(entry: T) {
|
21
|
-
this.entry = entry;
|
22
|
-
}
|
23
|
-
|
24
|
-
/**
|
25
|
-
* 获取顶级文档
|
26
|
-
* 子类应该实现此方法以提供正确的顶级文档
|
27
|
-
*/
|
28
|
-
abstract getTopDoc(): Promise<BaseDoc<Collection, T> | null>;
|
29
|
-
|
30
|
-
/**
|
31
|
-
* 获取子文档
|
32
|
-
* 子类应该实现此方法以提供正确的子文档列表
|
33
|
-
*/
|
34
|
-
abstract getChildren(): Promise<BaseDoc<Collection, T>[]>;
|
35
|
-
|
36
|
-
/**
|
37
|
-
* 获取文档的层级深度
|
38
|
-
* 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,深度为3
|
39
|
-
*/
|
40
|
-
getLevel(): number {
|
41
|
-
return this.entry.id.split('/').length;
|
42
|
-
}
|
43
|
-
|
44
|
-
/**
|
45
|
-
* 获取所有祖先文档的ID
|
46
|
-
* 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,祖先ID为 ["zh-cn/blog", "zh-cn"]
|
47
|
-
* @returns 祖先文档的ID列表
|
48
|
-
*/
|
49
|
-
getAncestorIds(): string[] {
|
50
|
-
const parts = this.entry.id.split('/');
|
51
|
-
return parts
|
52
|
-
.slice(0, -1)
|
53
|
-
.map((_part, index) => parts.slice(0, index + 1).join('/'));
|
54
|
-
}
|
55
|
-
|
56
|
-
/**
|
57
|
-
* 获取指定层级的祖先文档ID
|
58
|
-
* 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,level=2 的祖先ID为 "zh-cn/blog"
|
59
|
-
* @param level 层级深度,从1开始
|
60
|
-
* @returns 祖先文档的ID
|
61
|
-
*/
|
62
|
-
getAncestorId(level: number): string {
|
63
|
-
const ancestorIds = this.getAncestorIds();
|
64
|
-
|
65
|
-
if (level < 1) {
|
66
|
-
cosyLogger.error('Level must be greater than 0');
|
67
|
-
throw new Error(ERROR_PREFIX + 'Level must be greater than 0');
|
68
|
-
}
|
69
|
-
|
70
|
-
if (level >= this.getLevel()) {
|
71
|
-
cosyLogger.debug('当前文档的ID:' + this.entry.id);
|
72
|
-
cosyLogger.debug('当前文档的Level:' + this.getLevel());
|
73
|
-
cosyLogger.debug('正在获取祖先ID,level:' + level);
|
74
|
-
|
75
|
-
throw new Error(
|
76
|
-
ERROR_PREFIX + 'Level must be less than the document level'
|
77
|
-
);
|
78
|
-
}
|
79
|
-
|
80
|
-
if (ancestorIds.length < level) {
|
81
|
-
cosyLogger.debug('当前文档的ID\n' + this.entry.id);
|
82
|
-
cosyLogger.array('当前文档的祖先IDs', ancestorIds);
|
83
|
-
cosyLogger.debug('正在获取祖先ID,level:\n' + level);
|
84
|
-
|
85
|
-
throw new Error(ERROR_PREFIX + 'Level must be greater than 0');
|
86
|
-
}
|
87
|
-
|
88
|
-
return ancestorIds[level - 1];
|
89
|
-
}
|
90
|
-
|
91
|
-
/**
|
92
|
-
* 获取顶级侧边栏项目
|
93
|
-
* 如果有顶级文档,返回顶级文档的侧边栏项目
|
94
|
-
* 否则返回当前文档的侧边栏项目
|
95
|
-
*/
|
96
|
-
async getTopSidebarItem(): Promise<SidebarItemEntity> {
|
97
|
-
const topDoc = await this.getTopDoc();
|
98
|
-
if (topDoc) {
|
99
|
-
return await topDoc.toSidebarItem();
|
100
|
-
}
|
101
|
-
|
102
|
-
return new SidebarItemEntity({
|
103
|
-
text: this.getTitle(),
|
104
|
-
items: [],
|
105
|
-
link: this.getLink(),
|
106
|
-
});
|
107
|
-
}
|
108
|
-
|
109
|
-
/**
|
110
|
-
* 获取父文档ID
|
111
|
-
* 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,父ID为 "zh-cn/blog"
|
112
|
-
*
|
113
|
-
* @returns 父文档ID,如果没有父文档则返回null
|
114
|
-
*/
|
115
|
-
getParentId(): string | null {
|
116
|
-
return this.getAncestorId(this.getLevel() - 1);
|
117
|
-
}
|
118
|
-
|
119
|
-
/**
|
120
|
-
* 获取顶级文档的ID
|
121
|
-
* 例如:对于 ID 为 "zh-cn/blog/typescript" 的文档,顶级ID为 "zh-cn/blog"
|
122
|
-
*
|
123
|
-
* 默认实现假设顶级ID是前两部分
|
124
|
-
* 子类可以根据需要覆盖此方法
|
125
|
-
*/
|
126
|
-
getTopDocId(): string {
|
127
|
-
const level = this.getLevel();
|
128
|
-
|
129
|
-
if (level <= 2) {
|
130
|
-
return this.entry.id;
|
131
|
-
}
|
132
|
-
|
133
|
-
return this.getAncestorId(2);
|
134
|
-
}
|
135
|
-
|
136
|
-
/**
|
137
|
-
* 获取文档ID
|
138
|
-
*/
|
139
|
-
getId(): string {
|
140
|
-
return this.entry.id;
|
141
|
-
}
|
142
|
-
|
143
|
-
/**
|
144
|
-
* 获取文档标题
|
145
|
-
*/
|
146
|
-
getTitle(): string {
|
147
|
-
return this.entry.data.title as string;
|
148
|
-
}
|
149
|
-
|
150
|
-
/**
|
151
|
-
* 获取文档语言
|
152
|
-
*/
|
153
|
-
getLang(): string {
|
154
|
-
return this.entry.id.split('/')[0];
|
155
|
-
}
|
156
|
-
|
157
|
-
/**
|
158
|
-
* 获取文档slug
|
159
|
-
*/
|
160
|
-
getSlug(): string {
|
161
|
-
return this.getId().split('/').slice(1).join('/');
|
162
|
-
}
|
163
|
-
|
164
|
-
/**
|
165
|
-
* 获取文档描述
|
166
|
-
*/
|
167
|
-
getDescription(): string {
|
168
|
-
return this.entry.data.description as string;
|
169
|
-
}
|
170
|
-
|
171
|
-
/**
|
172
|
-
* 获取文档链接
|
173
|
-
* 每个子类必须实现此方法以提供正确的链接
|
174
|
-
*/
|
175
|
-
abstract getLink(): string;
|
176
|
-
|
177
|
-
/**
|
178
|
-
* 渲染文档内容
|
179
|
-
*/
|
180
|
-
async render(): Promise<RenderResult> {
|
181
|
-
return await render(this.entry);
|
182
|
-
}
|
183
|
-
|
184
|
-
/**
|
185
|
-
* 转换为侧边栏项目
|
186
|
-
* 如果文档有子文档,会包含子文档的侧边栏项目
|
187
|
-
*/
|
188
|
-
async toSidebarItem(): Promise<SidebarItemEntity> {
|
189
|
-
const debug = false;
|
190
|
-
|
191
|
-
const children = await this.getChildren();
|
192
|
-
const childItems = await Promise.all(
|
193
|
-
children.map((child) => child.toSidebarItem())
|
194
|
-
);
|
195
|
-
|
196
|
-
if (debug) {
|
197
|
-
cosyLogger.info(`${this.entry.id} 的侧边栏项目`);
|
198
|
-
console.log(childItems);
|
199
|
-
}
|
200
|
-
|
201
|
-
return new SidebarItemEntity({
|
202
|
-
text: this.getTitle(),
|
203
|
-
items: childItems,
|
204
|
-
link: this.getLink(),
|
205
|
-
});
|
206
|
-
}
|
207
|
-
}
|
@@ -1,107 +0,0 @@
|
|
1
|
-
import type { BlogEntry } from '../database/BlogDB';
|
2
|
-
import { LinkUtil } from '../../src/utils/link';
|
3
|
-
import Tag from './Tag';
|
4
|
-
import { BaseDoc } from './BaseDoc';
|
5
|
-
import { COLLECTION_BLOG } from '../database/BlogDB';
|
6
|
-
import { blogDB } from '../database/BlogDB';
|
7
|
-
|
8
|
-
/**
|
9
|
-
* 博客文档类,配合 BlogDB 使用
|
10
|
-
*
|
11
|
-
* 目录结构:
|
12
|
-
* ```
|
13
|
-
* blogs/
|
14
|
-
* ├── zh-cn/ # 中文博客目录
|
15
|
-
* │ ├── typescript-intro.md # 文章内容
|
16
|
-
* │ ├── images/ # 文章相关图片
|
17
|
-
* │ └── web-performance.md
|
18
|
-
* └── en/ # 英文博客目录
|
19
|
-
* ├── typescript-intro.md
|
20
|
-
* ├── images/
|
21
|
-
* └── web-performance.md
|
22
|
-
* ```
|
23
|
-
*
|
24
|
-
* 说明:
|
25
|
-
* - 每个语言(如 zh-cn, en)是一个独立的目录,存放该语言下的所有博客文章
|
26
|
-
* - 每篇博客为一个 markdown 文件,文件名即为 slug
|
27
|
-
* - 每个语言目录下可包含 images 文件夹,存放该语言博客相关图片
|
28
|
-
* - 支持多语言博客内容,便于国际化
|
29
|
-
* - 博客目录可作为 git 子模块独立管理
|
30
|
-
*
|
31
|
-
* 相关:
|
32
|
-
* - BlogDB:博客数据库管理类,负责博客文档的增删查改
|
33
|
-
* - BlogDoc:博客文档实体类,封装单篇博客的相关操作
|
34
|
-
*
|
35
|
-
* 用法示例:
|
36
|
-
* ```typescript
|
37
|
-
* import BlogDoc from '../entities/BlogDoc';
|
38
|
-
* import { blogDB } from '../database/BlogDB';
|
39
|
-
*
|
40
|
-
* // 获取所有中文博客
|
41
|
-
* const blogs = await blogDB.allBlogsByLang('zh-cn');
|
42
|
-
*
|
43
|
-
* // 获取博客链接
|
44
|
-
* const link = blogs[0].getLink();
|
45
|
-
* ```
|
46
|
-
*/
|
47
|
-
|
48
|
-
export default class BlogDoc extends BaseDoc<
|
49
|
-
typeof COLLECTION_BLOG,
|
50
|
-
BlogEntry
|
51
|
-
> {
|
52
|
-
private constructor(entry: BlogEntry) {
|
53
|
-
super(entry);
|
54
|
-
}
|
55
|
-
|
56
|
-
static fromEntry(entry: BlogEntry) {
|
57
|
-
return new BlogDoc(entry);
|
58
|
-
}
|
59
|
-
|
60
|
-
async getTopDoc(): Promise<BlogDoc | null> {
|
61
|
-
const id = this.getTopDocId();
|
62
|
-
const doc = await blogDB.find(id);
|
63
|
-
return doc;
|
64
|
-
}
|
65
|
-
|
66
|
-
async getChildren(): Promise<BlogDoc[]> {
|
67
|
-
return await blogDB.getChildren(this.entry.id);
|
68
|
-
}
|
69
|
-
|
70
|
-
getLink(): string {
|
71
|
-
return LinkUtil.getBlogLink(this.entry.id, this.getLang());
|
72
|
-
}
|
73
|
-
|
74
|
-
getTags(): Tag[] {
|
75
|
-
const tags = this.entry.data.tags as string[];
|
76
|
-
|
77
|
-
if (!tags || tags.length === 0) {
|
78
|
-
return [];
|
79
|
-
}
|
80
|
-
|
81
|
-
return tags.map((tag) => new Tag(tag, 0, this.getLang()));
|
82
|
-
}
|
83
|
-
|
84
|
-
getDate(): Date {
|
85
|
-
return new Date(this.entry.data.date as Date);
|
86
|
-
}
|
87
|
-
|
88
|
-
getDateForDisplay() {
|
89
|
-
try {
|
90
|
-
const dateObj = new Date(this.entry.data.date as Date);
|
91
|
-
|
92
|
-
// Check if date is valid
|
93
|
-
if (isNaN(dateObj.getTime())) {
|
94
|
-
console.warn(`Invalid date format: ${this.entry.data.date}`);
|
95
|
-
return 'Date unavailable: ' + this.getTitle() + ' ' + this.getLink();
|
96
|
-
}
|
97
|
-
return dateObj.toLocaleDateString('zh-CN', {
|
98
|
-
year: 'numeric',
|
99
|
-
month: 'long',
|
100
|
-
day: 'numeric',
|
101
|
-
});
|
102
|
-
} catch (error) {
|
103
|
-
console.error(`Error formatting date: ${this.entry.data.date}`, error);
|
104
|
-
return 'Date unavailable';
|
105
|
-
}
|
106
|
-
}
|
107
|
-
}
|
@@ -1,102 +0,0 @@
|
|
1
|
-
import { cosyLogger } from '../cosy';
|
2
|
-
import { SidebarItemEntity } from './SidebarItem';
|
3
|
-
import type { CourseEntry } from '../database/CourseDB';
|
4
|
-
import { courseDB } from '../database/CourseDB';
|
5
|
-
import { LinkUtil } from '../../src/utils/link';
|
6
|
-
import { COLLECTION_COURSE } from '../database/CourseDB';
|
7
|
-
import { BaseDoc } from './BaseDoc';
|
8
|
-
|
9
|
-
export default class CourseDoc extends BaseDoc<
|
10
|
-
typeof COLLECTION_COURSE,
|
11
|
-
CourseEntry
|
12
|
-
> {
|
13
|
-
constructor(entry: CourseEntry) {
|
14
|
-
super(entry);
|
15
|
-
}
|
16
|
-
|
17
|
-
static fromEntry(entry: CourseEntry) {
|
18
|
-
return new CourseDoc(entry);
|
19
|
-
}
|
20
|
-
|
21
|
-
getLink(): string {
|
22
|
-
return LinkUtil.getCourseLink(this.entry.id);
|
23
|
-
}
|
24
|
-
|
25
|
-
getOrder(): number {
|
26
|
-
return this.entry.data.order ?? 0;
|
27
|
-
}
|
28
|
-
|
29
|
-
isFolder(): boolean {
|
30
|
-
return this.entry.data.folder ?? false;
|
31
|
-
}
|
32
|
-
|
33
|
-
async getTopDoc(): Promise<CourseDoc | null> {
|
34
|
-
const id = this.getTopDocId();
|
35
|
-
const doc = await courseDB.find(id);
|
36
|
-
return doc;
|
37
|
-
}
|
38
|
-
|
39
|
-
async getAncestor(level: number): Promise<CourseDoc | null> {
|
40
|
-
const debug = false;
|
41
|
-
if (debug) {
|
42
|
-
cosyLogger.info(`获取 ${this.entry.id} 的祖先文档,level: ${level}`);
|
43
|
-
}
|
44
|
-
|
45
|
-
if (level >= this.getLevel()) {
|
46
|
-
if (debug) {
|
47
|
-
cosyLogger.info(`祖先文档为自身`);
|
48
|
-
}
|
49
|
-
return this;
|
50
|
-
}
|
51
|
-
|
52
|
-
const id = this.getAncestorId(level);
|
53
|
-
const doc = await courseDB.find(id);
|
54
|
-
return doc;
|
55
|
-
}
|
56
|
-
|
57
|
-
/**
|
58
|
-
* 获取子文档
|
59
|
-
* @returns 子文档列表
|
60
|
-
*/
|
61
|
-
async getChildren(): Promise<CourseDoc[]> {
|
62
|
-
const debug = false;
|
63
|
-
const children = (await courseDB.getChildren(this.entry.id)).sort(
|
64
|
-
(a, b) => a.getOrder() - b.getOrder()
|
65
|
-
);
|
66
|
-
if (debug && children.length > 0) {
|
67
|
-
cosyLogger.array(
|
68
|
-
`${this.entry.id} 的子文档(${children.length})`,
|
69
|
-
children.map((child) => `#${child.getOrder()} ${child.entry.id}`)
|
70
|
-
);
|
71
|
-
}
|
72
|
-
return children;
|
73
|
-
}
|
74
|
-
|
75
|
-
override async toSidebarItem(): Promise<SidebarItemEntity> {
|
76
|
-
const debug = false;
|
77
|
-
const children = await this.getChildren();
|
78
|
-
let childItems = await Promise.all(
|
79
|
-
children.map((child) => child.toSidebarItem())
|
80
|
-
);
|
81
|
-
|
82
|
-
if (this.isBook()) {
|
83
|
-
childItems = [...childItems];
|
84
|
-
}
|
85
|
-
|
86
|
-
if (debug) {
|
87
|
-
cosyLogger.info(`${this.entry.id} 的侧边栏项目`);
|
88
|
-
console.log(childItems);
|
89
|
-
}
|
90
|
-
|
91
|
-
return new SidebarItemEntity({
|
92
|
-
text: this.getTitle(),
|
93
|
-
items: childItems,
|
94
|
-
link: this.getLink(),
|
95
|
-
badge: this.entry.data.badge,
|
96
|
-
});
|
97
|
-
}
|
98
|
-
|
99
|
-
isBook(): boolean {
|
100
|
-
return this.entry.id.split('/').length === 2;
|
101
|
-
}
|
102
|
-
}
|