Sidebar-Charts.js 11.9 KB
(function () {

	// Adds Atlassian shapes
	// 图表
	Sidebar.prototype.addChartsPalette = function () {
		const { enumConst, enumChartType, generatorCell, generatorChartsId } = this.chartsComponentExtend()

		const s = 'html=1;shadow=0;dashed=0;shape=mxgraph.atlassian.';
		const s2 = 'html=1;shadow=0;dashed=0;fillColor=none;strokeColor=none;shape=mxgraph.bootstrap.rect;';
		const s3 = mxConstants.STYLE_STROKEWIDTH + '=1;shadow=0;dashed=0;align=center;html=1;' + mxConstants.STYLE_SHAPE + "=mxgraph.mockup.";
		const gn = 'mxgraph.charts';
		const dt = 'charts ';
		const sb = this;
		this.setCurrentSearchEntryLibrary('charts');

		const fns = [
			this.addEntry('line chart', mxUtils.bind(this, function () {
				const id = generatorChartsId()
				const cell = generatorCell(id, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumChartType.LINE_CHART)
				return this.createVertexTemplateFromCells([ cell ], cell.geometry.width, cell.geometry.height, '折线图');
			})),
			this.addEntry('bar chart', mxUtils.bind(this, function () {
				const id = generatorChartsId()
				const cell = generatorCell(id, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumConst.CHART_IMG_PLACEHOLDER_SIZE, enumChartType.BAR_CHART, '/thingskit-drawio/images/thingskit/bar-chart.png')
				return this.createVertexTemplateFromCells([ cell ], cell.geometry.width, cell.geometry.height, '柱状图');
			})),
		];

		this.addPaletteFunctions('charts', '图表', false, fns);

		this.setCurrentSearchEntryLibrary();
	};

	Sidebar.prototype.chartsInstanceMapping = new Map()

	/**
	 * @description 图表组件 拓展
	 * @returns {{generatorChartsId: (function(): string), createChartsNode: (function(*, *=, *=): string), chartsLineChartsConfig: (function(): {yAxis: {type: string}, xAxis: {data, type: string}, series: [{data, type: string}]}), generatorCell: (function(*, *, *): *), chartsComponentInit: chartsComponentInit}}
	 */
	Sidebar.prototype.chartsComponentExtend = function () {
		const enumConst = {

			/**
			 * @description 图表cell 默认宽度
			 */
			CHART_CELL_DEFAULT_WIDTH: 400,

			/**
			 * @description 图表cell 默认高度
			 */
			CHART_CELL_DEFAULT_HEIGHT: 400,

			/**
			 * @description 图表cell的 width attribute
			 */
			CHART_CELL_WIDTH: 'width',

			/**
			 * @description 图表cell的 height attribute
			 */
			CHART_CELL_HEIGHT: 'height',

			/**
			 * @description cell 类型是否为 charts
			 */
			CHART_COMP: 'charts',

			/**
			 * @description cell id key
			 */
			CHART_CELL_ID: 'chartInstanceId',

			/**
			 * @description 组件类型 key
			 */
			COMPONENTS_TYPE_KEY: 'componentsType',

			/**
			 * @description 图表容器 class name
			 */
			CHART_CONTAINER_CLS: 'echarts__instance',

			/**
			 * @description 图表容器  id 前缀
			 */
			CHART_CONTAINER_ID_PREFIX: 'echarts__instance__',

			/**
			 * @description 图表图片占位符大小
			 */
			CHART_IMG_PLACEHOLDER_SIZE: 30,

			/**
			 * @description 图表类型 key
			 */
			CHART_TYPE_KEY: 'chartType',
		}

		const enumChartType = {
			LINE_CHART: 'lineChart',
			BAR_CHART: 'barChart',
		}

		const chartOptionMapping = {
			[enumChartType.LINE_CHART]: chartsLineChartsConfig,
			[enumChartType.BAR_CHART]: chartsBarChartConfig,
		}

		const graph = this.graph

		chartsComponentInit()

		/**
		 * @description 创建cell
		 * @param id
		 * @param width
		 * @param height
		 * @param chartType
		 * @param placeholderPath
		 * @returns {*}
		 */
		function generatorCell(id, width, height, chartType, placeholderPath) {
			const cell = new mxCell(createChartsNode(id, width, height, placeholderPath), new mxGeometry(0, 0, width, height), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;');
			cell.setVertex(true)
			graph.setAttributeForCell(cell, enumConst.COMPONENTS_TYPE_KEY, 'charts');
			graph.setAttributeForCell(cell, enumConst.CHART_CELL_ID, id);
			graph.setAttributeForCell(cell, enumConst.CHART_CELL_WIDTH, width)
			graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, height)
			graph.setAttributeForCell(cell, enumConst.CHART_TYPE_KEY, chartType)
			return cell
		}

		/**
		 * @description 生成图表 id
		 * @returns {string}
		 */
		function generatorChartsId() {
			return `${ enumConst.CHART_CONTAINER_ID_PREFIX }${ Date.now() }`
		}

		/**
		 * @description 创建图表节点
		 * @param id
		 * @param width
		 * @param height
		 * @param placeholderPath
		 * @returns {string}
		 */
		function createChartsNode(id, width = 400, height = 400, placeholderPath = '/thingskit-drawio/images/thingskit/line-chart.png') {
			return `<div class="echarts__instance" style="width: ${ width }px; height: ${ height }px" id="${ id }"><img src="${ placeholderPath }" alt=""></div>`
		}

		/**
		 * @description 折线图配置
		 * @returns {{yAxis: {type: string}, xAxis: {data: string[], type: string}, series: [{data: number[], type: string}]}}
		 */
		function chartsLineChartsConfig() {
			return {
				xAxis: {
					type: 'category',
					data: [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
				},
				yAxis: {
					type: 'value',
				},
				series: [
					{
						data: [ 150, 230, 224, 218, 135, 147, 260 ],
						type: 'line',
					},
				],
			}
		}

		/**
		 * @description 柱状图
		 * @returns {{yAxis: {type: string}, xAxis: {data: string[], type: string}, series: [{data: number[], showBackground: boolean, backgroundStyle: {color: string}, type: string}]}}
		 */
		function chartsBarChartConfig() {
			return {
				xAxis: {
					type: 'category',
					data: [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ],
				},
				yAxis: {
					type: 'value',
				},
				series: [
					{
						data: [ 120, 200, 150, 80, 70, 110, 130 ],
						type: 'bar',
						showBackground: true,
						backgroundStyle: {
							color: 'rgba(180, 180, 180, 0.2)',
						},
					},
				],
			};
		}

		/**
		 * @description 图表组件 拓展处理
		 */
		function chartsComponentInit() {
			const chartsInstanceMapping = Sidebar.prototype.chartsInstanceMapping

			/**
			 * @description 拓展添加charts 实例方法
			 */
			const addClickHandler = Sidebar.prototype.addClickHandler
			Sidebar.prototype.addClickHandler = function (elt, ds, cells) {
				const cell = cells[0]
				const cellValue = cell.value
				const validate = cellValue && cellValue.nodeName === 'UserObject' && isChartCell(cell)

				/**
				 * @description 拓展Sidebar鼠标按下
				 * @type {ds.mouseDown}
				 */
				const mouseDown = ds.mouseDown
				ds.mouseDown = function (evt) {
					if (validate) {
						const id = generatorChartsId()
						const geo = Object.assign(graph.model.getGeometry(cell), { width: 400, height: 400 })
						cell.setGeometry(geo)
						graph.setAttributeForCell(cell, enumConst.CHART_CELL_ID, id);
						graph.setAttributeForCell(cell, enumConst.CHART_CELL_WIDTH, enumConst.CHART_CELL_DEFAULT_WIDTH);
						graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, enumConst.CHART_CELL_DEFAULT_HEIGHT);
						graph.setAttributeForCell(cell, 'label', createChartsNode(id))
					}
					mouseDown.apply(this, arguments)
				};

				/**
				 * @description 拓展放置图表
				 * @type {ds.mouseUp}
				 */
				const mouseUp = ds.mouseUp
				ds.mouseUp = function () {
					try {
						mouseUp.apply(this, arguments)
					} finally {
						if (validate) {
							const id = getCellId(cell)
							const chartType = graph.getAttributeForCell(cell, enumConst.CHART_TYPE_KEY)
							const chartDom = document.getElementById(id);
							const myChart = echarts.init(chartDom);
							const option = chartOptionMapping[chartType] ? chartOptionMapping[chartType]() : {}
							option && myChart.setOption(option);
							chartsInstanceMapping.set(id, myChart)
						}
					}
				}
				addClickHandler.apply(this, arguments)
			}

			/**
			 * @description charts cell发生resize时改变charts size
			 * @type {Function}
			 */
			const cellResized = mxGraph.prototype.cellResized
			mxGraph.prototype.cellResized = function (cell, rect) {
				const { width, height } = rect
				const id = getCellId(cell)
				const chartDom = document.getElementById(id)
				chartDom.style.width = `${ width }px`
				chartDom.style.height = `${ height }px`
				graph.setAttributeForCell(cell, enumConst.CHART_CELL_WIDTH, width)
				graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, height)
				const instance = chartsInstanceMapping.get(id)
				instance.resize()
				cellResized.apply(this, arguments)
			}

			/**
			 * @description 删除charts cell 时 删除保存的charts 实例
			 */
			const deleteCells = Graph.prototype.deleteCells
			Graph.prototype.deleteCells = function (cell) {
				const id = getCellId(cell)
				chartsInstanceMapping.delete(id)
				return deleteCells.apply(this, arguments)
			}

			/**
			 * @description 获取charts cell 的id
			 * @param cell
			 * @returns {*}
			 */
			function getCellId(cell) {
				return graph.getAttributeForCell(cell, enumConst.CHART_CELL_ID)
			}

			/**
			 * @description 创建图表实例 并 保存实例到map中
			 * @param id
			 * @param width
			 * @param height
			 * @param chartType
			 */
			function generatorEChartInstance(id, width, height, chartType) {
				const chartDom = document.getElementById(id);
				chartDom.style.width = `${ width }px`
				chartDom.style.height = `${ height }px`
				const myChart = echarts.init(chartDom);
				const option = chartOptionMapping[chartType] ? chartOptionMapping[chartType]() : {}
				option && myChart.setOption(option);
				chartsInstanceMapping.set(id, myChart)
			}

			/**
			 * @description 初始化图表
			 */
			const openFileHandle = EditorUi.prototype.openFileHandle
			EditorUi.prototype.openFileHandle = function () {
				try {
					openFileHandle.apply(this, arguments)
				} finally {
					const allCell = this.editor.graph.getDefaultParent().children
					const domIdMapping = new Map()
					for (const cell of allCell) {
						const chartInstanceId = graph.getAttributeForCell(cell, enumConst.CHART_CELL_ID)
						if (isChartCell(cell) && chartInstanceId) {
							const width = graph.getAttributeForCell(cell, enumConst.CHART_CELL_WIDTH)
							const height = graph.getAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT)
							const chartType = graph.getAttributeForCell(cell, enumConst.CHART_TYPE_KEY)
							domIdMapping.set(chartInstanceId, { width, height, chartType })
						}
					}
					const chartsDomList = document.querySelectorAll(`.${ enumConst.CHART_CONTAINER_CLS }`)
					for (const chartDom of chartsDomList) {
						const id = chartDom.getAttribute('id')
						const { width, height, chartType } = domIdMapping.get(id)
						console.log(chartType)
						generatorEChartInstance(id, width, height, chartType)
					}
				}
			}

			/**
			 * @description 是否是图表cell
			 * @param cell
			 * @returns {boolean}
			 */
			function isChartCell(cell) {
				const componentsType = graph.getAttributeForCell(cell, enumConst.COMPONENTS_TYPE_KEY)
				return !!(componentsType && componentsType === enumConst.CHART_COMP)
			}

			/**
			 * @description 拓展Sidebar 提示框
			 */
			const createTooltip = Sidebar.prototype.createTooltip
			Sidebar.prototype.createTooltip = function (elt, cells) {
				const id = generatorChartsId()
				const validateFlag = isChartCell(cells[0])
				const chartType = graph.getAttributeForCell(cells[0], enumConst.CHART_TYPE_KEY)
				try {
					if (validateFlag) {
						const cell = generatorCell(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)
						const _arguments = Array.prototype.slice.call(arguments, 0)
						_arguments.splice(1, 1, [ cell ])
						createTooltip.apply(this, _arguments)
					} else {
						createTooltip.apply(this, arguments)
					}
				} finally {
					if (validateFlag) generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)
				}
			}
		}

		return {
			generatorCell,
			generatorChartsId,
			createChartsNode,
			chartsComponentInit,
			chartsLineChartsConfig,
			enumConst,
			enumChartType,
		}
	}

})();