Skip to content

Commit

Permalink
feat: #728 把根据表格生成图表的功能放出来
Browse files Browse the repository at this point in the history
  • Loading branch information
sunsonliu committed Apr 24, 2024
1 parent b2c6a0e commit e1df984
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 10 deletions.
38 changes: 38 additions & 0 deletions examples/markdown/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,44 @@ $$
-----
## 表格配图
**说明**
- 在通用表格语法的基础上,通过在首行首列单元格里写入关键字来同时生成表格和图表
**示例(折线图)**
```markdown
| :line:{x,y} | Header1 | Header2 | Header3 | Header4 |
| ------ | ------ | ------ | ------ | ------ |
| Sample1 | 11 | 11 | 4 | 33 |
| Sample2 | 112 | 111 | 22 | 222 |
| Sample3 | 333 | 142 | 311 | 11 |
```
**效果**
| :line:{x,y} | Header1 | Header2 | Header3 | Header4 |
| ------ | ------ | ------ | ------ | ------ |
| Sample1 | 11 | 11 | 4 | 33 |
| Sample2 | 112 | 111 | 22 | 222 |
| Sample3 | 333 | 142 | 311 | 11 |
**示例(柱状图)**
```markdown
| :bar:{y,x} | Header1 | Header2 | Header3 | Header4 |
| ------ | ------ | ------ | ------ | ------ |
| Sample1 | 11 | 11 | 4 | 33 |
| Sample2 | 112 | 111 | 22 | 222 |
| Sample3 | 333 | 142 | 311 | 11 |
```
**效果**
| :bar:{y,x} | Header1 | Header2 | Header3 | Header4 |
| ------ | ------ | ------ | ------ | ------ |
| Sample1 | 11 | 11 | 4 | 33 |
| Sample2 | 112 | 111 | 22 | 222 |
| Sample3 | 333 | 142 | 311 | 11 |
-----
## 流程图[^不通用提醒]
**说明**
Expand Down
18 changes: 15 additions & 3 deletions examples/scripts/index-demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ var customMenuC = Cherry.createMenuHook('帮助中心', {
]
});

/**
* 定义带图表表格的按钮
*/
var customMenuTable = Cherry.createMenuHook('图表', {
iconName: 'trendingUp',
subMenuConfig: [
{ noIcon: true, name: '折线图', onclick: (event)=>{cherry.insert('\n| :line:{x,y} | Header1 | Header2 | Header3 | Header4 |\n| ------ | ------ | ------ | ------ | ------ |\n| Sample1 | 11 | 11 | 4 | 33 |\n| Sample2 | 112 | 111 | 22 | 222 |\n| Sample3 | 333 | 142 | 311 | 11 |\n');} },
{ noIcon: true, name: '柱状图', onclick: (event)=>{cherry.insert('\n| :bar:{x,y} | Header1 | Header2 | Header3 | Header4 |\n| ------ | ------ | ------ | ------ | ------ |\n| Sample1 | 11 | 11 | 4 | 33 |\n| Sample2 | 112 | 111 | 22 | 222 |\n| Sample3 | 333 | 142 | 311 | 11 |\n');} },
]
});

