逐步完成前后端服务器

This commit is contained in:
2025-09-09 15:00:30 +08:00
parent fcb09432e9
commit c7dfa1e9fc
2937 changed files with 1477567 additions and 0 deletions

View File

@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 { __extends } from "tslib";
import * as zrUtil from 'zrender/lib/core/util.js';
import createSeriesDataSimply from '../helper/createSeriesDataSimply.js';
import { defaultEmphasis } from '../../util/model.js';
import { makeSeriesEncodeForNameBased } from '../../data/helper/sourceHelper.js';
import LegendVisualProvider from '../../visual/LegendVisualProvider.js';
import SeriesModel from '../../model/Series.js';
var FunnelSeriesModel = /** @class */function (_super) {
__extends(FunnelSeriesModel, _super);
function FunnelSeriesModel() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = FunnelSeriesModel.type;
return _this;
}
FunnelSeriesModel.prototype.init = function (option) {
_super.prototype.init.apply(this, arguments);
// Enable legend selection for each data item
// Use a function instead of direct access because data reference may changed
this.legendVisualProvider = new LegendVisualProvider(zrUtil.bind(this.getData, this), zrUtil.bind(this.getRawData, this));
// Extend labelLine emphasis
this._defaultLabelLine(option);
};
FunnelSeriesModel.prototype.getInitialData = function (option, ecModel) {
return createSeriesDataSimply(this, {
coordDimensions: ['value'],
encodeDefaulter: zrUtil.curry(makeSeriesEncodeForNameBased, this)
});
};
FunnelSeriesModel.prototype._defaultLabelLine = function (option) {
// Extend labelLine emphasis
defaultEmphasis(option, 'labelLine', ['show']);
var labelLineNormalOpt = option.labelLine;
var labelLineEmphasisOpt = option.emphasis.labelLine;
// Not show label line if `label.normal.show = false`
labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show;
labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show;
};
// Overwrite
FunnelSeriesModel.prototype.getDataParams = function (dataIndex) {
var data = this.getData();
var params = _super.prototype.getDataParams.call(this, dataIndex);
var valueDim = data.mapDimension('value');
var sum = data.getSum(valueDim);
// Percent is 0 if sum is 0
params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2);
params.$vars.push('percent');
return params;
};
FunnelSeriesModel.type = 'series.funnel';
FunnelSeriesModel.defaultOption = {
// zlevel: 0, // 一级层叠
z: 2,
legendHoverLink: true,
colorBy: 'data',
left: 80,
top: 60,
right: 80,
bottom: 60,
// width: {totalWidth} - left - right,
// height: {totalHeight} - top - bottom,
// 默认取数据最小最大值
// min: 0,
// max: 100,
minSize: '0%',
maxSize: '100%',
sort: 'descending',
orient: 'vertical',
gap: 0,
funnelAlign: 'center',
label: {
show: true,
position: 'outer'
// formatter: 标签文本格式器同Tooltip.formatter不支持异步回调
},
labelLine: {
show: true,
length: 20,
lineStyle: {
// color: 各异,
width: 1
}
},
itemStyle: {
// color: 各异,
borderColor: '#fff',
borderWidth: 1
},
emphasis: {
label: {
show: true
}
},
select: {
itemStyle: {
borderColor: '#212121'
}
}
};
return FunnelSeriesModel;
}(SeriesModel);
export default FunnelSeriesModel;

View File

