|
@@ -1,11 +1,43 @@
|
|
|
<template>
|
|
<template>
|
|
|
<widget-card :bodyStyle="bodyStyle" ref="container">
|
|
<widget-card :bodyStyle="bodyStyle" ref="container">
|
|
|
- <canvas ref="chart" class="chart"></canvas>
|
|
|
|
|
|
|
+ <template #header>
|
|
|
|
|
+ <div class="header">
|
|
|
|
|
+ <span>销售业绩</span>
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ style="width: 120px;"
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ v-model="value"
|
|
|
|
|
+ @change="changeSelect"
|
|
|
|
|
+ placeholder="请选择"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
|
|
|
|
|
+ </el-option>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <div class="box-content" ref="box">
|
|
|
|
|
+ <div class="box">
|
|
|
|
|
+ <div class="text1">官方销售额/二手市场</div>
|
|
|
|
|
+ <div class="text2">
|
|
|
|
|
+ <span v-html="getNum(official, true)"></span>/ <span v-html="getNum(transfer, true)"></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="box">
|
|
|
|
|
+ <div class="text1">官方订单/二手市场(单)</div>
|
|
|
|
|
+ <div class="text2">{{ officialNum }}/{{ transferNum }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-box">
|
|
|
|
|
+ <canvas ref="chart" class="chart"></canvas>
|
|
|
|
|
+ </div>
|
|
|
</widget-card>
|
|
</widget-card>
|
|
|
</template>
|
|
</template>
|
|
|
<script>
|
|
<script>
|
|
|
import WidgetCard from './WidgetCard';
|
|
import WidgetCard from './WidgetCard';
|
|
|
import VueCharts from 'vue-chartjs';
|
|
import VueCharts from 'vue-chartjs';
|
|
|
|
|
+import { format } from 'date-fns';
|
|
|
|
|
+import acc from '../mixins/acc';
|
|
|
|
|
+import addDays from 'date-fns/esm/addDays';
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
data() {
|
|
data() {
|
|
@@ -13,30 +45,187 @@ export default {
|
|
|
myChart: null,
|
|
myChart: null,
|
|
|
bodyStyle: {
|
|
bodyStyle: {
|
|
|
display: 'flex',
|
|
display: 'flex',
|
|
|
- alignItems: 'center'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ alignItems: 'center',
|
|
|
|
|
+ justifyContent: ' space-around'
|
|
|
|
|
+ },
|
|
|
|
|
+ official: 0,
|
|
|
|
|
+ transfer: 0,
|
|
|
|
|
+ officialNum: 0,
|
|
|
|
|
+ transferNum: 0,
|
|
|
|
|
+ value: '',
|
|
|
|
|
+ options: [],
|
|
|
|
|
+ numInfo: {},
|
|
|
|
|
+ priceInfo: {}
|
|
|
};
|
|
};
|
|
|
},
|
|
},
|
|
|
|
|
+ mixins: [acc],
|
|
|
|
|
+ computed: {
|
|
|
|
|
+ total() {
|
|
|
|
|
+ return this.accAdd(this.official, this.transfer);
|
|
|
|
|
+ },
|
|
|
|
|
+ totalNum() {
|
|
|
|
|
+ return this.accAdd(this.officialNum, this.transferNum);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
mounted() {
|
|
mounted() {
|
|
|
- this.$refs.chart.width = this.$refs.container.$el.offsetWidth - 20;
|
|
|
|
|
- this.$refs.chart.height = this.$refs.container.$el.offsetHeight - 20;
|
|
|
|
|
- this.myChart = new Chart(this.$refs.chart.getContext('2d'), {
|
|
|
|
|
- type: 'pie',
|
|
|
|
|
- data: {
|
|
|
|
|
- labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'],
|
|
|
|
|
- datasets: [
|
|
|
|
|
- {
|
|
|
|
|
- backgroundColor: ['#41B883', '#E46651', '#00D8FF', '#DD1B16'],
|
|
|
|
|
- data: [40, 20, 80, 10]
|
|
|
|
|
|
|
+ let options = [];
|
|
|
|
|
+ this.value = format(new Date(), 'yyyy-MM-dd');
|
|
|
|
|
+ for (let i = 0; i < 7; i++) {
|
|
|
|
|
+ options.push({
|
|
|
|
|
+ label: i < 2 ? (i === 0 ? '今日' : '昨日') : format(addDays(new Date(), i), 'MM-dd'),
|
|
|
|
|
+ value: format(addDays(new Date(), 0 - i), 'yyyy-MM-dd')
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ this.options = options;
|
|
|
|
|
+ this.$http
|
|
|
|
|
+ .get('/statistic/orderNumTrend', {
|
|
|
|
|
+ day: 7
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(res => {
|
|
|
|
|
+ this.numInfo = res;
|
|
|
|
|
+
|
|
|
|
|
+ this.$http
|
|
|
|
|
+ .get('/statistic/orderPriceTrend', {
|
|
|
|
|
+ day: 7
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(res => {
|
|
|
|
|
+ this.priceInfo = res;
|
|
|
|
|
+ this.$refs.chart.width = this.$refs.container.$el.offsetWidth - this.$refs.box.offsetWidth - 40;
|
|
|
|
|
+ this.$refs.chart.height = this.$refs.container.$el.offsetHeight - 100;
|
|
|
|
|
+ this.init(this.value);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ init(value) {
|
|
|
|
|
+ this.officialNum = this.numInfo.official ? this.numInfo.official[value] || 0 : 0;
|
|
|
|
|
+ this.transferNum = this.numInfo.transfer ? this.numInfo.transfer[value] || 0 : 0;
|
|
|
|
|
+ this.official = this.priceInfo.official ? this.priceInfo.official[value] || 0 : 0;
|
|
|
|
|
+ this.transfer = this.priceInfo.transfer ? this.priceInfo.transfer[value] || 0 : 0;
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.myChart = new Chart(this.$refs.chart.getContext('2d'), {
|
|
|
|
|
+ type: 'doughnut',
|
|
|
|
|
+ data: {
|
|
|
|
|
+ labels: ['官方销售额', '二手市场销售额'],
|
|
|
|
|
+ datasets: [
|
|
|
|
|
+ {
|
|
|
|
|
+ title: '销售额',
|
|
|
|
|
+ backgroundColor: ['#FEB30E', '#FF7970'],
|
|
|
|
|
+ data: [this.official, this.transfer]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ options: {
|
|
|
|
|
+ responsive: true,
|
|
|
|
|
+ plugins: {
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ labels: {
|
|
|
|
|
+ generateLabels: function (chart) {
|
|
|
|
|
+ // Get the default label list
|
|
|
|
|
+ const original = Chart.overrides.pie.plugins.legend.labels.generateLabels;
|
|
|
|
|
+ const labelsOriginal = original.call(this, chart);
|
|
|
|
|
+
|
|
|
|
|
+ // Build an array of colors used in the datasets of the chart
|
|
|
|
|
+ let datasetColors = chart.data.datasets.map(function (e) {
|
|
|
|
|
+ return e.backgroundColor;
|
|
|
|
|
+ });
|
|
|
|
|
+ datasetColors = datasetColors.flat();
|
|
|
|
|
+
|
|
|
|
|
+ // Modify the color and hide state of each label
|
|
|
|
|
+ labelsOriginal.forEach(label => {
|
|
|
|
|
+ // There are twice as many labels as there are datasets. This converts the label index into the corresponding dataset index
|
|
|
|
|
+ label.datasetIndex = (label.index - (label.index % 2)) / 2;
|
|
|
|
|
+
|
|
|
|
|
+ // The hidden state must match the dataset's hidden state
|
|
|
|
|
+ label.hidden = !chart.isDatasetVisible(label.datasetIndex);
|
|
|
|
|
+
|
|
|
|
|
+ // Change the color to match the dataset
|
|
|
|
|
+ label.fillStyle = datasetColors[label.index];
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return labelsOriginal;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ onClick: function (mouseEvent, legendItem, legend) {
|
|
|
|
|
+ // toggle the visibility of the dataset from what it currently is
|
|
|
|
|
+ legend.chart.getDatasetMeta(
|
|
|
|
|
+ legendItem.datasetIndex
|
|
|
|
|
+ ).hidden = legend.chart.isDatasetVisible(legendItem.datasetIndex);
|
|
|
|
|
+ legend.chart.update();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ callbacks: {
|
|
|
|
|
+ label: function (context) {
|
|
|
|
|
+ const labelIndex = context.datasetIndex * 2 + context.dataIndex;
|
|
|
|
|
+ return context.chart.data.labels[labelIndex] + ': ' + context.formattedValue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- ]
|
|
|
|
|
- },
|
|
|
|
|
- options: { responsive: true, maintainAspectRatio: false }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ changeSelect() {
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.init(this.value);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
components: {
|
|
components: {
|
|
|
WidgetCard
|
|
WidgetCard
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
</script>
|
|
</script>
|
|
|
-<style lang="less" scoped></style>
|
|
|
|
|
|
|
+<style lang="less" scoped>
|
|
|
|
|
+.box-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-around;
|
|
|
|
|
+ align-self: stretch;
|
|
|
|
|
+ padding: 30px;
|
|
|
|
|
+}
|
|
|
|
|
+.box {
|
|
|
|
|
+ padding: 0 20px;
|
|
|
|
|
+ height: 125px;
|
|
|
|
|
+ background: #f5f7fa;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ min-width: 153px;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ .text1 {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #666666;
|
|
|
|
|
+ line-height: 20px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ }
|
|
|
|
|
+ .text2 {
|
|
|
|
|
+ color: #feb30e;
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ line-height: 29px;
|
|
|
|
|
+ margin-top: 2px;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ /deep/small {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &:nth-child(2) {
|
|
|
|
|
+ .text2 {
|
|
|
|
|
+ color: #4dcc6f;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+.header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|