diff --git a/bricks/build.sh b/bricks/build.sh index c14d71f..d29f95a 100755 --- a/bricks/build.sh +++ b/bricks/build.sh @@ -11,7 +11,7 @@ SOURCES=" page_data_loader.js factory.js uitypesdef.js utils.js uitype.js \ llm.js websocket.js datarow.js tabular.js continueaudio.js \ line.js pie.js bar.js gobang.js period.js iconbarpage.js \ keypress.js asr.js webspeech.js countdown.js progressbar.js \ - qaframe.js svg.js videoplayer.js " + qaframe.js svg.js videoplayer.js sctter.js radar.js kline.js heatmap.js map.js" echo ${SOURCES} cat ${SOURCES} > ../dist/bricks.js # uglifyjs --compress --mangle -- ../dist/bricks.js > ../dist/bricks.min.js diff --git a/bricks/heatmap.js b/bricks/heatmap.js new file mode 100644 index 0000000..748ecd6 --- /dev/null +++ b/bricks/heatmap.js @@ -0,0 +1,73 @@ +var bricks = window.bricks || {}; + +bricks.ChartHeatmap = class extends bricks.EchartsExt { + /* + 数据格式: + [ + { x: '周一', y: '上午', value: 120 }, + { x: '周二', y: '下午', value: 90 }, ... + ] + + 参数: + { + data_url, + xField: 'x', // X 轴字段 + yField: 'y', // Y 轴字段 + valueField: 'value' + } + */ + setup_options(data) { + const xField = this.xField || 'x'; + const yField = this.yField || 'y'; + const valueField = this.valueField || 'value'; + + const xs = [...new Set(data.map(d => d[xField]))]; + const ys = [...new Set(data.map(d => d[yField]))]; + + const map = {}; + ys.forEach(y => map[y] = {}); + data.forEach(d => { + map[d[yField]][d[xField]] = d[valueField]; + }); + + const heatmapData = []; + for (let j = 0; j < ys.length; j++) { + const y = ys[j]; + for (let i = 0; i < xs.length; i++) { + const x = xs[i]; + heatmapData.push([i, j, map[y][x] || 0]); + } + } + + return { + tooltip: { position: 'top' }, + grid: { height: '80%', top: '10%' }, + xAxis: { + type: 'category', + data: xs, + splitArea: { show: true } + }, + yAxis: { + type: 'category', + data: ys, + splitArea: { show: true } + }, + visualMap: { + min: 0, + max: Math.max(...data.map(d => d[valueField])), + calculable: true, + orient: 'horizontal', + left: 'center', + bottom: '5%' + }, + series: [{ + type: 'heatmap', + data: heatmapData, + label: { show: false }, + emphasis: { itemStyle: { shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)' } } + }] + }; + } +}; + +bricks.Factory.register('ChartHeatmap', bricks.ChartHeatmap); diff --git a/bricks/kline.js b/bricks/kline.js new file mode 100644 index 0000000..c1b9493 --- /dev/null +++ b/bricks/kline.js @@ -0,0 +1,49 @@ +var bricks = window.bricks || {}; + +bricks.ChartKLine = class extends bricks.EchartsExt { + /* + 数据格式: + [ + { name: '2025-04-01', open: 2100, close: 2150, low: 2080, high: 2160 }, + ... + ] + + 参数: + { + data_url, + nameField: 'name', // 时间轴字段 + valueFields: ['open','close','low','high'] + } + */ + setup_options(data) { + const { nameField } = this; + const categories = data.map(d => d[nameField]); + const kData = data.map(d => [d.open, d.close, d.low, d.high]); + + return { + tooltip: { + trigger: 'axis', + axisPointer: { type: 'cross' } + }, + xAxis: { + type: 'category', + data: categories, + scale: true + }, + yAxis: { + type: 'value', + scale: true + }, + series: [{ + type: 'candlestick', + data: kData + }], + dataZoom: [ + { type: 'inside' }, + { type: 'slider' } + ] + }; + } +}; + +bricks.Factory.register('ChartKLine', bricks.ChartKLine); diff --git a/bricks/map.js b/bricks/map.js new file mode 100644 index 0000000..8e951d6 --- /dev/null +++ b/bricks/map.js @@ -0,0 +1,65 @@ +var bricks = window.bricks || {}; + +bricks.ChartMap = class extends bricks.EchartsExt { + /* + 数据格式: + [ + { name: '北京', value: 120 }, + { name: '上海', value: 90 }, ... + ] + + 参数: + { + data_url, + nameField: 'name', + valueField: 'value', + map_name: 'china', // 对应 echarts.registerMap 注册的地图名 + map_options: {} // 可选自定义样式 + } + */ + async setup_options(data) { + const { nameField, valueField = 'value', map_name = 'china', map_options } = this; + + const mapData = data.map(d => ({ + name: d[nameField], + value: d[valueField] + })); + + return { + tooltip: { + trigger: 'item', + formatter: '{b}: {c}' + }, + visualMap: { + min: 0, + max: Math.max(...mapData.map(d => d.value)), + left: 'left', + top: 'bottom', + text: ['高', '低'], + calculable: true + }, + series: [{ + name: '数据', + type: 'map', + map: map_name, + roam: false, + label: { show: true, color: '#fff' }, + itemStyle: { + areaColor: '#eee', + borderColor: '#444' + }, + emphasis: { + label: { color: '#fff' }, + itemStyle: { areaColor: '#e09191' } + }, + data: mapData + }], + ...(map_options || {}) + }; + } +}; + +bricks.Factory.register('ChartMap', bricks.ChartMap); +/* +💡 使用前需通过 `echarts.registerMap('china', geoJson)` 注册地图数据。 +*/ diff --git a/bricks/radar.js b/bricks/radar.js new file mode 100644 index 0000000..bf66f5a --- /dev/null +++ b/bricks/radar.js @@ -0,0 +1,54 @@ +var bricks = window.bricks || {}; + +bricks.ChartRadar = class extends bricks.EchartsExt { + /* + 数据格式示例: + [ + { name: '张三', indicator1: 80, indicator2: 60, ... }, + { name: '李四', indicator1: 70, indicator2: 90, ... } + ] + + 参数: + { + data_url, + data_params, + nameField: 'name', + valueFields: ['indicator1', 'indicator2', ...], + radar_options: { // 自定义雷达配置 + indicator: [ + { name: '销售', max: 100 }, + { name: '管理', max: 100 }, ... + ] + } + } + */ + setup_options(data) { + const { nameField, valueFields } = this; + const series = []; + const indicator = this.radar_options?.indicator || valueFields.map(f => ({ name: f, max: 100 })); + + data.forEach(item => { + series.push({ + name: item[nameField], + type: 'radar', + data: [ + { + value: valueFields.map(f => item[f]), + name: item[nameField] + } + ] + }); + }); + + return { + tooltip: { trigger: 'item' }, + legend: { data: data.map(d => d[nameField]) }, + radar: { indicator }, + series + }; + } +}; + +bricks.Factory.register('ChartRadar', bricks.ChartRadar); +/* +*/ diff --git a/bricks/scatter.js b/bricks/scatter.js new file mode 100644 index 0000000..6f83f28 --- /dev/null +++ b/bricks/scatter.js @@ -0,0 +1,98 @@ +var bricks = window.bricks || {}; + +bricks.ChartScatter = class extends bricks.EchartsExt { + /* + * opts: + * { + * data_url, + * data_params, + * method, + * user_data, + * nameField: 可选,用于标签 + * xField: X坐标字段 + * yField: Y坐标字段 + * sizeField?: 气泡大小字段(可选) + * categoryField?: 分组字段(用于不同颜色) + * } + */ + + setup_options(data) { + let seriesMap = {}; + + if (this.categoryField) { + // 按 categoryField 分组 + data.forEach(item => { + const cat = item[this.categoryField] || 'default'; + if (!seriesMap[cat]) { + seriesMap[cat] = { + name: cat, + type: 'scatter', + data: [] + }; + } + seriesMap[cat].data.push([ + item[this.xField], + item[this.yField], + item[this.nameField] || null, + item[this.sizeField] || 1 + ]); + }); + } else { + // 单一系列 + const seriesData = data.map(item => [ + item[this.xField], + item[this.yField], + item[this.nameField] || null, + item[this.sizeField] || 1 + ]); + + seriesMap['scatter'] = { + type: 'scatter', + data: seriesData + }; + } + + const series = Object.values(seriesMap); + const xAxisName = this.xField; + const yAxisName = this.yField; + + return { + tooltip: { + trigger: 'axis', + formatter: function(params) { + const p = params[0]; + return `${p.seriesName}
${xAxisName}: ${p.value[0]}
${yAxisName}: ${p.value[1]}`; + } + }, + legend: { + data: Object.keys(seriesMap) + }, + xAxis: { + type: 'value', + name: xAxisName + }, + yAxis: { + type: 'value', + name: yAxisName + }, + series: series + }; + } +}; + +// 注册工厂 +bricks.Factory.register('ChartScatter', bricks.ChartScatter); + +/* +new bricks.ChartScatter({ + user_data: [ + { x: 10, y: 20, size: 30, group: 'Group A', label: 'Point 1' }, + { x: 15, y: 25, size: 40, group: 'Group B', label: 'Point 2' } + ], + xField: 'x', + yField: 'y', + sizeField: 'size', + categoryField: 'group', + nameField: 'label' +}); +*/