---
name: Chart and Data Visualization
description: Hướng dẫn tạo charts và visualizations sử dụng ECharts trong dự án LADA Express
---

# Chart and Data Visualization Skill

## Mục đích

Skill này hướng dẫn cách tạo và sử dụng charts với vue-echarts để visualize dữ liệu trong dự án LADA Express.

## Công nghệ sử dụng

- **vue-echarts 7.x** - Vue wrapper cho Apache ECharts
- **echarts/core** - ECharts core
- **echarts/charts** - Chart types (Line, Bar, Pie, etc.)
- **echarts/components** - Components (Tooltip, Legend, Grid, etc.)

## 1. Setup ECharts

### Import và đăng ký components

```javascript
import VChart from 'vue-echarts';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { LineChart, BarChart, PieChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
} from 'echarts/components';

// Đăng ký components
use([
  CanvasRenderer,
  LineChart,
  BarChart,
  PieChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
]);
```

## 2. Line Chart Pattern

### Basic Line Chart

```vue
<template>
  <div class="card">
    <div class="card-header">
      <h5 class="card-title mb-0">
        <i class="fas fa-chart-line me-2"></i>
        Biểu đồ doanh thu theo ngày
      </h5>
    </div>
    <div class="card-body">
      <v-chart 
        ref="lineChart" 
        :option="lineChartOption" 
        style="height: 400px;"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import VChart from 'vue-echarts';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { LineChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
} from 'echarts/components';

use([
  CanvasRenderer,
  LineChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
]);

const lineChart = ref();
const lineChartOption = ref({});

function renderLineChart(data) {
  const labels = data.map(item => item.Date);
  const values = data.map(item => item.Revenue);
  
  lineChartOption.value = {
    title: {
      text: 'Doanh thu theo ngày',
      left: 'center'
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross'
      },
      formatter: function (params) {
        let result = params[0].name + '<br/>';
        params.forEach(param => {
          result += param.marker + param.seriesName + ': ' +
            new Intl.NumberFormat('vi-VN', {
              style: 'currency',
              currency: 'VND'
            }).format(param.value) + '<br/>';
        });
        return result;
      }
    },
    legend: {
      data: ['Doanh thu'],
      top: '10%'
    },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      top: '20%',
      containLabel: true
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: labels
    },
    yAxis: {
      type: 'value',
      min: 0,
      axisLabel: {
        formatter: function (value) {
          return new Intl.NumberFormat('vi-VN').format(value);
        }
      }
    },
    series: [
      {
        name: 'Doanh thu',
        type: 'line',
        data: values,
        smooth: true,
        lineStyle: {
          color: '#28a745'
        },
        itemStyle: {
          color: '#28a745'
        },
        areaStyle: {
          color: 'rgba(40, 167, 69, 0.1)'
        }
      }
    ]
  };
}

onMounted(() => {
  // Load data và render chart
  const sampleData = [
    { Date: '01/01', Revenue: 1000000 },
    { Date: '02/01', Revenue: 1500000 },
    { Date: '03/01', Revenue: 1200000 }
  ];
  
  renderLineChart(sampleData);
});
</script>
```

### Multi-line Chart

```javascript
lineChartOption.value = {
  title: {
    text: 'So sánh doanh thu',
    left: 'center'
  },
  tooltip: {
    trigger: 'axis'
  },
  legend: {
    data: ['Doanh thu', 'Thu hộ', 'Chi phí'],
    top: '10%'
  },
  xAxis: {
    type: 'category',
    data: labels
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      name: 'Doanh thu',
      type: 'line',
      data: revenueData,
      smooth: true,
      itemStyle: { color: '#28a745' }
    },
    {
      name: 'Thu hộ',
      type: 'line',
      data: collectData,
      smooth: true,
      itemStyle: { color: '#17a2b8' }
    },
    {
      name: 'Chi phí',
      type: 'line',
      data: expenseData,
      smooth: true,
      itemStyle: { color: '#dc3545' }
    }
  ]
};
```

## 3. Bar Chart Pattern

### Basic Bar Chart

```javascript
const barChartOption = ref({
  title: {
    text: 'Doanh thu theo tháng',
    left: 'center'
  },
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'shadow'
    }
  },
  grid: {
    left: '3%',
    right: '4%',
    bottom: '3%',
    containLabel: true
  },
  xAxis: {
    type: 'category',
    data: ['T1', 'T2', 'T3', 'T4', 'T5', 'T6']
  },
  yAxis: {
    type: 'value',
    axisLabel: {
      formatter: function (value) {
        return new Intl.NumberFormat('vi-VN').format(value);
      }
    }
  },
  series: [
    {
      name: 'Doanh thu',
      type: 'bar',
      data: [120000, 200000, 150000, 180000, 220000, 250000],
      itemStyle: {
        color: '#007bff'
      }
    }
  ]
});
```

### Stacked Bar Chart