@ -0,0 +1,198 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 { __extends } from "tslib";
import * as graphic from '../../util/graphic.js';
import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states.js';
import ChartView from '../../view/Chart.js';
import { setLabelLineStyle, getLabelLineStatesModels } from '../../label/labelGuideHelper.js';
import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle.js';
import { saveOldStyle } from '../../animation/basicTransition.js';
var opacityAccessPath = ['itemStyle', 'opacity'];
/**
* Piece of pie including Sector, Label, LabelLine
*/
var FunnelPiece = /** @class */function (_super) {
__extends(FunnelPiece, _super);
function FunnelPiece(data, idx) {
var _this = _super.call(this) || this;
var polygon = _this;
var labelLine = new graphic.Polyline();
var text = new graphic.Text();
polygon.setTextContent(text);
_this.setTextGuideLine(labelLine);
_this.updateData(data, idx, true);
return _this;
}
FunnelPiece.prototype.updateData = function (data, idx, firstCreate) {
var polygon = this;
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var emphasisModel = itemModel.getModel('emphasis');
var opacity = itemModel.get(opacityAccessPath);
opacity = opacity == null ? 1 : opacity;
if (!firstCreate) {
saveOldStyle(polygon);
}
// Update common style
polygon.useStyle(data.getItemVisual(idx, 'style'));
polygon.style.lineJoin = 'round';
if (firstCreate) {
polygon.setShape({
points: layout.points
});
polygon.style.opacity = 0;
graphic.initProps(polygon, {
style: {
opacity: opacity
}
}, seriesModel, idx);
} else {
graphic.updateProps(polygon, {
style: {
opacity: opacity
},
shape: {
points: layout.points
}
}, seriesModel, idx);
}
setStatesStylesFromModel(polygon, itemModel);
this._updateLabel(data, idx);
toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled'));
};
FunnelPiece.prototype._updateLabel = function (data, idx) {
var polygon = this;
var labelLine = this.getTextGuideLine();
var labelText = polygon.getTextContent();
var seriesModel = data.hostModel;
var itemModel = data.getItemModel(idx);
var layout = data.getItemLayout(idx);
var labelLayout = layout.label;
var style = data.getItemVisual(idx, 'style');
var visualColor = style.fill;
setLabelStyle(
// position will not be used in setLabelStyle
labelText, getLabelStatesModels(itemModel), {
labelFetcher: data.hostModel,
labelDataIndex: idx,
defaultOpacity: style.opacity,
defaultText: data.getName(idx)
}, {
normal: {
align: labelLayout.textAlign,
verticalAlign: labelLayout.verticalAlign
}
});
polygon.setTextConfig({
local: true,
inside: !!labelLayout.inside,
insideStroke: visualColor,
// insideFill: 'auto',
outsideFill: visualColor
});
var linePoints = labelLayout.linePoints;
labelLine.setShape({
points: linePoints
});
polygon.textGuideLineConfig = {
anchor: linePoints ? new graphic.Point(linePoints[0][0], linePoints[0][1]) : null
};
// Make sure update style on labelText after setLabelStyle.
// Because setLabelStyle will replace a new style on it.
graphic.updateProps(labelText, {
style: {
x: labelLayout.x,
y: labelLayout.y
}
}, seriesModel, idx);
labelText.attr({
rotation: labelLayout.rotation,
originX: labelLayout.x,
originY: labelLayout.y,
z2: 10
});
setLabelLineStyle(polygon, getLabelLineStatesModels(itemModel), {
// Default use item visual color
stroke: visualColor
});
};
return FunnelPiece;
}(graphic.Polygon);
var FunnelView = /** @class */function (_super) {
__extends(FunnelView, _super);
function FunnelView() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = FunnelView.type;
_this.ignoreLabelLineUpdate = true;
return _this;
}
FunnelView.prototype.render = function (seriesModel, ecModel, api) {
var data = seriesModel.getData();
var oldData = this._data;
var group = this.group;
data.diff(oldData).add(function (idx) {
var funnelPiece = new FunnelPiece(data, idx);
data.setItemGraphicEl(idx, funnelPiece);
group.add(funnelPiece);
}).update(function (newIdx, oldIdx) {
var piece = oldData.getItemGraphicEl(oldIdx);
piece.updateData(data, newIdx);
group.add(piece);
data.setItemGraphicEl(newIdx, piece);
}).remove(function (idx) {
var piece = oldData.getItemGraphicEl(idx);
graphic.removeElementWithFadeOut(piece, seriesModel, idx);
}).execute();
this._data = data;
};
FunnelView.prototype.remove = function () {
this.group.removeAll();
this._data = null;
};
FunnelView.prototype.dispose = function () {};
FunnelView.type = 'funnel';
return FunnelView;
}(ChartView);
export default FunnelView;

View File

