数据可视化仪表板需要处理大量数据、图表配置、用户交互等复杂逻辑。easy-model 的模型驱动架构能有效组织这些功能,提升开发效率。本文通过实际用例展示 easy-model 在仪表板开发中的优势。
图表配置模型
图表是仪表板的核心。我们创建 ChartModel 封装图表逻辑:
import { useModel } from "@e7w/easy-model"
class ChartModel {
config = {
type: "bar" as "bar" | "line" | "pie",
data: [] as Array<{ label: string; value: number }>,
title: "",
showLegend: true
};
constructor(initialConfig: typeof this.config) {
this.config = initialConfig;
}
updateData(newData: typeof this.config.data) {
this.config.data = newData;
}
toggleLegend() {
this.config.showLegend = !this.config.showLegend;
}
getMaxValue() {
return Math.max(...this.config.data.map(d => d.value));
}
}
function ChartComponent({ chartId }: { chartId: string }) {
const params = useMemo(() => [{
type: "bar",
data: [
{ label: "Jan", value: 100 },
{ label: "Feb", value: 150 },
{ label: "Mar", value: 200 }
],
title: "月销售额",
showLegend: true
}])
const chartModel = useModel(ChartModel, params);
return (
<div>
<h3>{chartModel.config.title}</h3>
<p>最大值: {chartModel.getMaxValue()}</p>
<button onClick={() => chartModel.toggleLegend()}>
{chartModel.config.showLegend ? "隐藏图例" : "显示图例"}
</button>
{/* 渲染图表逻辑 */}
</div>
);
}
模型封装图表业务逻辑,如数据更新、配置切换等。
仪表板布局管理
仪表板布局需要跨组件共享:
import { useModel } from "@e7w/easy-model"
class DashboardModel {
widgets: Array<{ id: string; type: string; position: { x: number; y: number } }> = [];
dashboardId: string;
constructor(dashboardId: string) {
this.dashboardId = dashboardId;
}
addWidget(widget: typeof this.widgets[0]) {
this.widgets.push(widget);
}
moveWidget(id: string, newPosition: { x: number; y: number }) {
const widget = this.widgets.find(w => w.id === id);
if (widget) widget.position = newPosition;
}
getWidgetCount() {
return this.widgets.length;
}
}
function Dashboard({ dashboardId }: { dashboardId: string }) {
const dashboard = useModel(DashboardModel,[dashboardId]);
return (
<div>
<h2>仪表板 ({dashboard.getWidgetCount()} 个组件)</h2>
{dashboard.widgets.map(widget => (
<div key={widget.id} style={{
position: 'absolute',
left: widget.position.x,
top: widget.position.y
}}>
{/* 渲染组件 */}
</div>
))}
</div>
);
}
useModel 支持布局状态共享。
数据异步加载
仪表板数据通常来自API:
import { loader, useLoader, useModel } from "@e7w/easy-model"
class DataSourceModel {
data: any[] = [];
sourceId: string;
constructor(sourceId: string) {
this.sourceId = sourceId;
}
@loader.load(true)
async loadData() {
// 模拟API调用
const response = await fetch(`/api/data/${this.sourceId}`);
this.data = await response.json();
}
}
function DataWidget({ sourceId }: { sourceId: string }) {
const dataModel = useModel(DataSourceModel, [sourceId]);
const { isLoading } = useLoader();
return (
<div>
{isLoading(dataModel.loadData) ? "加载中..." : (
<ul>
{dataModel.data.map((item, i) => <li key={i}>{item.name}</li>)}
</ul>
)}
<button onClick={dataModel.loadData} disabled={isLoading(dataModel.loadData)}>
刷新数据
</button>
</div>
);
}
异步数据加载,状态自动管理。
测试保证质量
模型类易于测试:
describe("ChartModel", () => {
it("should update data", () => {
const model = new ChartModel({
type: "bar",
data: [],
title: "Test",
showLegend: true,
});
const newData = [{ label: "A", value: 10 }];
model.updateData(newData);
expect(model.config.data).toEqual(newData);
});
it("should calculate max value", () => {
const model = new ChartModel({
type: "bar",
data: [
{ label: "A", value: 10 },
{ label: "B", value: 20 },
],
title: "Test",
showLegend: true,
});
expect(model.getMaxValue()).toBe(20);
});
});
测试确保图表逻辑正确。
总结
easy-model 在数据可视化仪表板中表现出色:模型封装图表逻辑、状态共享布局、异步数据处理。结合测试,提升开发质量。试用 easy-model,让仪表板开发更简单!
项目地址:GitHub