```javascript
const stackedBarOption = ref({
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'shadow'
    }
  },
  legend: {
    data: ['Đã giao', 'Đang giao', 'Đã hủy']
  },
  xAxis: {
    type: 'category',
    data: ['T1', 'T2', 'T3', 'T4', 'T5', 'T6']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      name: 'Đã giao',
      type: 'bar',
      stack: 'total',
      data: [120, 132, 101, 134, 90, 230],
      itemStyle: { color: '#28a745' }
    },
    {
      name: 'Đang giao',
      type: 'bar',
      stack: 'total',
      data: [220, 182, 191, 234, 290, 330],
      itemStyle: { color: '#ffc107' }
    },
    {
      name: 'Đã hủy',
      type: 'bar',
      stack: 'total',
      data: [150, 232, 201, 154, 190, 330],
      itemStyle: { color: '#dc3545' }
    }
  ]
});
```

## 4. Pie Chart Pattern

### Basic Pie Chart

```javascript
const pieChartOption = ref({
  title: {
    text: 'Phân bổ đơn hàng theo trạng thái',
    left: 'center'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b}: {c} ({d}%)'
  },
  legend: {
    orient: 'vertical',
    left: 'left',
    top: 'middle'
  },
  series: [
    {
      name: 'Trạng thái',
      type: 'pie',
      radius: '50%',
      data: [
        { value: 1048, name: 'Đã giao', itemStyle: { color: '#28a745' } },
        { value: 735, name: 'Đang giao', itemStyle: { color: '#ffc107' } },
        { value: 580, name: 'Chờ lấy', itemStyle: { color: '#17a2b8' } },
        { value: 484, name: 'Đã hủy', itemStyle: { color: '#dc3545' } }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ]
});
```

### Donut Chart

```javascript
const donutChartOption = ref({
  tooltip: {
    trigger: 'item'
  },
  legend: {
    top: '5%',
    left: 'center'
  },
  series: [
    {
      name: 'Trạng thái',
      type: 'pie',
      radius: ['40%', '70%'], // Inner and outer radius for donut
      avoidLabelOverlap: false,
      label: {
        show: false,
        position: 'center'
      },
      emphasis: {
        label: {
          show: true,
          fontSize: 20,
          fontWeight: 'bold'
        }
      },
      labelLine: {
        show: false
      },
      data: [
        { value: 1048, name: 'Đã giao' },
        { value: 735, name: 'Đang giao' },
        { value: 580, name: 'Chờ lấy' },
        { value: 484, name: 'Đã hủy' }
      ]
    }
  ]
});
```

## 5. Responsive Charts

### Auto-resize on window resize

```vue
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';

const chartRef = ref();

function handleResize() {
  if (chartRef.value) {
    chartRef.value.resize();
  }
}

onMounted(() => {
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  window.removeEventListener('resize', handleResize);
});
</script>
```

## 6. Chart Utilities

### Format Currency

```javascript
function formatCurrency(value) {
  return new Intl.NumberFormat('vi-VN', {
    style: 'currency',
    currency: 'VND'
  }).format(value);
}

// Usage in tooltip
tooltip: {
  formatter: function (params) {
    return params.name + '<br/>' +
           params.marker + params.seriesName + ': ' +
           formatCurrency(params.value);
  }
}
```

### Format Number

```javascript
function formatNumber(value) {
  return new Intl.NumberFormat('vi-VN').format(value);
}

// Usage in axis label
yAxis: {
  axisLabel: {
    formatter: formatNumber
  }
}
```

## 7. Loading State

```vue
<template>
  <div class="card">
    <div class="card-body">
      <div v-if="loading" class="text-center">
        <span class="spinner-border spinner-border-sm"></span>
        Đang tải dữ liệu...
      </div>
      
      <v-chart 
        v-else
        :option="chartOption" 
        style="height: 400px;"
      />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const loading = ref(true);
const chartOption = ref({});

async function loadChartData() {
  loading.value = true;
  try {
    // Load data
    const data = await fetchData();
    renderChart(data);
  } catch (error) {
    console.error('Error loading chart data:', error);
  } finally {
    loading.value = false;
  }
}
</script>
```

## 8. Common Chart Configurations

### Grid Configuration

```javascript
grid: {
  left: '3%',
  right: '4%',
  bottom: '3%',
  top: '20%',
  containLabel: true
}
```

### Tooltip Configuration

```javascript
tooltip: {
  trigger: 'axis',
  axisPointer: {
    type: 'cross',
    label: {
      backgroundColor: '#6a7985'
    }
  }
}
```

### Legend Configuration

```javascript
legend: {
  data: ['Series 1', 'Series 2'],
  top: '10%',
  left: 'center'
}
```

## Best Practices

### 1. Performance

- Chỉ import components cần thiết
- Sử dụng `CanvasRenderer` cho charts lớn
- Limit số lượng data points
- Debounce resize events

### 2. Responsive Design

- Set height với style hoặc CSS
- Handle window resize
- Use percentage-based dimensions

### 3. Data Formatting

- Format currency và numbers
- Use Vietnamese locale
- Consistent formatting across charts

### 4. Colors

- Sử dụng color palette nhất quán
- Bootstrap colors: primary, success, danger, warning, info
- Accessibility: sufficient contrast

### 5. Tooltips

- Provide meaningful information
- Format values properly
- Include units (VND, %, etc.)

## Checklist khi tạo Chart mới

- [ ] Import và register ECharts components
- [ ] Create chart ref
- [ ] Define chart option object
- [ ] Implement data loading
- [ ] Format data cho chart
- [ ] Add tooltip với formatting
- [ ] Add legend nếu cần
- [ ] Handle loading state
- [ ] Handle empty data
- [ ] Test responsive behavior
- [ ] Add resize handler
