594 lines
12 KiB
Markdown
594 lines
12 KiB
Markdown
|
|
# ChartPanel 组件
|
|||
|
|
|
|||
|
|
## 组件说明
|
|||
|
|
|
|||
|
|
图表面板组件,基于 ECharts 封装,用于展示数据可视化图表。支持折线图、柱状图、饼图、环形图等多种图表类型,适合在仪表盘、监控面板、数据分析页面中使用。
|
|||
|
|
|
|||
|
|
## 组件位置
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/components/ChartPanel/ChartPanel.jsx
|
|||
|
|
src/components/ChartPanel/ChartPanel.css
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 参数说明
|
|||
|
|
|
|||
|
|
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
|||
|
|
|--------|------|------|--------|------|
|
|||
|
|
| type | string | 否 | 'line' | 图表类型:line/bar/pie/ring |
|
|||
|
|
| title | string | 否 | - | 图表标题 |
|
|||
|
|
| data | ChartData | 是 | - | 图表数据 |
|
|||
|
|
| height | number | 否 | 200 | 图表高度(像素) |
|
|||
|
|
| option | object | 否 | {} | 自定义 ECharts 配置项 |
|
|||
|
|
| className | string | 否 | '' | 自定义类名 |
|
|||
|
|
|
|||
|
|
### ChartData 数据格式
|
|||
|
|
|
|||
|
|
#### 折线图 / 柱状图数据格式
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
{
|
|||
|
|
xAxis: string[] // X 轴数据(时间、类别等)
|
|||
|
|
series: Array<{
|
|||
|
|
name: string // 系列名称
|
|||
|
|
data: number[] // Y 轴数据
|
|||
|
|
color?: string // 自定义颜色(可选)
|
|||
|
|
}>
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 饼图 / 环形图数据格式
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
{
|
|||
|
|
data: Array<{
|
|||
|
|
name: string // 数据项名称
|
|||
|
|
value: number // 数据值
|
|||
|
|
color?: string // 自定义颜色(可选)
|
|||
|
|
}>
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 图表类型说明
|
|||
|
|
|
|||
|
|
| 类型 | 适用场景 | 数据维度 |
|
|||
|
|
|------|---------|---------|
|
|||
|
|
| line | 趋势展示、时序数据、性能监控 | 多系列、时间轴 |
|
|||
|
|
| bar | 对比分析、排名展示、统计数据 | 多系列、分类轴 |
|
|||
|
|
| pie | 占比分析、分布展示 | 单维度、百分比 |
|
|||
|
|
| ring | 占比分析(带中心文字) | 单维度、百分比 |
|
|||
|
|
|
|||
|
|
## 使用示例
|
|||
|
|
|
|||
|
|
### 折线图
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
import ChartPanel from '../components/ChartPanel/ChartPanel'
|
|||
|
|
|
|||
|
|
const lineData = {
|
|||
|
|
xAxis: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00'],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: 'CPU 使用率',
|
|||
|
|
data: [30, 45, 65, 50, 70, 55],
|
|||
|
|
color: '#1677ff',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: '内存使用率',
|
|||
|
|
data: [20, 35, 55, 45, 60, 50],
|
|||
|
|
color: '#52c41a',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
<ChartPanel
|
|||
|
|
type="line"
|
|||
|
|
title="性能监控"
|
|||
|
|
data={lineData}
|
|||
|
|
height={300}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 柱状图
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
const barData = {
|
|||
|
|
xAxis: ['周一', '周二', '周三', '周四', '周五'],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '新增用户',
|
|||
|
|
data: [120, 200, 150, 180, 220],
|
|||
|
|
color: '#1677ff',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: '活跃用户',
|
|||
|
|
data: [80, 150, 120, 140, 180],
|
|||
|
|
color: '#52c41a',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
<ChartPanel
|
|||
|
|
type="bar"
|
|||
|
|
title="用户统计"
|
|||
|
|
data={barData}
|
|||
|
|
height={250}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 饼图
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
const pieData = {
|
|||
|
|
data: [
|
|||
|
|
{ name: '运行中', value: 45, color: '#52c41a' },
|
|||
|
|
{ name: '已停止', value: 20, color: '#8c8c8c' },
|
|||
|
|
{ name: '错误', value: 5, color: '#ff4d4f' },
|
|||
|
|
{ name: '待部署', value: 30, color: '#faad14' },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
<ChartPanel
|
|||
|
|
type="pie"
|
|||
|
|
title="状态分布"
|
|||
|
|
data={pieData}
|
|||
|
|
height={250}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 环形图
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
const ringData = {
|
|||
|
|
data: [
|
|||
|
|
{ name: '在线', value: 85 },
|
|||
|
|
{ name: '离线', value: 15 },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
<ChartPanel
|
|||
|
|
type="ring"
|
|||
|
|
title="在线率"
|
|||
|
|
data={ringData}
|
|||
|
|
height={200}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 自定义 ECharts 配置
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
<ChartPanel
|
|||
|
|
type="line"
|
|||
|
|
data={lineData}
|
|||
|
|
option={{
|
|||
|
|
grid: {
|
|||
|
|
left: '5%',
|
|||
|
|
right: '5%',
|
|||
|
|
bottom: '10%',
|
|||
|
|
},
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
axisLabel: {
|
|||
|
|
rotate: 45,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 配合 SideInfoPanel 使用
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
import SideInfoPanel from '../components/SideInfoPanel/SideInfoPanel'
|
|||
|
|
import ChartPanel from '../components/ChartPanel/ChartPanel'
|
|||
|
|
|
|||
|
|
<SideInfoPanel
|
|||
|
|
sections={[
|
|||
|
|
{
|
|||
|
|
key: 'performance',
|
|||
|
|
title: '性能监控',
|
|||
|
|
icon: <LineChartOutlined />,
|
|||
|
|
content: (
|
|||
|
|
<>
|
|||
|
|
<ChartPanel
|
|||
|
|
type="line"
|
|||
|
|
title="CPU 使用率"
|
|||
|
|
data={cpuData}
|
|||
|
|
height={200}
|
|||
|
|
/>
|
|||
|
|
<ChartPanel
|
|||
|
|
type="line"
|
|||
|
|
title="内存使用率"
|
|||
|
|
data={memoryData}
|
|||
|
|
height={200}
|
|||
|
|
/>
|
|||
|
|
</>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'distribution',
|
|||
|
|
title: '状态分布',
|
|||
|
|
icon: <PieChartOutlined />,
|
|||
|
|
content: (
|
|||
|
|
<>
|
|||
|
|
<ChartPanel
|
|||
|
|
type="ring"
|
|||
|
|
title="在线状态"
|
|||
|
|
data={statusData}
|
|||
|
|
height={200}
|
|||
|
|
/>
|
|||
|
|
<ChartPanel
|
|||
|
|
type="bar"
|
|||
|
|
title="区域分布"
|
|||
|
|
data={regionData}
|
|||
|
|
height={200}
|
|||
|
|
/>
|
|||
|
|
</>
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
]}
|
|||
|
|
/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## DOM 结构层级
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<div class="chart-panel">
|
|||
|
|
|
|||
|
|
<!-- 图表标题(可选) -->
|
|||
|
|
{title && (
|
|||
|
|
<div class="chart-panel-title">
|
|||
|
|
{title}
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<!-- ECharts 容器 -->
|
|||
|
|
<div
|
|||
|
|
ref={chartRef}
|
|||
|
|
class="chart-panel-chart"
|
|||
|
|
style="height: 200px"
|
|||
|
|
>
|
|||
|
|
<!-- ECharts 实例挂载点 -->
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ECharts 配置说明
|
|||
|
|
|
|||
|
|
### 折线图默认配置
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
bottom: 0,
|
|||
|
|
left: 'center',
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '15%',
|
|||
|
|
containLabel: true,
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: data.xAxis,
|
|||
|
|
boundaryGap: false,
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value',
|
|||
|
|
},
|
|||
|
|
series: data.series.map(s => ({
|
|||
|
|
name: s.name,
|
|||
|
|
type: 'line',
|
|||
|
|
data: s.data,
|
|||
|
|
smooth: true,
|
|||
|
|
itemStyle: { color: s.color },
|
|||
|
|
})),
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 柱状图默认配置
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: { type: 'shadow' },
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
bottom: 0,
|
|||
|
|
left: 'center',
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '15%',
|
|||
|
|
containLabel: true,
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: data.xAxis,
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value',
|
|||
|
|
},
|
|||
|
|
series: data.series.map(s => ({
|
|||
|
|
name: s.name,
|
|||
|
|
type: 'bar',
|
|||
|
|
data: s.data,
|
|||
|
|
itemStyle: { color: s.color },
|
|||
|
|
})),
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 饼图默认配置
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{b}: {c} ({d}%)',
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
bottom: 0,
|
|||
|
|
left: 'center',
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: '70%',
|
|||
|
|
center: ['50%', '45%'],
|
|||
|
|
data: data.data.map(item => ({
|
|||
|
|
name: item.name,
|
|||
|
|
value: item.value,
|
|||
|
|
itemStyle: { color: item.color },
|
|||
|
|
})),
|
|||
|
|
emphasis: {
|
|||
|
|
itemStyle: {
|
|||
|
|
shadowBlur: 10,
|
|||
|
|
shadowOffsetX: 0,
|
|||
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 环形图默认配置
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
{
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{b}: {c} ({d}%)',
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
bottom: 0,
|
|||
|
|
left: 'center',
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['50%', '70%'], // 环形图特征:内外半径
|
|||
|
|
center: ['50%', '45%'],
|
|||
|
|
data: data.data.map(item => ({
|
|||
|
|
name: item.name,
|
|||
|
|
value: item.value,
|
|||
|
|
itemStyle: { color: item.color },
|
|||
|
|
})),
|
|||
|
|
label: {
|
|||
|
|
show: false, // 环形图默认不显示标签
|
|||
|
|
},
|
|||
|
|
emphasis: {
|
|||
|
|
itemStyle: {
|
|||
|
|
shadowBlur: 10,
|
|||
|
|
shadowOffsetX: 0,
|
|||
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 组件生命周期
|
|||
|
|
|
|||
|
|
### 初始化流程
|
|||
|
|
|
|||
|
|
1. 组件挂载时,通过 `useRef` 获取 DOM 容器
|
|||
|
|
2. 使用 `echarts.init()` 初始化 ECharts 实例
|
|||
|
|
3. 根据 `type` 和 `data` 生成配置项
|
|||
|
|
4. 调用 `setOption()` 渲染图表
|
|||
|
|
|
|||
|
|
### 更新流程
|
|||
|
|
|
|||
|
|
1. 当 `type`、`data`、`option` 变化时触发 `useEffect`
|
|||
|
|
2. 复用已有的 ECharts 实例
|
|||
|
|
3. 重新生成配置项并调用 `setOption()` 更新图表
|
|||
|
|
|
|||
|
|
### 响应式处理
|
|||
|
|
|
|||
|
|
1. 监听 `window.resize` 事件
|
|||
|
|
2. 调用 `chartInstance.resize()` 自动调整图表尺寸
|
|||
|
|
3. 组件卸载时移除事件监听并销毁 ECharts 实例
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
useEffect(() => {
|
|||
|
|
const handleResize = () => {
|
|||
|
|
if (chartInstance.current) {
|
|||
|
|
chartInstance.current.resize()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
window.addEventListener('resize', handleResize)
|
|||
|
|
|
|||
|
|
return () => {
|
|||
|
|
window.removeEventListener('resize', handleResize)
|
|||
|
|
if (chartInstance.current) {
|
|||
|
|
chartInstance.current.dispose()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}, [])
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 样式定制
|
|||
|
|
|
|||
|
|
组件提供以下 CSS 类名供自定义样式:
|
|||
|
|
|
|||
|
|
- `.chart-panel` - 面板容器
|
|||
|
|
- `.chart-panel-title` - 图表标题
|
|||
|
|
- `.chart-panel-chart` - ECharts 容器
|
|||
|
|
|
|||
|
|
### 自定义样式示例
|
|||
|
|
|
|||
|
|
```css
|
|||
|
|
/* 修改标题样式 */
|
|||
|
|
.chart-panel-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #1677ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修改面板背景 */
|
|||
|
|
.chart-panel {
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 修改图表容器 */
|
|||
|
|
.chart-panel-chart {
|
|||
|
|
background: #ffffff;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 使用场景
|
|||
|
|
|
|||
|
|
1. **性能监控面板** - 展示 CPU、内存、网络等实时性能数据
|
|||
|
|
2. **数据分析仪表盘** - 展示业务指标、用户统计、销售数据
|
|||
|
|
3. **状态分布展示** - 展示系统状态、设备状态、任务状态的占比
|
|||
|
|
4. **趋势对比分析** - 展示多个指标的时序变化和对比
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **数据格式**:
|
|||
|
|
- 确保数据格式与图表类型匹配
|
|||
|
|
- 折线图/柱状图使用 xAxis + series 格式
|
|||
|
|
- 饼图/环形图使用 data 数组格式
|
|||
|
|
- 数据更新时会自动重新渲染
|
|||
|
|
|
|||
|
|
2. **图表高度**:
|
|||
|
|
- 默认高度 200px,建议根据内容调整
|
|||
|
|
- 折线图/柱状图:200-400px
|
|||
|
|
- 饼图/环形图:200-300px
|
|||
|
|
- 避免高度过小导致显示不清
|
|||
|
|
|
|||
|
|
3. **颜色使用**:
|
|||
|
|
- 可在数据中指定 color 属性自定义颜色
|
|||
|
|
- 不指定时使用 ECharts 默认配色
|
|||
|
|
- 建议使用语义化颜色(绿色=正常,红色=错误)
|
|||
|
|
- 同一页面保持配色风格一致
|
|||
|
|
|
|||
|
|
4. **性能优化**:
|
|||
|
|
- 大数据量时考虑数据采样
|
|||
|
|
- 避免频繁更新图表(建议间隔 > 1s)
|
|||
|
|
- 组件卸载时会自动销毁 ECharts 实例
|
|||
|
|
- 窗口 resize 时会自动调整图表尺寸
|
|||
|
|
|
|||
|
|
5. **响应式**:
|
|||
|
|
- 图表会自动适应容器宽度
|
|||
|
|
- 窗口大小变化时自动 resize
|
|||
|
|
- 小屏幕时建议减小图表高度
|
|||
|
|
|
|||
|
|
6. **自定义配置**:
|
|||
|
|
- 使用 option 参数传入自定义 ECharts 配置
|
|||
|
|
- 自定义配置会与默认配置深度合并
|
|||
|
|
- 可完全覆盖默认配置实现高度定制
|
|||
|
|
|
|||
|
|
7. **标题使用**:
|
|||
|
|
- title 为可选参数
|
|||
|
|
- 在 SideInfoPanel 中使用时,section 已有标题,可省略 ChartPanel 标题
|
|||
|
|
- 独立使用时建议添加标题增强可读性
|
|||
|
|
|
|||
|
|
## 配合使用的组件
|
|||
|
|
|
|||
|
|
- **SideInfoPanel** - 侧边信息面板(推荐)
|
|||
|
|
- **StatCard** - 统计卡片(配合使用)
|
|||
|
|
- **SplitLayout** - 分栏布局
|
|||
|
|
- **PageTitleBar** - 页面标题栏
|
|||
|
|
|
|||
|
|
## ECharts 依赖
|
|||
|
|
|
|||
|
|
本组件依赖 ECharts 库,确保已安装:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install echarts
|
|||
|
|
# 或
|
|||
|
|
yarn add echarts
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
导入方式:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
import * as echarts from 'echarts'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 示例数据结构参考
|
|||
|
|
|
|||
|
|
### 性能监控数据
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const performanceData = {
|
|||
|
|
xAxis: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00'],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: 'CPU',
|
|||
|
|
data: [30, 45, 65, 50, 70, 55],
|
|||
|
|
color: '#1677ff',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: '内存',
|
|||
|
|
data: [20, 35, 55, 45, 60, 50],
|
|||
|
|
color: '#52c41a',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 状态分布数据
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const statusData = {
|
|||
|
|
data: [
|
|||
|
|
{ name: '运行中', value: 45, color: '#52c41a' },
|
|||
|
|
{ name: '已停止', value: 20, color: '#8c8c8c' },
|
|||
|
|
{ name: '错误', value: 5, color: '#ff4d4f' },
|
|||
|
|
{ name: '待部署', value: 30, color: '#faad14' },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 区域分布数据
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const regionData = {
|
|||
|
|
xAxis: ['华东', '华南', '华北', '西南', '西北'],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '服务器数量',
|
|||
|
|
data: [120, 80, 95, 65, 45],
|
|||
|
|
color: '#1677ff',
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|