| | |
| | | <template> |
| | | <view class="echarts-container"> |
| | | <!-- #ifdef H5 || APP-PLUS --> |
| | | <view class="echarts-container" :style="containerStyle"> |
| | | <!-- #ifdef APP-PLUS || H5 --> |
| | | <view |
| | | :id="chartId" |
| | | class="echarts-chart" |
| | | :style="chartStyle" |
| | | :prop="chartData" |
| | | :change:prop="echarts.updateData" |
| | | :chartIdProp="chartId" |
| | | :change:chartIdProp="echarts.updateId" |
| | | :colorsProp="colors" |
| | | :change:colorsProp="echarts.updateColors" |
| | | ></view> |
| | | <!-- #endif --> |
| | | |
| | | <!-- #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO --> |
| | | <!-- #ifndef APP-PLUS || H5 --> |
| | | <view class="mp-not-supported"> |
| | | <text class="mp-text">图表组件暂不支持小程序平台</text> |
| | | </view> |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | chartId: '', |
| | | chartInstance: null |
| | | chartId: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | chartStyle() { |
| | | const w = typeof this.width === 'number' ? `${this.width}px` : this.width |
| | | const h = typeof this.height === 'number' ? `${this.height}px` : this.height |
| | | return { width: w, height: h } |
| | | }, |
| | | containerStyle() { |
| | | const w = typeof this.width === 'number' ? `${this.width}px` : this.width |
| | | const h = typeof this.height === 'number' ? `${this.height}px` : this.height |
| | | return { width: w, height: h } |
| | |
| | | created() { |
| | | // Generate unique chart ID |
| | | this.chartId = 'echarts-line-' + Math.random().toString(36).substr(2, 9) |
| | | }, |
| | | mounted() { |
| | | // Delay initialization to ensure DOM is ready |
| | | setTimeout(() => { |
| | | this.initChart() |
| | | }, 100) |
| | | }, |
| | | watch: { |
| | | chartData: { |
| | | handler(newData) { |
| | | if (this.chartInstance) { |
| | | this.updateChart(newData) |
| | | } |
| | | }, |
| | | deep: true |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <script module="echarts" lang="renderjs"> |
| | | let myChart |
| | | let chartId = '' |
| | | let currentColors = ['#1890FF', '#F04864'] |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | isLoaded: false, |
| | | pendingData: null |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.initEcharts() |
| | | }, |
| | | methods: { |
| | | initChart() { |
| | | // #ifdef H5 |
| | | this.initH5Chart() |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | this.initAppChart() |
| | | // #endif |
| | | |
| | | // #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO |
| | | this.initMpChart() |
| | | // #endif |
| | | }, |
| | | |
| | | initH5Chart() { |
| | | // For H5 platform, use the existing echarts from qiun-data-charts |
| | | this.$nextTick(() => { |
| | | try { |
| | | const echarts = window.echarts |
| | | if (echarts) { |
| | | this.createChart(echarts) |
| | | } else { |
| | | // Fallback: load from qiun-data-charts |
| | | import('../../uni_modules/qiun-data-charts/static/h5/echarts.min.js').then(module => { |
| | | const echarts = window.echarts || module.default |
| | | this.createChart(echarts) |
| | | }).catch(err => { |
| | | console.error('Failed to load ECharts for H5:', err) |
| | | }) |
| | | } |
| | | } catch (error) { |
| | | console.error('Error initializing H5 chart:', error) |
| | | initEcharts() { |
| | | if (typeof window.echarts === 'object') { |
| | | this.isLoaded = true |
| | | if (this.pendingData) { |
| | | this.renderChart(this.pendingData) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | initAppChart() { |
| | | // For App platform |
| | | this.$nextTick(() => { |
| | | try { |
| | | const echarts = window.echarts |
| | | if (echarts) { |
| | | this.createChart(echarts) |
| | | } else { |
| | | // Load from qiun-data-charts |
| | | import('../../uni_modules/qiun-data-charts/static/app-plus/echarts.min.js').then(module => { |
| | | const echarts = window.echarts || module.default |
| | | this.createChart(echarts) |
| | | }).catch(err => { |
| | | console.error('Failed to load ECharts for App:', err) |
| | | }) |
| | | } |
| | | } catch (error) { |
| | | console.error('Error initializing App chart:', error) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | initMpChart() { |
| | | // For mini-program platforms |
| | | this.$nextTick(() => { |
| | | try { |
| | | // Use the same approach as qiun-data-charts for mini-programs |
| | | const echarts = window.echarts |
| | | if (echarts) { |
| | | this.createChart(echarts) |
| | | } else { |
| | | console.warn('ECharts not available in mini-program environment') |
| | | } |
| | | } catch (error) { |
| | | console.error('Error initializing mini-program chart:', error) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | createChart(echarts) { |
| | | try { |
| | | const chartDom = document.getElementById(this.chartId) |
| | | if (!chartDom) { |
| | | console.error('Chart DOM element not found with ID:', this.chartId) |
| | | return |
| | | } |
| | | |
| | | this.chartInstance = echarts.init(chartDom, null, { |
| | | devicePixelRatio: window.devicePixelRatio |
| | | } else { |
| | | // Load ECharts dynamically |
| | | // Using the file from uni_modules which is available in the project |
| | | const script = document.createElement('script') |
| | | // Try to load from local static path if possible, or import |
| | | // Since we can't easily use script src for local files in App without correct path |
| | | // We use import() which is bundled by uni-app |
| | | import('../../uni_modules/qiun-data-charts/static/h5/echarts.min.js').then(module => { |
| | | window.echarts = module.default || module |
| | | this.isLoaded = true |
| | | if (this.pendingData) { |
| | | this.renderChart(this.pendingData) |
| | | } |
| | | }).catch(err => { |
| | | console.error('Failed to load ECharts:', err) |
| | | }) |
| | | this.updateChart(this.chartData) |
| | | |
| | | // Handle window resize for H5 |
| | | // #ifdef H5 |
| | | window.addEventListener('resize', this.handleResize) |
| | | // #endif |
| | | } catch (error) { |
| | | console.error('Error creating chart:', error) |
| | | } |
| | | |
| | | window.addEventListener('resize', this.resize) |
| | | }, |
| | | |
| | | updateId(newValue, oldValue, ownerInstance, instance) { |
| | | chartId = newValue |
| | | }, |
| | | |
| | | updateColors(newValue, oldValue, ownerInstance, instance) { |
| | | if (newValue && Array.isArray(newValue)) { |
| | | currentColors = newValue |
| | | } |
| | | }, |
| | | |
| | | updateChart(data) { |
| | | if (!this.chartInstance || !data || !data.categories || !data.series) { |
| | | return |
| | | updateData(newValue, oldValue, ownerInstance, instance) { |
| | | if (!newValue) return |
| | | |
| | | if (this.isLoaded) { |
| | | this.renderChart(newValue) |
| | | } else { |
| | | this.pendingData = newValue |
| | | } |
| | | }, |
| | | |
| | | renderChart(data) { |
| | | if (!window.echarts || !chartId) return |
| | | |
| | | const el = document.getElementById(chartId) |
| | | if (!el) return |
| | | |
| | | if (!myChart) { |
| | | myChart = window.echarts.init(el, null, { |
| | | devicePixelRatio: window.devicePixelRatio |
| | | }) |
| | | } |
| | | |
| | | const option = { |
| | | color: this.colors, |
| | | color: currentColors, |
| | | grid: { |
| | | top: '25%', |
| | | left: '5%', |
| | |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | data: data.categories, |
| | | data: data.categories || [], |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#CCCCCC' |
| | |
| | | show: false |
| | | } |
| | | }, |
| | | series: data.series.map((serie, index) => ({ |
| | | series: (data.series || []).map((serie, index) => ({ |
| | | name: serie.name, |
| | | type: 'line', |
| | | data: serie.data, |
| | | smooth: false, |
| | | lineStyle: { |
| | | width: 3, |
| | | color: this.colors[index % this.colors.length] |
| | | color: currentColors[index % currentColors.length] |
| | | }, |
| | | itemStyle: { |
| | | color: this.colors[index % this.colors.length] |
| | | color: currentColors[index % currentColors.length] |
| | | }, |
| | | symbol: 'circle', |
| | | symbolSize: 8, |
| | |
| | | } |
| | | } |
| | | |
| | | this.chartInstance.setOption(option, true) |
| | | myChart.setOption(option, true) |
| | | }, |
| | | |
| | | handleResize() { |
| | | if (this.chartInstance) { |
| | | this.chartInstance.resize() |
| | | } |
| | | }, |
| | | |
| | | // Method to manually resize chart |
| | | resize() { |
| | | if (this.chartInstance) { |
| | | this.chartInstance.resize() |
| | | if (myChart) { |
| | | myChart.resize() |
| | | } |
| | | } |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | if (this.chartInstance) { |
| | | this.chartInstance.dispose() |
| | | this.chartInstance = null |
| | | if (myChart) { |
| | | myChart.dispose() |
| | | myChart = null |
| | | } |
| | | |
| | | // #ifdef H5 |
| | | window.removeEventListener('resize', this.handleResize) |
| | | // #endif |
| | | window.removeEventListener('resize', this.resize) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .echarts-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |