Skip to content

Commit

Permalink
upd: 新增charts组件
Browse files Browse the repository at this point in the history
  • Loading branch information
mayinrain committed Jan 14, 2025
1 parent 766bdc7 commit d1ffc15
Show file tree
Hide file tree
Showing 13 changed files with 523 additions and 8 deletions.
6 changes: 2 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
],
// 每次保存的时候将代码按 eslint 格式进行修复
"editor.codeActionsOnSave": {
// For ESLint
"source.fixAll.eslint": true,
// 文件保存时开启 stylelint 自动修复程序
"source.fixAll.stylelint": true,
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
// json 文件按 prettier 规范格式化
"[json]": {
Expand Down
57 changes: 57 additions & 0 deletions docs/.vitepress/components/BCharts/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# BCharts
提供通用的图表组件,支持时间范围选择和数据刷新。

## 组件注册

```js
import { BCharts } from '@fesjs/traction-widget';
app.use(BCharts);
```

## 代码演示
### 基础用法
传入图表配置和数据获取方法,自动处理图表的渲染和更新。

--USE

--CODE

## 参数说明
### BCharts Props
| 属性 | 说明 | 类型 | 默认值 | 是否必须 |
| ----- | ---------------------- | --------------------------------------- | -------- | -------- |
| chartId | 图表DOM的id | string | - ||
| config | 图表配置项 | ChartConfig | - ||
| initialDays | 初始时间范围天数 | number | 7 ||

### ChartConfig 类型定义
```ts
interface BarStyle {
color: string;
borderColor: string;
}

interface ChartConfig {
// 图表标题
title: string;
// 数据项配置
series: {
field: string;
name: string;
itemStyle: BarStyle;
}[];
// 获取数据的方法,接收时间范围参数
fetchData: (startTime: number, endTime: number) => Promise<any[]>;
// x轴字段名
xAxisField: string;
// 自定义 tooltip 格式化函数
tooltipFormatter?: (params: any[]) => string;
}
```

## 注意事项
1. 组件会自动处理图表的初始化和销毁
2. 时间范围变化时会自动重新获取数据并更新图表
3. 支持自定义每个数据系列的样式
4. 确保提供唯一的 chartId 以避免 DOM 冲突
5. 可以通过 tooltipFormatter 自定义提示框的显示格式
93 changes: 93 additions & 0 deletions docs/.vitepress/components/BCharts/use.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<template>
<div class="chart-demo">
<BCharts
chart-id="demo-chart"
:config="chartConfig"
:initial-days="7"
/>
</div>
</template>

<script setup lang="ts">
import { BCharts } from '@fesjs/traction-widget';
const chartConfig = {
title: '告警统计分析',
series: [
{
field: 'critical',
name: '严重告警',
itemStyle: {
color: '#FEEEEE',
borderColor: '#FF4D4F'
}
},
{
field: 'major',
name: '主要告警',
itemStyle: {
color: '#EDF2FF',
borderColor: '#5384FF'
}
},
{
field: 'minor',
name: '次要告警',
itemStyle: {
color: '#FFF4EB',
borderColor: '#FF9900'
}
},
{
field: 'warning',
name: '警告',
itemStyle: {
color: '#FFF3DC',
borderColor: '#FAC017'
}
},
{
field: 'info',
name: '信息',
itemStyle: {
color: '#D1F4E9',
borderColor: '#00CB91'
}
}
],
xAxisField: 'date',
fetchData: async (startTime: number, endTime: number) => {
// 模拟异步数据获取
return new Promise((resolve) => {
setTimeout(() => {
// 生成模拟数据
const days = Math.floor((endTime - startTime) / (24 * 60 * 60 * 1000));
const data = [];
for (let i = 0; i <= days; i++) {
const date = new Date(startTime + i * 24 * 60 * 60 * 1000);
data.push({
date: date.toISOString().split('T')[0],
critical: Math.floor(Math.random() * 10),
major: Math.floor(Math.random() * 15),
minor: Math.floor(Math.random() * 20),
warning: Math.floor(Math.random() * 25),
info: Math.floor(Math.random() * 30)
});
}
resolve(data);
}, 1000);
});
}
};
</script>

<style scoped>
.chart-demo {
padding: 24px;
background: #fff;
border-radius: 4px;
min-height: 500px;
}
</style>
1 change: 1 addition & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export default defineConfig({
{ text: 'BSearch', link: '/components/BSearch' },
{ text: 'BDynamicForms', link: '/components/BDynamicForms'},
{ text: 'BMetricTip', link: '/components/BMetricTip'},
{ text: 'BCharts', link: '/components/BCharts'}
]
},
{
Expand Down
82 changes: 82 additions & 0 deletions packages/traction-widget/components/Charts/Charts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<div class="wd-content-body">
<h4 class="chart-title">{{ config.title }}</h4>
<div class="date-range">
<div class="mr16 my-date-picker">
<FDatePicker type="daterange" v-model="dateRange" @change="updateDateRange" />
</div>
<FButton key="btn-1" class="mr16" :class="{ 'my-btn': true, active: days === 7 }" @click="updateDays(7)">
最近7天
</FButton>
<FButton key="btn-2" :class="{ 'my-btn': true, active: days === 30 }" @click="updateDays(30)">
最近30天
</FButton>
</div>
<div :id="chartId" class="chart-container">
</div>
</div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';
import { FDatePicker, FButton } from '@fesjs/fes-design';
import { useChart, type ChartConfig } from './useChart';
import {
getYear, getMonth, getDate, subDays, differenceInDays,
} from 'date-fns';
interface Props {
chartId: string;
config: ChartConfig;
initialDays?: number;
}
const props = defineProps<Props>();
// 日期范围相关
const days = ref(props.initialDays || 7);
const initialEndDate = new Date().getTime();
const initialStartDate = subDays(new Date(initialEndDate), days.value - 1).getTime();
const endDate = ref(initialEndDate);
const startDate = ref(initialStartDate);
// 初始化时,开始日期在前,结束日期在后
const dateRange = ref([initialStartDate, initialEndDate]);
const updateDateRange = (range: number[]) => {
const [startStamp, endStamp] = range;
startDate.value = startStamp;
endDate.value = endStamp;
const now = Date.now();
if (startStamp && endStamp) {
const isToday = getYear(now) === getYear(endStamp)
&& getMonth(now) === getMonth(endStamp)
&& getDate(now) === getDate(endStamp);
const daysDiff = differenceInDays(endStamp, startStamp) + 1;
if (isToday && [7, 30].includes(daysDiff)) {
days.value = daysDiff;
} else {
days.value = 0;
}
}
};
const updateDays = (newDays: number) => {
const _endDate = new Date();
const _startDate = subDays(_endDate, newDays - 1);
dateRange.value = [_startDate.getTime(), _endDate.getTime()];
days.value = newDays;
startDate.value = _startDate.getTime();
endDate.value = _endDate.getTime();
};
// 图表相关
const { loading } = useChart(
props.chartId,
props.config,
startDate,
endDate
);
</script>
9 changes: 9 additions & 0 deletions packages/traction-widget/components/Charts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { withInstall } from '../_util/withInstall';
import Charts from './Charts.vue';

import type { SFCWithInstall } from '../_util/interface';

type ChartsType = SFCWithInstall<typeof Charts>;
export const BCharts = withInstall<ChartsType>(Charts as ChartsType);

export default BCharts;
41 changes: 41 additions & 0 deletions packages/traction-widget/components/Charts/style/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.wd-content-body {
width: 100%;
}

.chart-title {
margin-bottom: 16px;
}

.date-range {
display: flex;
align-items: center;
margin-bottom: 16px;
}

.chart-container {
position: relative;
width: 100%;
height: 400px;
}

.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.7);
}

.mr16 {
margin-right: 16px;
}

.my-btn.active {
background-color: #f5f8ff;
border-color: #5384ff;
color: #5384ff;
}
1 change: 1 addition & 0 deletions packages/traction-widget/components/Charts/style/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './index.less';
Loading

0 comments on commit d1ffc15

Please sign in to comment.