var basicConfig = {
id: 'markdown',
externals: {
Expand All @@ -103,8 +114,7 @@ var basicConfig = {
lineNumber: true, // 默认显示行号
},
table: {
enableChart: false,
// chartEngine: Engine Class
enableChart: true,
},
fontEmphasis: {
allowWhitespace: false, // 是否允许首尾空格
Expand Down Expand Up @@ -166,6 +176,7 @@ var basicConfig = {
insert: ['image', 'audio', 'video', 'link', 'hr', 'br', 'code', 'formula', 'toc', 'table', 'pdf', 'word', 'ruby'],
},
'graph',
'customMenuTable',
'togglePreview',
'settings',
'codeTheme',
Expand All @@ -181,13 +192,14 @@ var basicConfig = {
sidebar: ['mobilePreview', 'copy', 'theme', 'publish'],
sidebar: ['mobilePreview', 'copy', 'theme'],
toc: {
updateLocationHash: false, // 要不要更新URL的hash
updateLocationHash: true, // 要不要更新URL的hash
defaultModel: 'full', // pure: 精简模式/缩略模式,只有一排小点; full: 完整模式,会展示所有标题
},
customMenu: {
customMenuAName: customMenuA,
customMenuBName: customMenuB,
customMenuCName: customMenuC,
customMenuTable,
},
// config: {
// publish: [
Expand Down
22 changes: 20 additions & 2 deletions src/Previewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -633,10 +633,28 @@ export default class Previewer {
break;
case 'update':
try {
if (newContent[change.newIndex].dom.querySelector('svg')) {
// 处理表格包含图表的特殊场景
let hasUpdate = false;
if (
newContent[change.newIndex].dom.className === 'cherry-table-container' &&
newContent[change.newIndex].dom.querySelector('.cherry-table-figure') &&
oldContent[change.oldIndex].dom.querySelector('.cherry-table-figure')
) {
oldContent[change.oldIndex].dom
.querySelector('.cherry-table-figure')
.replaceWith(newContent[change.newIndex].dom.querySelector('.cherry-table-figure'));
oldContent[change.oldIndex].dom.dataset.sign = newContent[change.oldIndex].dom.dataset.sign;
this.$updateDom(
newContent[change.newIndex].dom.querySelector('.cherry-table'),
oldContent[change.oldIndex].dom.querySelector('.cherry-table'),
);
hasUpdate = true;
} else if (newContent[change.newIndex].dom.querySelector('svg')) {
throw new Error(); // SVG暂不使用patch更新
}
this.$updateDom(newContent[change.newIndex].dom, oldContent[change.oldIndex].dom);
if (!hasUpdate) {
this.$updateDom(newContent[change.newIndex].dom, oldContent[change.oldIndex].dom);
}
} catch (e) {
domContainer.insertBefore(newContent[change.newIndex].dom, oldContent[change.oldIndex].dom);
domContainer.removeChild(oldContent[change.oldIndex].dom);
Expand Down
170 changes: 170 additions & 0 deletions src/addons/advance/cherry-table-echarts-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/**
* Tencent is pleased to support the open source community by making CherryMarkdown available.
*
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
* The below software in this distribution may have been modified by THL A29 Limited ("Tencent Modifications").
*
* All Tencent Modifications are Copyright (C) THL A29 Limited.
*
* CherryMarkdown is licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mergeWith from 'lodash/mergeWith';

const DEFAULT_OPTIONS = {
renderer: 'svg',
width: 500,
height: 300,
};

export default class EChartsTableEngine {
static install(cherryOptions, ...args) {
if (typeof window === 'undefined') {
console.warn('echarts-table-engine only works in browser.');
mergeWith(cherryOptions, {
engine: {
syntax: {
table: {
enableChart: false,
},
},
},
});
return;
}
mergeWith(cherryOptions, {
engine: {
syntax: {
table: {
enableChart: true,
chartRenderEngine: EChartsTableEngine,
externals: ['echarts'],
},
},
},
});
}

constructor(echartsOptions = {}) {
const { echarts, ...options } = echartsOptions;
if (!echarts && !window.echarts) {
throw new Error('table-echarts-plugin[init]: Package echarts not found.');
}
this.options = { ...DEFAULT_OPTIONS, ...(options || {}) };
this.echartsRef = echarts || window.echarts; // echarts引用
this.dom = null;
}

getInstance() {
if (!this.dom) {
this.dom = document.createElement('div');
this.echartsRef.init(this.dom, null, this.options);
}
return this.echartsRef.getInstanceByDom(this.dom);
}

render(type, options, tableObject) {
// console.log(type, options, tableObject);
let chartOption = {};
switch (type) {
case 'bar':
chartOption = this.renderBarChart(tableObject, options);
break;
case 'line':
chartOption = this.renderLineChart(tableObject, options);
break;
default:
return '';
}
const eChartInstance = this.getInstance();
eChartInstance.clear();
eChartInstance.setOption(chartOption);
return eChartInstance.getDom().innerHTML;
}

renderBarChart(tableObject, options) {
return this.$renderChartCommon(tableObject, options, 'bar');
}

renderLineChart(tableObject, options) {
return this.$renderChartCommon(tableObject, options, 'line');
}

$renderChartCommon(tableObject, options, type) {
// TODO: 通过options反转xy轴
const baseSeries = {
bar: {
type: 'bar',
barWidth: 20,
animation: false,
name: '',
data: [],
},
line: {
type: 'line',
animation: false,
name: '',
data: [],
},
};
if (!baseSeries[type]) {
return;
}
const dataSet = tableObject.rows.reduce(
(result, row) => {
// legend
result.legend.data.push(row[0]);
// series
result.series.push({
...baseSeries[type],
name: row[0],
data: row.slice(1).map((data) => {
const num = Number.parseFloat(data.replace(/,/g, ''));
return num;
}),
});
return result;
},
{
legend: { data: [] },
series: [],
},
);
const chartOptions = {
...dataSet,
xAxis: {
data: tableObject.header.slice(1),
type: 'category',
},
yAxis: {
type: 'value',
axisLabel: {
width: '100%',
},
},
grid: {
containLabel: true,
left: '1%',
right: '1%',
bottom: '10%',
},
};
return chartOptions;
}

onDestroy() {
if (!this.dom) {
return;
}
this.echartsRef.dispose(this.dom);
}
}
10 changes: 6 additions & 4 deletions src/core/hooks/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,13 @@ export default class Table extends ParagraphBase {
return tableResult;
}
const chart = this.chartRenderEngine.render(chartOptions.type, chartOptions.options, tableObject);
const chartHtml = `<figure id="table_chart_${chartOptionsSign}_${tableResult.sign}"
data-sign="table_chart_${chartOptionsSign}_${tableResult.sign}" data-lines="0">${chart}</figure>`;
const chartHtml = `<figure class="cherry-table-figure">${chart}</figure>`;
const newSign = `${tableResult.sign}${chartOptionsSign}`;
return {
html: `${chartHtml}${tableResult.html}`,
sign: chartOptionsSign + tableResult.sign,
html: tableResult.html
.replace(/(^<div .*?>)/, `$1${chartHtml}`)
.replace(/(^<div .*? data-sign=")[^"]+?"/, `$1${newSign}"`),
sign: newSign,
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import Cherry from './index.core';
import MermaidCodeEngine from '@/addons/cherry-code-block-mermaid-plugin';
import PlantUMLCodeEngine from '@/addons/cherry-code-block-plantuml-plugin';
import EChartsTableEngine from '@/addons/advance/cherry-table-echarts-plugin';
import mermaid from 'mermaid';

const mermaidAPI = mermaid?.mermaidAPI;
Expand All @@ -25,6 +26,7 @@ Cherry.usePlugin(MermaidCodeEngine, {
sequence: { useMaxWidth: false },
});
Cherry.usePlugin(PlantUMLCodeEngine, {});
Cherry.usePlugin(EChartsTableEngine);

export * from './index.core';
export default Cherry;
3 changes: 2 additions & 1 deletion src/sass/ch-icon.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@
.ch-icon-justifyLeft:before { content: "\EA6E" }
.ch-icon-justifyRight:before { content: "\EA6F" }
.ch-icon-chevronsLeft:before { content: "\EA70" }
.ch-icon-chevronsRight:before { content: "\EA71" }
.ch-icon-chevronsRight:before { content: "\EA71" }
.ch-icon-trendingUp:before { content: "\EA72" }
1 change: 1 addition & 0 deletions src/sass/icons/uEA72-trendingUp.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e1df984

Please sign in to comment.