@anyblock/any-block-core 3.4.6
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/ABAlias.ts +298 -0
- package/ABConvertEvent.ts +307 -0
- package/ABConvertManager.ts +266 -0
- package/ABSetting.ts +114 -0
- package/LICENSE +661 -0
- package/README.md +162 -0
- package/converter/ABConvert.ts +158 -0
- package/converter/GeneratorDataTable.ts +40 -0
- package/converter/abc_c2list.ts +497 -0
- package/converter/abc_code.ts +93 -0
- package/converter/abc_deco.ts +793 -0
- package/converter/abc_dir_tree.ts +467 -0
- package/converter/abc_echarts.ts +539 -0
- package/converter/abc_ex.ts +112 -0
- package/converter/abc_list.ts +650 -0
- package/converter/abc_markmap.ts +119 -0
- package/converter/abc_mdit_container.ts +161 -0
- package/converter/abc_mermaid.ts +343 -0
- package/converter/abc_plantuml.ts +109 -0
- package/converter/abc_plantuml_tools.ts +236 -0
- package/converter/abc_table.ts +242 -0
- package/converter/abc_text.ts +237 -0
- package/demo.ts +236 -0
- package/index.min.ts +26 -0
- package/index.ts +26 -0
- package/locales/en.ts +88 -0
- package/locales/helper.ts +27 -0
- package/locales/zh-cn.ts +88 -0
- package/package.json +22 -0
- package/setting.ts +0 -0
- package/style/styles.css +1218 -0
- package/style/styles.min.css +1 -0
- package/style/styles.scss +1244 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 仅负责转为 echart 的 json/js 文本
|
|
3
|
+
*
|
|
4
|
+
* 均为 md_str -> md_str, 不负责图表渲染部分
|
|
5
|
+
*
|
|
6
|
+
* 需要结合 [any-block/obsidian-charts](https://github.com/any-block/obsidian-charts)
|
|
7
|
+
* 或 vuepress 生态系统中的 echarts 插件使用
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { DateTime, Duration } from 'luxon';
|
|
11
|
+
|
|
12
|
+
import { ABConvert_IOEnum, ABConvert } from "./ABConvert"
|
|
13
|
+
import { type List_ListItem, ListProcess } from "./abc_list"
|
|
14
|
+
import { type List_C2ListItem, C2ListProcess } from "./abc_c2list"
|
|
15
|
+
|
|
16
|
+
const _abc_list2echarts_tree = ABConvert.factory({
|
|
17
|
+
id: "list2echarts_tree",
|
|
18
|
+
name: "ECharts树图",
|
|
19
|
+
match: /list2echarts_tree(.*)/,
|
|
20
|
+
detail: "将列表转换为放ECharts树图,可选附加参数: `_tb` 或 `_radial` 或 `_polyline` 等",
|
|
21
|
+
process_param: ABConvert_IOEnum.text,
|
|
22
|
+
process_return: ABConvert_IOEnum.text,
|
|
23
|
+
process: (el, header, content: string): string=>{
|
|
24
|
+
let list_data: List_ListItem = ListProcess.list2data(content, false)
|
|
25
|
+
list_data = ListProcess.data2strict(list_data)
|
|
26
|
+
let radial_array: RadialNode[] = listdata2radial_array(list_data)
|
|
27
|
+
let radial_data: RadialNode = array2data(radial_array) as RadialNode
|
|
28
|
+
|
|
29
|
+
// type attachment, 根据类型附加一些不同的信息
|
|
30
|
+
let attachment: string = ""
|
|
31
|
+
const match = header.match(/list2echarts_tree(.*)/);
|
|
32
|
+
if (match) {
|
|
33
|
+
// edgeShape
|
|
34
|
+
if (match[1].trim().toLowerCase() === '_polyline') {
|
|
35
|
+
attachment += `edgeShape: 'polyline',`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// orient
|
|
39
|
+
if (match[1].trim().toLowerCase() === '_tb') {
|
|
40
|
+
attachment += `orient: 'TB',`
|
|
41
|
+
} else if (match[1].trim().toLowerCase() === '_lr') {
|
|
42
|
+
attachment += `orient: 'LR',`
|
|
43
|
+
} else if (match[1].trim().toLowerCase() === '_bt') {
|
|
44
|
+
attachment += `orient: 'BT',`
|
|
45
|
+
} else if (match[1].trim().toLowerCase() === '_rl') {
|
|
46
|
+
attachment += `orient: 'RL',`
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 方向
|
|
50
|
+
if (match[1].trim().toLowerCase() === '_radial') {
|
|
51
|
+
attachment += `\
|
|
52
|
+
layout: 'radial',
|
|
53
|
+
symbol: 'emptyCircle',`
|
|
54
|
+
} else {
|
|
55
|
+
attachment += `\
|
|
56
|
+
label: {
|
|
57
|
+
position: 'left', // 展开则显示在左侧
|
|
58
|
+
verticalAlign: 'middle',
|
|
59
|
+
align: 'right',
|
|
60
|
+
},
|
|
61
|
+
leaves: {
|
|
62
|
+
label: {
|
|
63
|
+
position: 'right', // 折叠后则显示在右侧
|
|
64
|
+
verticalAlign: 'middle',
|
|
65
|
+
align: 'left'
|
|
66
|
+
}
|
|
67
|
+
},`
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// json -> script string
|
|
72
|
+
const data_str = JSON.stringify(radial_data)
|
|
73
|
+
const script_str = `\`\`\`echarts
|
|
74
|
+
const option = {
|
|
75
|
+
tooltip: {
|
|
76
|
+
trigger: 'item',
|
|
77
|
+
triggerOn: 'mousemove',
|
|
78
|
+
},
|
|
79
|
+
series: [
|
|
80
|
+
{
|
|
81
|
+
type: 'tree',
|
|
82
|
+
data: [${data_str}],
|
|
83
|
+
${attachment}
|
|
84
|
+
top: '10%',
|
|
85
|
+
bottom: '10%',
|
|
86
|
+
symbolSize: 7,
|
|
87
|
+
initialTreeDepth: 3,
|
|
88
|
+
animationDurationUpdate: 750,
|
|
89
|
+
emphasis: {
|
|
90
|
+
focus: 'descendant',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// const height = 600
|
|
97
|
+
\`\`\``
|
|
98
|
+
return script_str
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const _abc_list2echarts_sunburst = ABConvert.factory({
|
|
103
|
+
id: "list2echarts_sunburst",
|
|
104
|
+
name: "ECharts旭日图",
|
|
105
|
+
match: "list2echarts_sunburst",
|
|
106
|
+
detail: "将列表转换为放ECharts旭日图 (平均分配)",
|
|
107
|
+
process_param: ABConvert_IOEnum.text,
|
|
108
|
+
process_return: ABConvert_IOEnum.text,
|
|
109
|
+
process: (el, header, content: string): string=>{
|
|
110
|
+
let list_data: List_ListItem = ListProcess.list2data(content, false)
|
|
111
|
+
list_data = ListProcess.data2strict(list_data)
|
|
112
|
+
let radial_array: RadialNode[] = listdata2radial_array(list_data, true)
|
|
113
|
+
|
|
114
|
+
// json -> script string
|
|
115
|
+
const data_str = JSON.stringify(radial_array)
|
|
116
|
+
const script_str = `\`\`\`echarts
|
|
117
|
+
const option = {
|
|
118
|
+
series: [
|
|
119
|
+
{
|
|
120
|
+
type: 'sunburst',
|
|
121
|
+
data: ${data_str},
|
|
122
|
+
radius: [0, '90%'],
|
|
123
|
+
label: {
|
|
124
|
+
rotate: 'radial'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
\`\`\``
|
|
130
|
+
return script_str
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const _abc_list2echarts_gantt = ABConvert.factory({
|
|
135
|
+
id: "list2echarts_gantt",
|
|
136
|
+
name: "ECharts甘特图",
|
|
137
|
+
match: "list2echarts_gantt",
|
|
138
|
+
detail: "将列表转换为放ECharts甘特图 (ECharts的甘特图是用custom模拟的)",
|
|
139
|
+
process_param: ABConvert_IOEnum.text,
|
|
140
|
+
process_return: ABConvert_IOEnum.text,
|
|
141
|
+
process: (el, header, content: string): string=>{
|
|
142
|
+
let c2list_data: List_C2ListItem = C2ListProcess.list2c2data(content)
|
|
143
|
+
|
|
144
|
+
const gantt_array = c2listdata2gantt_array(c2list_data)
|
|
145
|
+
const script_str = ganttdata_to_script(gantt_array)
|
|
146
|
+
|
|
147
|
+
return script_str
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// 树节点类型
|
|
152
|
+
type RadialNode = {
|
|
153
|
+
name: string,
|
|
154
|
+
value?: number,
|
|
155
|
+
children?: RadialNode[]
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
*
|
|
159
|
+
* @param data
|
|
160
|
+
* @param leafValue
|
|
161
|
+
* 若为true,则标记叶子节点,并给叶子节点添加 value:1 属性
|
|
162
|
+
* 默认不要给非叶子节点添加 value 属性,可能会影响如旭日图的父节点大小分配
|
|
163
|
+
* @returns
|
|
164
|
+
*/
|
|
165
|
+
function listdata2radial_array(data: List_ListItem, leafValue: boolean = false): RadialNode[] {
|
|
166
|
+
let nodes: RadialNode[] = [] // 节点树
|
|
167
|
+
let prev_nodes: RadialNode[] = [] // 缓存每个level的最新节点
|
|
168
|
+
|
|
169
|
+
// 第一次变换,所有节点为 "key": {...} 形式
|
|
170
|
+
for (let index = 0; index<data.length; index++) {
|
|
171
|
+
// 当前节点
|
|
172
|
+
const item = data[index]
|
|
173
|
+
const current_key: string = item.content
|
|
174
|
+
const current_value: RadialNode = {
|
|
175
|
+
name: current_key,
|
|
176
|
+
...(leafValue ? { value: 1 } : {})
|
|
177
|
+
}
|
|
178
|
+
prev_nodes[item.level] = current_value
|
|
179
|
+
|
|
180
|
+
// 放入节点树的对应位置中
|
|
181
|
+
if (item.level>=1 && prev_nodes.hasOwnProperty(item.level-1)) { // 非根节点且有父节点
|
|
182
|
+
let lastItem = prev_nodes[item.level-1]
|
|
183
|
+
if (typeof lastItem != "object" || Array.isArray(lastItem)) {
|
|
184
|
+
console.error(`list数据不合规,父节点的value值不是{}类型`)
|
|
185
|
+
return nodes
|
|
186
|
+
}
|
|
187
|
+
if (leafValue) { // 非叶子节点需删除value属性
|
|
188
|
+
delete lastItem.value
|
|
189
|
+
}
|
|
190
|
+
if (!lastItem.hasOwnProperty("children")) {
|
|
191
|
+
lastItem.children = [current_value]
|
|
192
|
+
} else {
|
|
193
|
+
lastItem.children!.push(current_value)
|
|
194
|
+
}
|
|
195
|
+
} else if (item.level==0) { // 根节点
|
|
196
|
+
nodes.push(current_value)
|
|
197
|
+
} else { // 不合规
|
|
198
|
+
console.error(`list数据不合规,没有正规化. level:${item.level}, prev_nodes:${prev_nodes}`)
|
|
199
|
+
return nodes
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return nodes
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// gantt节点类型
|
|
207
|
+
type GanttNode = {
|
|
208
|
+
content: string,
|
|
209
|
+
start: number, // 时间戳,支持从 ISO 8601 字符串解析并转换而来。如果无法转换过来,则...(TODO)
|
|
210
|
+
end: number, // 时间戳,支持从 ISO 8601 字符串解析并转换而来。如果无法转换过来,则...(TODO)
|
|
211
|
+
duration: number, // (冗余,先不用)
|
|
212
|
+
group1: number, // 自动分组1
|
|
213
|
+
group2?: string, // (暂不支持第二分组,即时间线名)
|
|
214
|
+
}
|
|
215
|
+
type GanttInfo = {
|
|
216
|
+
data: GanttNode[],
|
|
217
|
+
time_mode: 'ISO8601' | 'timestamp' | 'string' | undefined,
|
|
218
|
+
min_time?: number,
|
|
219
|
+
max_time?: number,
|
|
220
|
+
max_group1: number,
|
|
221
|
+
}
|
|
222
|
+
function c2listdata2gantt_array(data: List_C2ListItem): GanttInfo {
|
|
223
|
+
let nodes: GanttNode[] = [] // 节点列表
|
|
224
|
+
let prev_nodes: GanttNode|undefined // 缓存零级level的最新节点
|
|
225
|
+
let prev_group1: number[] = [] // 缓存每一个分组的尾范围,其length表示当前分组数
|
|
226
|
+
let min_time: number|undefined
|
|
227
|
+
let max_time: number|undefined
|
|
228
|
+
|
|
229
|
+
let time_mode: 'ISO8601' | 'timestamp' | 'string' | undefined // 第一次遇到时间节点时,记录模式
|
|
230
|
+
|
|
231
|
+
// 第一次变换,所有节点为 "key": {...} 形式
|
|
232
|
+
for (let index = 0; index<data.length; index++) {
|
|
233
|
+
const item = data[index]
|
|
234
|
+
const content: string = item.content.trim()
|
|
235
|
+
|
|
236
|
+
// 时间节点
|
|
237
|
+
if (item.level == 0) {
|
|
238
|
+
// 1.1. 准备字段
|
|
239
|
+
const content_array = content.split("/")
|
|
240
|
+
const start_time_str: string = content_array[0].trim();
|
|
241
|
+
// node.start = start_time_str;
|
|
242
|
+
const end_time_str: string = content_array[1] ? content_array[1].trim() : "P1D"
|
|
243
|
+
// node.end = end_time_str;
|
|
244
|
+
// const mid_time_str: string|null = content_array[2] ? content_array[2].trim() : null // 暂不支持,以后再说
|
|
245
|
+
|
|
246
|
+
// 1.2. 准备格式类型
|
|
247
|
+
if (!time_mode) {
|
|
248
|
+
if (/^\d+$/.test(start_time_str)) {
|
|
249
|
+
time_mode = 'timestamp';
|
|
250
|
+
} else if (DateTime.fromISO(start_time_str).isValid) {
|
|
251
|
+
time_mode = 'ISO8601';
|
|
252
|
+
} else {
|
|
253
|
+
time_mode = 'string';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 2.1. 填充初始值
|
|
258
|
+
const node: GanttNode = {
|
|
259
|
+
content: "",
|
|
260
|
+
start: 0,
|
|
261
|
+
end: 0,
|
|
262
|
+
duration: 0,
|
|
263
|
+
group1: 0,
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 2.2. 填充归一化的时间
|
|
267
|
+
if (time_mode === 'ISO8601') {
|
|
268
|
+
const startDateTime = DateTime.fromISO(start_time_str);
|
|
269
|
+
if (startDateTime.isValid) { node.start = startDateTime.toMillis(); }
|
|
270
|
+
else { console.warn('开始时间解析失败1'); prev_nodes = undefined; continue; }
|
|
271
|
+
//
|
|
272
|
+
const potentialEndDate = DateTime.fromISO(end_time_str);
|
|
273
|
+
if (potentialEndDate.isValid) { // b1. 绝对日期
|
|
274
|
+
node.end = potentialEndDate.toMillis();
|
|
275
|
+
} else {
|
|
276
|
+
const potentialDuration = Duration.fromISO(end_time_str);
|
|
277
|
+
if (potentialDuration.isValid) { // b2. 持续时间 (e.g., "P1D")
|
|
278
|
+
node.end = startDateTime.plus(potentialDuration).toMillis();
|
|
279
|
+
} else { // b3. 解析失败
|
|
280
|
+
console.warn('结束时间解析失败1'); prev_nodes = undefined; continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} else if (time_mode === 'timestamp') {
|
|
284
|
+
const startTimestamp = parseInt(start_time_str, 10);
|
|
285
|
+
if (!isNaN(startTimestamp)) node.start = startTimestamp;
|
|
286
|
+
else { console.warn('开始时间戳解析失败2'); prev_nodes = undefined; continue; }
|
|
287
|
+
//
|
|
288
|
+
const endTimestamp = parseInt(end_time_str, 10);
|
|
289
|
+
if (!isNaN(endTimestamp)) node.end = endTimestamp;
|
|
290
|
+
else { console.warn('结束时间戳解析失败2'); prev_nodes = undefined; continue; }
|
|
291
|
+
} else {
|
|
292
|
+
console.warn('暂不支持非时间格式的甘特图时间解析');
|
|
293
|
+
prev_nodes = undefined
|
|
294
|
+
continue
|
|
295
|
+
}
|
|
296
|
+
min_time = (min_time === undefined) ? node.start : Math.min(min_time, node.start)
|
|
297
|
+
max_time = (max_time === undefined) ? node.end : Math.max(max_time, node.end)
|
|
298
|
+
|
|
299
|
+
// 2.3. 填充分组
|
|
300
|
+
if (prev_group1.length == 0) {
|
|
301
|
+
// b1. 还没有第一组,添加新组
|
|
302
|
+
node.group1 = 0; prev_group1.push(node.end);
|
|
303
|
+
} else {
|
|
304
|
+
// b2. 寻找到所属组
|
|
305
|
+
let target_group1: undefined | number
|
|
306
|
+
for (let group1=0; group1 < prev_group1.length; group1++) {
|
|
307
|
+
if (node.start < prev_group1[group1]) continue
|
|
308
|
+
target_group1 = group1
|
|
309
|
+
node.group1 = group1; prev_group1[group1] = node.end;
|
|
310
|
+
break
|
|
311
|
+
}
|
|
312
|
+
// b3. 没有合适组,添加新组
|
|
313
|
+
if (target_group1 === undefined) {
|
|
314
|
+
node.group1 = prev_group1.length; prev_group1.push(node.end);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
nodes.push(node)
|
|
319
|
+
prev_nodes = node
|
|
320
|
+
continue
|
|
321
|
+
}
|
|
322
|
+
// 内容节点
|
|
323
|
+
else {
|
|
324
|
+
if (!prev_nodes || !time_mode) continue // 异常
|
|
325
|
+
prev_nodes.content = content
|
|
326
|
+
prev_nodes = undefined
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
data: nodes,
|
|
332
|
+
time_mode,
|
|
333
|
+
max_group1: prev_group1.length,
|
|
334
|
+
min_time,
|
|
335
|
+
max_time,
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// TODO
|
|
340
|
+
// gantt 这部分的 todos,应该还剩这几个:
|
|
341
|
+
// - 明暗主题模式的颜色组 (obsidian和vuepress中判断主题)
|
|
342
|
+
// - 可选的 mid time (task完成度)
|
|
343
|
+
// - 可选 task flag (`[x]` `[ ]`)
|
|
344
|
+
// - 自适应x轴显示 (根据缩放比例自动选择显示年/月/日/时间)
|
|
345
|
+
// - 支持多时间线
|
|
346
|
+
function ganttdata_to_script(gantt_data: GanttInfo): string {
|
|
347
|
+
const currentTheme = document.body.classList.contains('theme-dark') ? 'dark' : 'light'; // obsidian主题
|
|
348
|
+
const { data: ganttNode, max_group1, min_time, max_time } = gantt_data
|
|
349
|
+
|
|
350
|
+
// data数据重新准备
|
|
351
|
+
let data: { value: (string|number)[], itemStyle?: any }[] = []
|
|
352
|
+
for (const item of ganttNode) {
|
|
353
|
+
data.push({
|
|
354
|
+
value: [
|
|
355
|
+
item.content, item.group1, item.start, item.end, item.end - item.start
|
|
356
|
+
],
|
|
357
|
+
// itemStyle: {
|
|
358
|
+
// color: getRandomColor()
|
|
359
|
+
// }
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
// 填充随机颜色 (弃用,改用主题色)
|
|
363
|
+
// function getRandomColor() {
|
|
364
|
+
// const letters = '0123456789ABCDEF';
|
|
365
|
+
// let color = '#';
|
|
366
|
+
// for (let i = 0; i < 6; i++) {
|
|
367
|
+
// color += letters[Math.floor(Math.random() * 16)];
|
|
368
|
+
// }
|
|
369
|
+
// return color;
|
|
370
|
+
// }
|
|
371
|
+
|
|
372
|
+
// 主体
|
|
373
|
+
const categories = Array.from({ length: max_group1 }, (_, i) => '');
|
|
374
|
+
const height_calc = categories.length * 40;
|
|
375
|
+
const startTime = min_time ?? 0 - 1 * 24 * 60 * 60 * 1000;
|
|
376
|
+
let option = {
|
|
377
|
+
tooltip: { // 悬浮显示文字
|
|
378
|
+
trigger: 'item',
|
|
379
|
+
},
|
|
380
|
+
dataZoom: [
|
|
381
|
+
{
|
|
382
|
+
type: 'slider', // 滑动条型 dataZoom
|
|
383
|
+
xAxisIndex: 0, // 控制第一个 X 轴
|
|
384
|
+
filterMode: 'weakFilter',
|
|
385
|
+
height: 20, // 滑动条高度
|
|
386
|
+
bottom: 10, // 距离底部的距离
|
|
387
|
+
start: 0, // 初始显示范围
|
|
388
|
+
end: 100, // 初始显示范围
|
|
389
|
+
handleIcon: 'path://M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
|
390
|
+
handleSize: '80%',
|
|
391
|
+
showDetail: false,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
type: 'inside', // 内置型 dataZoom,支持鼠标滚轮缩放
|
|
395
|
+
xAxisIndex: 0, // 控制第一个 X 轴
|
|
396
|
+
filterMode: 'weakFilter',
|
|
397
|
+
start: 0,
|
|
398
|
+
end: 100
|
|
399
|
+
}
|
|
400
|
+
],
|
|
401
|
+
grid: {
|
|
402
|
+
height: height_calc,
|
|
403
|
+
top: 40,
|
|
404
|
+
bottom: 40,
|
|
405
|
+
left: 15,
|
|
406
|
+
right: 15,
|
|
407
|
+
},
|
|
408
|
+
xAxis: {
|
|
409
|
+
position: 'top',
|
|
410
|
+
min: startTime,
|
|
411
|
+
scale: true,
|
|
412
|
+
splitNumber: 30, // 显示更密集 (非一定,会避免标签重叠)
|
|
413
|
+
axisLabel: {
|
|
414
|
+
fontSize: 10, // 显示更密集
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
yAxis: {
|
|
418
|
+
data: categories, // 分类
|
|
419
|
+
inverse: true,
|
|
420
|
+
},
|
|
421
|
+
series: [
|
|
422
|
+
{
|
|
423
|
+
type: 'custom',
|
|
424
|
+
itemStyle: {
|
|
425
|
+
opacity: 0.8 // 透明度
|
|
426
|
+
},
|
|
427
|
+
encode: {
|
|
428
|
+
x: [2, 3], // 将value数组的第N个元素映射到x轴 (开始和结束时间)
|
|
429
|
+
y: 1 // 将value数组的第N个元素映射到y轴 (分类索引)
|
|
430
|
+
},
|
|
431
|
+
data: data
|
|
432
|
+
}
|
|
433
|
+
]
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* const myOption = myChart.getOption(); 颜色组
|
|
438
|
+
* 主要注意调用时机,在回调中调用还行,在根部的话会是 undefined
|
|
439
|
+
* - color: 一个长度为9的颜色列表, 是主题定义的 // 明亮模式可能无此属性,要 ?? black
|
|
440
|
+
* - backgroundColor: 背景颜色
|
|
441
|
+
* - gradientColor: 渐变色,2长度数组
|
|
442
|
+
* - textStyle.color: 文字颜色
|
|
443
|
+
*/
|
|
444
|
+
const script_str = `\`\`\`echarts
|
|
445
|
+
height = ${height_calc + 80}
|
|
446
|
+
const option = ${JSON.stringify(option, null, 2)}
|
|
447
|
+
|
|
448
|
+
// 自定义渲染函数 - x轴
|
|
449
|
+
let lastMonth = -1; // 用来记录上一个刻度的月份
|
|
450
|
+
option.xAxis.axisLabel.formatter = function (val) {
|
|
451
|
+
let date = new Date(val);
|
|
452
|
+
let currentMonth = date.getMonth();
|
|
453
|
+
if (lastMonth !== currentMonth) { // 如果月份与上一个不同,或者这是第一个刻度
|
|
454
|
+
lastMonth = currentMonth;
|
|
455
|
+
return echarts.format.formatTime('yyyy-MM', val) + '\\n' +
|
|
456
|
+
echarts.format.formatTime('dd', val);
|
|
457
|
+
} else {
|
|
458
|
+
return '\\n' + // (头部空换行保持高度不变,避免缩放时图表跳动)
|
|
459
|
+
echarts.format.formatTime('dd', val);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// 自定义渲染函数 - tooltip, 支持html
|
|
464
|
+
option.tooltip.formatter = function (params) {
|
|
465
|
+
// let categoryName = categories[params.value[1]];
|
|
466
|
+
return echarts.format.formatTime('MM-dd', params.value[2]) +
|
|
467
|
+
'/' +
|
|
468
|
+
echarts.format.formatTime('MM-dd', params.value[3]) +
|
|
469
|
+
'<br />' +
|
|
470
|
+
params.value[0].replaceAll('\\n', '<br />');
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
// 自定义渲染函数 - 图表内容
|
|
474
|
+
option.series[0].renderItem = function renderItem(params, api) {
|
|
475
|
+
var categoryIndex = api.value(1); // 分类索引
|
|
476
|
+
var start = api.coord([api.value(2), categoryIndex]);
|
|
477
|
+
var end = api.coord([api.value(3), categoryIndex]);
|
|
478
|
+
var height = api.size([0, 1])[1] * 1 - 5;
|
|
479
|
+
var rectShape = echarts.graphic.clipRectByRect( // 绘制矩形,并裁减
|
|
480
|
+
{
|
|
481
|
+
x: start[0],
|
|
482
|
+
y: start[1] - height / 2,
|
|
483
|
+
width: end[0] - start[0],
|
|
484
|
+
height: height,
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
x: params.coordSys.x,
|
|
488
|
+
y: params.coordSys.y,
|
|
489
|
+
width: params.coordSys.width,
|
|
490
|
+
height: params.coordSys.height
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
var text_array = api.value(0).split('\\n');
|
|
494
|
+
const textColor = myChart.getOption().textStyle.color ?? "black";
|
|
495
|
+
return (
|
|
496
|
+
rectShape && {
|
|
497
|
+
type: 'group', // group 元素,包含矩形和文本
|
|
498
|
+
children: [
|
|
499
|
+
{
|
|
500
|
+
type: 'rect', // 矩形元素
|
|
501
|
+
transition: ['shape'],
|
|
502
|
+
shape: {
|
|
503
|
+
...rectShape,
|
|
504
|
+
r: 5, // 圆角半径
|
|
505
|
+
},
|
|
506
|
+
style: api.style()
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
type: 'text', // 文本元素
|
|
510
|
+
transition: ['style'], // 对样式应用过渡动画
|
|
511
|
+
style: {
|
|
512
|
+
x: start[0] + 5, // 将文本放在矩形内部,并向右偏移 5px
|
|
513
|
+
y: start[1], // 垂直居中
|
|
514
|
+
text: (text_array.length > 1 ? text_array[0] + '...' : text_array[0]),
|
|
515
|
+
fill: textColor, // 文本颜色
|
|
516
|
+
textVerticalAlign: 'middle', // 文本垂直对齐方式
|
|
517
|
+
textAlign: 'left' // 文本水平对齐方式
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
]
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
\`\`\``;
|
|
525
|
+
|
|
526
|
+
return script_str
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// 将data数组转对象,如果根节点只有一个则正常转,如果有多个根节点则自动加上名为root的根节点
|
|
530
|
+
function array2data(array: object[]): object {
|
|
531
|
+
if (array.length==1) {
|
|
532
|
+
return array[0]
|
|
533
|
+
} else {
|
|
534
|
+
return {
|
|
535
|
+
name: "root",
|
|
536
|
+
children: array
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 处理器_特殊
|
|
3
|
+
*
|
|
4
|
+
* 这是一个demo: 教你如何写一个自己的转换器
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {ABConvert_IOEnum, ABConvert, type ABConvert_SpecSimp} from "./ABConvert"
|
|
8
|
+
import {ABConvertManager} from "../ABConvertManager"
|
|
9
|
+
import { get_ABAlias_iter } from "../ABAlias";
|
|
10
|
+
|
|
11
|
+
const _abc_faq = ABConvert.factory({
|
|
12
|
+
id: "faq",
|
|
13
|
+
name: "FAQ",
|
|
14
|
+
match: "FAQ",
|
|
15
|
+
detail: "渲染常见问题/对话。每个项需以 `/^([a-zA-Z])(: |:)(.*)/` 开头",
|
|
16
|
+
process_param: ABConvert_IOEnum.text,
|
|
17
|
+
process_return: ABConvert_IOEnum.el,
|
|
18
|
+
process: (el, header, content: string): HTMLElement=>{
|
|
19
|
+
const e_faq:HTMLElement = document.createElement("div"); el.appendChild(e_faq); e_faq.classList.add("ab-faq");
|
|
20
|
+
const list_content:string[] = content.split("\n");
|
|
21
|
+
|
|
22
|
+
let mode_qa:string = ""
|
|
23
|
+
let last_content:string = ""
|
|
24
|
+
for (let line of list_content){
|
|
25
|
+
const m_line = line.match(/^([\S]+)(:|:)(.*)/) // 这里可以和 `code(chat)` 的 chat-view-qq 微信格式保持一致,可以互相转换
|
|
26
|
+
if (!m_line){ // 不匹配
|
|
27
|
+
if (mode_qa) {
|
|
28
|
+
last_content = last_content + "\n" + line
|
|
29
|
+
}
|
|
30
|
+
continue
|
|
31
|
+
} else { // 匹配
|
|
32
|
+
if (mode_qa) {
|
|
33
|
+
const e_faq_line = document.createElement("div"); e_faq.appendChild(e_faq_line); e_faq_line.classList.add("ab-faq-line", `ab-faq-${mode_qa}`);
|
|
34
|
+
const e_faq_bubble = document.createElement("div"); e_faq_line.appendChild(e_faq_bubble); e_faq_bubble.classList.add("ab-faq-bubble", `ab-faq-${mode_qa}`);
|
|
35
|
+
const e_faq_content = document.createElement("div"); e_faq_bubble.appendChild(e_faq_content); e_faq_content.classList.add("ab-faq-content");
|
|
36
|
+
ABConvertManager.getInstance().m_renderMarkdownFn(last_content, e_faq_content)
|
|
37
|
+
}
|
|
38
|
+
mode_qa = m_line[1]
|
|
39
|
+
last_content = m_line[3]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// 循环尾
|
|
43
|
+
if (mode_qa) {
|
|
44
|
+
const e_faq_line = document.createElement("div"); e_faq.appendChild(e_faq_line); e_faq_line.classList.add("ab-faq-line", `ab-faq-${mode_qa}`);
|
|
45
|
+
const e_faq_bubble = document.createElement("div"); e_faq_line.appendChild(e_faq_bubble); e_faq_bubble.classList.add("ab-faq-bubble", `ab-faq-${mode_qa}`);
|
|
46
|
+
const e_faq_content = document.createElement("div"); e_faq_bubble.appendChild(e_faq_content); e_faq_content.classList.add("ab-faq-content");
|
|
47
|
+
ABConvertManager.getInstance().m_renderMarkdownFn(last_content, e_faq_content)
|
|
48
|
+
}
|
|
49
|
+
return el
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const _abc_info_converter = ABConvert.factory({
|
|
54
|
+
id: "info_converter",
|
|
55
|
+
name: "INFO",
|
|
56
|
+
detail: "查看当前软件版本下的注册处理器表",
|
|
57
|
+
process_param: ABConvert_IOEnum.text,
|
|
58
|
+
process_return: ABConvert_IOEnum.el,
|
|
59
|
+
process: (el, header, content: string): HTMLElement=>{
|
|
60
|
+
const table_p: HTMLDivElement = document.createElement("div"); el.appendChild(table_p); table_p.classList.add("ab-setting", "md-table-fig1");
|
|
61
|
+
const table: HTMLDivElement = document.createElement("table"); table_p.appendChild(table); table.classList.add("ab-setting","md-table-fig2");
|
|
62
|
+
{
|
|
63
|
+
const thead = document.createElement("thead"); table.appendChild(thead);
|
|
64
|
+
const tr = document.createElement("tr"); thead.appendChild(tr);
|
|
65
|
+
let th;
|
|
66
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "处理器名\nProcessor name";
|
|
67
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "下拉框默认项\nThe default drop-down box";
|
|
68
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "用途描述\nPurpose description";
|
|
69
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "输入类型\nInput type";
|
|
70
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "输出类型\nOutput type";
|
|
71
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "正则\nRegExp";
|
|
72
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "是否启用\nIs enable";
|
|
73
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "定义来源\nSource";
|
|
74
|
+
th = document.createElement("th"); tr.appendChild(th); th.textContent = "别名替换\nAlias substitution";
|
|
75
|
+
}
|
|
76
|
+
const tbody = document.createElement("tbody"); table.appendChild(tbody);
|
|
77
|
+
for (let item of ABConvertManager.getInstance().list_abConvert){
|
|
78
|
+
const tr = document.createElement("tr"); tbody.appendChild(tr)
|
|
79
|
+
let td
|
|
80
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = item.name;
|
|
81
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = String(item.default);
|
|
82
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = item.detail; td.setAttribute("style", "max-width:240px;overflow-x:auto;white-space:nowrap;");
|
|
83
|
+
// td = document.createElement("td"); tr.appendChild(td); td.textContent = item.is_render?"渲染":"文本";
|
|
84
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = String(item.process_param);
|
|
85
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = String(item.process_return);
|
|
86
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = String(item.match);
|
|
87
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = item.is_disable?"No":"Yes";
|
|
88
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = item.register_from;
|
|
89
|
+
td = document.createElement("td"); tr.appendChild(td); td.textContent = item.process_alias;
|
|
90
|
+
}
|
|
91
|
+
return el
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const _abc_info_alias = ABConvert.factory({
|
|
96
|
+
id: "info_alias",
|
|
97
|
+
name: "INFO_Alias",
|
|
98
|
+
match: "info_alias",
|
|
99
|
+
detail: "查看当前软件版本下的注册别名表",
|
|
100
|
+
process_param: ABConvert_IOEnum.text,
|
|
101
|
+
process_return: ABConvert_IOEnum.json,
|
|
102
|
+
process: (el, header, content: string): string=>{
|
|
103
|
+
return JSON.stringify(
|
|
104
|
+
Array.from(get_ABAlias_iter(), (item) => ({
|
|
105
|
+
regex: item.regex.toString(),
|
|
106
|
+
replacement: item.replacement,
|
|
107
|
+
})),
|
|
108
|
+
null,
|
|
109
|
+
2,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
})
|