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

	// Adds Atlassian shapes
	// 图表
	Sidebar.prototype.addChartsPalette = function () {
		this.chartsComponentInit()
		const enumConst = this.enumConst
		const enumChartType = this.enumChartType
		const self = this

		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 = self.generatorChartsId()
				const cell = self.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 = self.generatorChartsId()
				const cell = self.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();
	};

	/**
	 * @description echarts 实例映射
	 * @type {Map<any, any>}
	 */
	Sidebar.prototype.chartsInstanceMapping = new Map()

	/**
	 * @description Sidebar 类型枚举
	 * @type {{CHART_CELL_DEFAULT_WIDTH: number, CHART_CELL_HEIGHT: string, CHART_CELL_DEFAULT_HEIGHT: number, CHART_TYPE_KEY: string, CHART_CELL_WIDTH: string, CHART_COMP: string, CHART_CELL_ID: string, CHART_CONTAINER_ID_PREFIX: string, CHART_IMG_PLACEHOLDER_SIZE: number, COMPONENTS_TYPE_KEY: string, CHART_CONTAINER_CLS: string}}
	 */
	Sidebar.prototype.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',
	}

	/**
	 * @description 图表类型值
	 * @type {{LINE_CHART: string, BAR_CHART: string}}
	 */
	Sidebar.prototype.enumChartType = {
		LINE_CHART: 'lineChart',
		BAR_CHART: 'barChart',
	}

	/**
	 * @description 图表options 映射
	 * @type {{[p: string]: (function(): {yAxis: {type: string}, xAxis: {data, type: string}, series: [{data, type: string}]})|(function(): {yAxis: {type: string}, xAxis: {data, type: string}, series: [{data, showBackground: boolean, backgroundStyle: {color: string}, type: string}]})}}
	 */
	Sidebar.prototype.chartOptionMapping = {
		[Sidebar.prototype.enumChartType.LINE_CHART]: function () {
			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',
					},
				],
			}
		},
		[Sidebar.prototype.enumChartType.BAR_CHART]: function () {
			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 创建cell
	 * @param id
	 * @param width
	 * @param height
	 * @param chartType
	 * @param placeholderPath
	 * @returns {*}
	 */
	Sidebar.prototype.generatorCell = function (id, width, height, chartType, placeholderPath) {
		const enumConst = this.enumConst
		const cell = new mxCell(this.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)
		this.graph.setAttributeForCell(cell, enumConst.COMPONENTS_TYPE_KEY, 'charts');
		this.graph.setAttributeForCell(cell, enumConst.CHART_CELL_ID, id);
		this.graph.setAttributeForCell(cell, enumConst.CHART_CELL_WIDTH, width)
		this.graph.setAttributeForCell(cell, enumConst.CHART_CELL_HEIGHT, height)
		this.graph.setAttributeForCell(cell, enumConst.CHART_TYPE_KEY, chartType)
		return cell
	}


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

	/**
	 * @description 创建图表节点
	 * @param id
	 * @param width
	 * @param height
	 * @param placeholderPath
	 * @returns {string}
	 */
	Sidebar.prototype.createChartsNode = function (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 是否是图表cell
	 * @param cell
	 * @returns {boolean}
	 */
	Sidebar.prototype.isChartCell = function (cell) {
		const enumConst = Sidebar.prototype.enumConst
		const componentsType = cell.getAttribute(enumConst.COMPONENTS_TYPE_KEY)
		return !!(componentsType && componentsType === enumConst.CHART_COMP)
	}

	/**
	 * @description 创建图表实例 并 保存实例到map中
	 * @param id
	 * @param width
	 * @param height
	 * @param chartType
	 */
	Sidebar.prototype.generatorEChartInstance = function (id, width, height, chartType) {
		const chartOptionMapping = this.chartOptionMapping
		const chartsInstanceMapping = this.chartsInstanceMapping
		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 获取charts cell 的id
	 * @param cell
	 * @returns {*}
	 */
	Sidebar.prototype.getCellId = function (cell) {
		return this.graph.getAttributeForCell(cell, this.enumConst.CHART_CELL_ID)
	}

	/**
	 * @description 图表组件 拓展处理
	 */
	Sidebar.prototype.chartsComponentInit = function () {
		const chartsInstanceMapping = this.chartsInstanceMapping
		const enumConst = this.enumConst
		const graph = this.graph
		const self = this
		/**
		 * @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' && this.isChartCell(cell)

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

			/**
			 * @description 拓展放置图表
			 * @type {ds.mouseUp | Function}
			 */
			const mouseUp = ds.mouseUp
			ds.mouseUp = function () {
				try {
					mouseUp.apply(this, arguments)
				} finally {
					if (validate) {
						const id = self.getCellId(cell)
						const chartType = cell.getAttribute(enumConst.CHART_TYPE_KEY)
						self.generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)

					}
				}
			}
			addClickHandler.apply(this, arguments)
		}

		/**
		 * @description charts cell发生resize时改变charts size
		 * @type {Function}
		 */
		const cellResized = mxGraph.prototype.cellResized
		mxGraph.prototype.cellResized = function (cell, rect) {
			if (Sidebar.prototype.isChartCell(cell)) {
				const { width, height } = rect
				const id = self.getCellId(cell)
				const chartDom = document.getElementById(id)
				chartDom.style.width = `${ width }px`
				chartDom.style.height = `${ height }px`
				self.graph.setAttributeForCell(cell, enumConst.CHART_CELL_WIDTH, width)
				self.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 = self.getCellId(cell)
			chartsInstanceMapping.delete(id)
			return deleteCells.apply(this, arguments)
		}


		/**
		 * @description 拓展Sidebar 提示框
		 */
		const createTooltip = Sidebar.prototype.createTooltip
		Sidebar.prototype.createTooltip = function (elt, cells) {
			const id = self.generatorChartsId()
			const enumConst = self.enumConst
			const validateFlag = self.isChartCell(cells[0])
			const chartType = graph.getAttributeForCell(cells[0], enumConst.CHART_TYPE_KEY)
			try {
				if (validateFlag) {
					const cell = self.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) self.generatorEChartInstance(id, enumConst.CHART_CELL_DEFAULT_WIDTH, enumConst.CHART_CELL_DEFAULT_HEIGHT, chartType)
			}
		}
	}

	/**
	 * @description 初始化图表
	 */
	const openFileHandle = EditorUi.prototype.openFileHandle
	EditorUi.prototype.openFileHandle = function () {
		const graph = this.editor.graph
		const enumConst = Sidebar.prototype.enumConst
		try {
			openFileHandle.apply(this, arguments)
		} finally {
			const allCell = graph.getDefaultParent().children
			const domIdMapping = new Map()
			for (const cell of allCell) {
				const chartInstanceId = graph.getAttributeForCell(cell, enumConst.CHART_CELL_ID)
				if (Sidebar.prototype.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)
				Sidebar.prototype.generatorEChartInstance(id, width, height, chartType)
			}
		}
	}

})();