@ -0,0 +1,344 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 * as layout from '../../util/layout.js';
import { parsePercent, linearMap } from '../../util/number.js';
import { isFunction } from 'zrender/lib/core/util.js';
function getViewRect(seriesModel, api) {
return layout.getLayoutRect(seriesModel.getBoxLayoutParams(), {
width: api.getWidth(),
height: api.getHeight()
});
}
function getSortedIndices(data, sort) {
var valueDim = data.mapDimension('value');
var valueArr = data.mapArray(valueDim, function (val) {
return val;
});
var indices = [];
var isAscending = sort === 'ascending';
for (var i = 0, len = data.count(); i < len; i++) {
indices[i] = i;
}
// Add custom sortable function & none sortable opetion by "options.sort"
if (isFunction(sort)) {
indices.sort(sort);
} else if (sort !== 'none') {
indices.sort(function (a, b) {
return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
});
}
return indices;
}
function labelLayout(data) {
var seriesModel = data.hostModel;
var orient = seriesModel.get('orient');
data.each(function (idx) {
var itemModel = data.getItemModel(idx);
var labelModel = itemModel.getModel('label');
var labelPosition = labelModel.get('position');
var labelLineModel = itemModel.getModel('labelLine');
var layout = data.getItemLayout(idx);
var points = layout.points;
var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';
var textAlign;
var textX;
var textY;
var linePoints;
if (isLabelInside) {
if (labelPosition === 'insideLeft') {
textX = (points[0][0] + points[3][0]) / 2 + 5;
textY = (points[0][1] + points[3][1]) / 2;
textAlign = 'left';
} else if (labelPosition === 'insideRight') {
textX = (points[1][0] + points[2][0]) / 2 - 5;
textY = (points[1][1] + points[2][1]) / 2;
textAlign = 'right';
} else {
textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
textAlign = 'center';
}
linePoints = [[textX, textY], [textX, textY]];
} else {
var x1 = void 0;
var y1 = void 0;
var x2 = void 0;
var y2 = void 0;
var labelLineLen = labelLineModel.get('length');
if (process.env.NODE_ENV !== 'production') {
if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -1) {
labelPosition = 'left';
console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');
}
if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition) > -1) {
labelPosition = 'bottom';
console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');
}
}
if (labelPosition === 'left') {
// Left side
x1 = (points[3][0] + points[0][0]) / 2;
y1 = (points[3][1] + points[0][1]) / 2;
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
} else if (labelPosition === 'right') {
// Right side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'left';
} else if (labelPosition === 'top') {
// Top side
x1 = (points[3][0] + points[0][0]) / 2;
y1 = (points[3][1] + points[0][1]) / 2;
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
} else if (labelPosition === 'bottom') {
// Bottom side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
} else if (labelPosition === 'rightTop') {
// RightTop side
x1 = orient === 'horizontal' ? points[3][0] : points[1][0];
y1 = orient === 'horizontal' ? points[3][1] : points[1][1];
if (orient === 'horizontal') {
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
} else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'top';
}
} else if (labelPosition === 'rightBottom') {
// RightBottom side
x1 = points[2][0];
y1 = points[2][1];
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
} else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'bottom';
}
} else if (labelPosition === 'leftTop') {
// LeftTop side
x1 = points[0][0];
y1 = orient === 'horizontal' ? points[0][1] : points[1][1];
if (orient === 'horizontal') {
y2 = y1 - labelLineLen;
textY = y2 - 5;
textAlign = 'center';
} else {
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
} else if (labelPosition === 'leftBottom') {
// LeftBottom side
x1 = orient === 'horizontal' ? points[1][0] : points[3][0];
y1 = orient === 'horizontal' ? points[1][1] : points[2][1];
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
} else {
x2 = x1 - labelLineLen;
textX = x2 - 5;
textAlign = 'right';
}
} else {
// Right side or Bottom side
x1 = (points[1][0] + points[2][0]) / 2;
y1 = (points[1][1] + points[2][1]) / 2;
if (orient === 'horizontal') {
y2 = y1 + labelLineLen;
textY = y2 + 5;
textAlign = 'center';
} else {
x2 = x1 + labelLineLen;
textX = x2 + 5;
textAlign = 'left';
}
}
if (orient === 'horizontal') {
x2 = x1;
textX = x2;
} else {
y2 = y1;
textY = y2;
}
linePoints = [[x1, y1], [x2, y2]];
}
layout.label = {
linePoints: linePoints,
x: textX,
y: textY,
verticalAlign: 'middle',
textAlign: textAlign,
inside: isLabelInside
};
});
}
export default function funnelLayout(ecModel, api) {
ecModel.eachSeriesByType('funnel', function (seriesModel) {
var data = seriesModel.getData();
var valueDim = data.mapDimension('value');
var sort = seriesModel.get('sort');
var viewRect = getViewRect(seriesModel, api);
var orient = seriesModel.get('orient');
var viewWidth = viewRect.width;
var viewHeight = viewRect.height;
var indices = getSortedIndices(data, sort);
var x = viewRect.x;
var y = viewRect.y;
var sizeExtent = orient === 'horizontal' ? [parsePercent(seriesModel.get('minSize'), viewHeight), parsePercent(seriesModel.get('maxSize'), viewHeight)] : [parsePercent(seriesModel.get('minSize'), viewWidth), parsePercent(seriesModel.get('maxSize'), viewWidth)];
var dataExtent = data.getDataExtent(valueDim);
var min = seriesModel.get('min');
var max = seriesModel.get('max');
if (min == null) {
min = Math.min(dataExtent[0], 0);
}
if (max == null) {
max = dataExtent[1];
}
var funnelAlign = seriesModel.get('funnelAlign');
var gap = seriesModel.get('gap');
var viewSize = orient === 'horizontal' ? viewWidth : viewHeight;
var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();
var getLinePoints = function (idx, offset) {
// End point index is data.count() and we assign it 0
if (orient === 'horizontal') {
var val_1 = data.get(valueDim, idx) || 0;
var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);
var y0 = void 0;
switch (funnelAlign) {
case 'top':
y0 = y;
break;
case 'center':
y0 = y + (viewHeight - itemHeight) / 2;
break;
case 'bottom':
y0 = y + (viewHeight - itemHeight);
break;
}
return [[offset, y0], [offset, y0 + itemHeight]];
}
var val = data.get(valueDim, idx) || 0;
var itemWidth = linearMap(val, [min, max], sizeExtent, true);
var x0;
switch (funnelAlign) {
case 'left':
x0 = x;
break;
case 'center':
x0 = x + (viewWidth - itemWidth) / 2;
break;
case 'right':
x0 = x + viewWidth - itemWidth;
break;
}
return [[x0, offset], [x0 + itemWidth, offset]];
};
if (sort === 'ascending') {
// From bottom to top
itemSize = -itemSize;
gap = -gap;
if (orient === 'horizontal') {
x += viewWidth;
} else {
y += viewHeight;
}
indices = indices.reverse();
}
for (var i = 0; i < indices.length; i++) {
var idx = indices[i];
var nextIdx = indices[i + 1];
var itemModel = data.getItemModel(idx);
if (orient === 'horizontal') {
var width = itemModel.get(['itemStyle', 'width']);
if (width == null) {
width = itemSize;
} else {
width = parsePercent(width, viewWidth);
if (sort === 'ascending') {
width = -width;
}
}
var start = getLinePoints(idx, x);
var end = getLinePoints(nextIdx, x + width);
x += width + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
} else {
var height = itemModel.get(['itemStyle', 'height']);
if (height == null) {
height = itemSize;
} else {
height = parsePercent(height, viewHeight);
if (sort === 'ascending') {
height = -height;
}
}
var start = getLinePoints(idx, y);
var end = getLinePoints(nextIdx, y + height);
y += height + gap;
data.setItemLayout(idx, {
points: start.concat(end.slice().reverse())
});
}
}
labelLayout(data);
});
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/**
* AUTO-GENERATED FILE. DO NOT MODIFY.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 FunnelView from './FunnelView.js';
import FunnelSeriesModel from './FunnelSeries.js';
import funnelLayout from './funnelLayout.js';
import dataFilter from '../../processor/dataFilter.js';
export function install(registers) {
registers.registerChartView(FunnelView);
registers.registerSeriesModel(FunnelSeriesModel);
registers.registerLayout(funnelLayout);
registers.registerProcessor(dataFilter('funnel'));
}