图表切片器用例

对于复杂的场景,你可以向下面这样定制GeneralSlicerData

从GeneralSlicerData派生定义切片器数据类型。 重写 attachListener 和 dettachListener 方法。 重写 onFiltered 方法实现自己的的筛选逻辑。 创建自己的切片器。 创建自己的 onDataLoaded 方法。 创建自己的 onFiltered 方法。 创建自己的切片器数据并附加到你的切片器上。
<template> <div class="sample-tutorial"> <gc-spread-sheets class="sample-spreadsheets" @workbookInitialized="initSpread"> </gc-spread-sheets> <div class="options-container"> <div id="workers" class="sample-chart"></div> <div id="yearsOnList" class="sample-chart"></div> </div> </div> </template> <script setup> import '@grapecity-software/spread-sheets-vue' import { ref, nextTick } from "vue"; import GC from "@grapecity-software/spread-sheets"; import '@grapecity-software/spread-sheets-resources-zh'; GC.Spread.Common.CultureManager.culture("zh-cn"); const spreadRef = ref(null); let initSpread = function (spread) { spreadRef.value = spread; let sd = data; if (sd.length > 0) { if (sd) { if (!spread) { return; } let sheet = spread.getActiveSheet(); initSlicer(sheet, sd); } } } class ChartSlicerData extends GC.Spread.Slicers.GeneralSlicerData { constructor(datas, columnNames) { super(datas, columnNames); this.listeners = []; } onFiltered() { let self = this; self.listeners.forEach(function (listener) { listener.onFiltered({ dataIndexes: self.getFilteredRowIndexes() }); listener.refreshList(); }) } attachListener(listener) { this.listeners.push(listener); } detachListener(listener) { for (var i = 0; i < this.listeners.length; i++) { if (this.listeners[i] === listener) { this.listeners.splice(i); break; } } } } class ChartSlicer { constructor(container, columnName, sheet, slicerData, title, legend) { this.sheet = sheet; this.data = slicerData; this.container = container; this.columnName = columnName; this.slicerData = slicerData; this.title = title; this.legend = legend; this.xAxis = []; this.series = []; this.verticalChart = null; this.slicerData.attachListener(this); this.onDataLoaded(); } getXAxis = function () { let xAxis = []; let exclusiveData = this.slicerData.getExclusiveData(this.columnName); let maxValue = getMaxInArray(exclusiveData); if (this.columnName === "years on list") { let xAxisCount = 6, xTick = Math.floor(maxValue / xAxisCount); for (let i = 1; i <= xAxisCount; i++) { xAxis.push(xTick * i); } } else if (this.columnName === "workers") { if (typeof maxValue === "number") { let base = 1; while (maxValue > 1) { xAxis.push(Math.pow(10, base)); base++; maxValue = parseInt(maxValue / 10); } } } return xAxis; }; getSeriesByXAxisScope(xAxis, columnName, slicerData) { if (!xAxis || xAxis.length <= 0 || !slicerData) { return; } let series = [], data = slicerData.getData(columnName), filterdRowIndexes = slicerData.getFilteredRowIndexes(); for (let x = 0, len1 = xAxis.length; x < len1; x++) { let scopeStart = 0; if (x > 0) { scopeStart = xAxis[x - 1]; } let scopeEnd = xAxis[x]; let seriesItem = 0; for (let i = 0, len = filterdRowIndexes.length; i < len; i++) { let rowIndex = filterdRowIndexes[i], dataItem = data[rowIndex]; if (dataItem >= scopeStart && dataItem < scopeEnd) { seriesItem++; } } series.push(seriesItem); } return series; } onDataLoaded() { let self = this; let chartDiv = document.createElement('div'), footerDiv = document.createElement('div'); chartDiv.id = 'chart_div'; chartDiv.style.width = '100%'; chartDiv.style.height = '90%'; footerDiv.innerHTML = '<span id="text_span"></span>' + '<button>Remove Filter</button>'; let textSpan = footerDiv.firstChild; let removeFilter = footerDiv.lastChild; removeFilter.onclick = function (e) { self.slicerData.doUnfilter(self.columnName); footerDiv.style.display = 'none'; }; footerDiv.style.width = '100%'; footerDiv.style.height = '100%'; footerDiv.style.display = 'none'; self.container.appendChild(chartDiv); self.container.appendChild(footerDiv); this.xAxis = this.getXAxis(); this.series = this.getSeriesByXAxisScope(this.xAxis, this.columnName, this.slicerData); this.verticalChart = echarts.init(chartDiv); let option = { title: { subtext: this.title, }, legend: { data: [this.legend] }, grid: { x: "15%", width: "80%", x2: "5%", y: "30%", height: "59%", y2: "15%" }, xAxis: [{ type: 'category', data: this.xAxis, axisTick: { show: true, length: 2, lineStyle: { color: "#333", width: 1 } } }], yAxis: [{ type: 'value' }], series: [{ name: this.legend, type: "bar", data: this.series, itemStyle: { normal: { color: "#9fd5b7", label: { show: true } }, emphasis: { color: "#ababab" } } }] }; this.verticalChart.setOption(option); function clickHandler(param) { let dataIndex = param.dataIndex; let startScope = 0, endScope = self.xAxis[dataIndex]; if (dataIndex > 0) { startScope = self.xAxis[dataIndex - 1]; } let _footerDiv = self.container.lastChild; _footerDiv.firstChild.innerText = self.title + ': ' + startScope + ' to ' + endScope; _footerDiv.style.display = 'block'; let indexes = []; let exclusiveData = self.slicerData.getExclusiveData(self.columnName); for (let i = 0, len = exclusiveData.length; i < len; i++) { if (exclusiveData[i] >= startScope && exclusiveData[i] < endScope) { indexes.push(i); } } self.slicerData.doFilter(self.columnName, { exclusiveRowIndexes: indexes }); } this.verticalChart.on("click", clickHandler); } refreshList() { this.verticalChart.setSeries([{ data: this.getSeriesByXAxisScope(this.verticalChart.getOption().xAxis[0].data, this.columnName, this.slicerData) }]); } onFiltered(data) { let sheet = this.sheet; sheet.suspendPaint(); sheet.suspendEvent(); let filteredRowIndexs = data.dataIndexes; for (let r = 0, len = sheet.getRowCount(); r < len; r++) { sheet.setRowVisible(r, false); } for (let i = 0, len = filteredRowIndexs.length; i < len; i++) { sheet.setRowVisible(filteredRowIndexs[i], true); } sheet.resumeEvent(); sheet.resumePaint(); } } const initSlicer = (sheet, datas) => { sheet.suspendPaint(); sheet.name("The 2014 Inc. 5000."); sheet.setDataSource(datas); sheet.setColumnCount(20); sheet.resumePaint(); let ret = parseJSONToArray(datas); let slicerData = new ChartSlicerData(ret.arrayDatas, ret.columnNames); let yearsOnListChart = new ChartSlicer(document.getElementById('yearsOnList'), "years on list", sheet, slicerData, "Years on the List", "Number of companies"); let workersChart = new ChartSlicer(document.getElementById('workers'), "workers", sheet, slicerData, "Workers", "Number of companies"); } const getMaxInArray = (array) => { if (!array || array.length <= 0) { return; } var max = array[0]; for (var i = 1, len = array.length; i < len; i++) { if (array[i] > max) { max = array[i]; } } return max; } const parseJSONToArray = (datas) => { if (!datas || datas.length <= 0) { return; } var columnNames = Object.keys(datas[0]), keyCount = columnNames.length, arrayDatas = []; for (var i = 0, len = datas.length; i < len; i++) { var data = datas[i]; if (data) { var dataItem = []; arrayDatas.push(dataItem); for (var j = 0; j < keyCount; j++) { dataItem.push(data[columnNames[j]]); } } } return { columnNames: columnNames, arrayDatas: arrayDatas }; } </script> <style scoped> #app { height: 100%; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 300px); height: 100%; overflow: auto; float: left; } .options-container { float: right; width: 290px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .sample-chart { width: 100%; height: 48%; box-sizing: border-box; } button { background: #9fd5b7; border: none; border-radius: 0; margin-left: 3px; } button:hover { background: #ababab; border: none; border-radius: 0; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } </style>
<!DOCTYPE html> <html lang="en" style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>SpreadJS VUE</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/vue3/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/zh/vue3/node_modules/systemjs/dist/system.src.js"></script> <script src="$DEMOROOT$/spread/source/js/external/echart/echarts.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/data/chartSlicer.js" type="text/javascript"></script> <script src="./systemjs.config.js"></script> <script src="https://cdn.grapecity.com.cn/online/resources/compiler-sfc.esm-browser.js" type="module" defer></script> <script> var System = SystemJS; System.import("./src/app.js"); System.import('$DEMOROOT$/zh/lib/vue3/license.js'); </script> </head> <body> <div id="app"></div> </body> </html>
(function (global) { SystemJS.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, paths: { // paths serve as alias 'npm:': 'node_modules/', 'cdn:': 'https://cdn.grapecity.com.cn/SpreadJS/package-contents/18.2.4/' }, packageConfigPaths: [ './node_modules/*/package.json', "./node_modules/@grapecity-software/*/package.json", "./node_modules/@babel/*/package.json", "./node_modules/@vue/*/package.json" ], map: { '@grapecity-software/spread-sheets': 'cdn:spread-sheets/index.js', '@grapecity-software/spread-sheets-resources-zh': 'cdn:spread-sheets-resources-zh/index.js', '@grapecity-software/spread-sheets-shapes': 'cdn:spread-sheets-shapes/index.js', '@grapecity-software/spread-sheets-slicers': 'cdn:spread-sheets-slicers/index.js', '@grapecity-software/spread-sheets-vue': 'cdn:spread-sheets-vue/index.js', 'vue': "npm:vue/dist/vue.esm-browser.js", 'tiny-emitter': 'npm:tiny-emitter/index.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', "systemjs-babel-build": "npm:systemjs-plugin-babel/systemjs-babel-browser.js", }, meta: { '*.css': { loader: 'systemjs-plugin-css' }, '*.vue': { loader: "../plugin-vue/index.js" } } }); })(this);