Binyy Wuhan

i love Flex i come from Wuhan

导航

数据可视化【原创】vue复合数字形式展示

做数据可视化的时候,经常碰到需要很灵活的数字形式展示。

先上个效果图:

 如图包括名称,数量,别名,单位,上升下降,环比等等的复合数据展示,并且需要支持样式灵活配置。

此组件包括2个模块,父容器组件box-group,其中每一项的子组件box。

 

父组件 box-group

 1 <template>
 2     <div class="box-group">
 3         <div class="box-wrapper" v-for="(item, index) in dataProvider" :key="index" v-if="index < dataProvider.length && index % col == 0">
 4             <box
 5                 class="item"
 6                 :theme="theme"
 7                 :style="{ marginBottom: lineGap }"
 8                 v-for="(bb, idx) in colArray"
 9                 :dataProvider="dataProvider[index + idx]"
10                 :key="idx"
11                 :openType="openType"
12                 @selected="selected"
13             ></box>
14         </div>
15     </div>
16 </template>
17 
18 <script>
19 import Box from './components/box.vue';
20 
21 export default {
22     name: 'LiloBoxGroup',
23     props: {
24         col: {
25             type: Number,
26             default: 2
27         },
28         theme: {
29             type: String,
30             default: 'blue'
31         },
32         lineGap: {
33             type: String,
34             default: '10px'
35         },
36         dataProvider: {
37             type: Array,
38             default() {
39                 return [
40                     // { label: '总人口数', value: '123213', unit: '人', color: '#ff0000' },
41                     // { label: '总户数', value: '123213', unit: '户', color: '#123312' },
42                     // { label: '总人口数', value: '123213', unit: '人', color: '#235234' },
43                     // { label: '总户数', value: '123213', unit: '户', color: '#444444' }
44                 ];
45             }
46         },
47         openType: {
48             type: String,
49             default: 'popup'
50         }
51     },
52     data() {
53         return {};
54     },
55     components: {
56         Box
57     },
58     mounted() {},
59     computed: {
60         colArray() {
61             let result = [];
62             for (let i = 0; i < this.col; i++) {
63                 result.push(i);
64             }
65             return result;
66         }
67     },
68     methods: {
69         selected(data) {
70             this.$emit('selected', data);
71         }
72     }
73 };
74 </script>
75 
76 <style lang="scss" scoped>
77 .box-group {
78     .box-wrapper {
79         display: flex;
80         margin-top: 5px;
81         .item {
82             flex: 1;
83         }
84     }
85 }
86 </style>

 

子组件 box

  1 <template>
  2     <div class="box">
  3         <div class="icon-wrapper"><div class="icon" :style="iconStyle" v-if="dataProvider"></div></div>
  4         <div class="box-wrapper" v-if="dataProvider">
  5             <div class="label" :style="labelStyle">{{ dataProvider.label }}</div>
  6             <div @click="goTo">
  7                 <span class="value" :style="valueStyle" v-if="valueShow">{{ dataProvider.value | toThousandFilter }}</span>
  8                 <span class="alias" :style="aliasStyle">{{ dataProvider.alias }}</span>
  9             </div>
 10             <div class="unit" :style="unitStyle" v-if="valueShow">
 11                 {{ dataProvider.unit }}
 12                 <span v-if="dataProvider.hasOwnProperty('percent') && dataProvider.percent" 
 13                     class="percent" :style="percentStyle" v-html="percentFormatter"></span>
 14                 <span v-if="dataProvider.hasOwnProperty('trend') && dataProvider.trend" 
 15                     class="trend" :style="trendStyle" v-html="trendFormatter"></span>
 16             </div>
 17         </div>
 18         <!-- <component ref="component" :is="openType" /> -->
 19     </div>
 20 </template>
 21 
 22 <script>
 23 import variables from '../../../../src/styles/variables.scss';
 24 // import Popup from './popup.vue';
 25 // import Drawer from './Drawer.vue';
 26 export default {
 27     name: 'LiloBox',
 28     // components: {
 29     //     Popup,
 30     //     Drawer
 31     // },
 32     props: {
 33         dataProvider: {
 34             type: Object,
 35             default() {
 36                 return null;
 37             }
 38         },
 39         theme: {
 40             type: String,
 41             default: 'blue'
 42         },
 43         openType: {
 44             type: String,
 45             default: 'popup'
 46         }
 47     },
 48     computed: {
 49         themeObject() {
 50             return {
 51                 icon: variables[this.theme],
 52                 label: variables[this.theme],
 53                 value: variables[this.theme],
 54                 alias: variables[this.theme],
 55                 unit: variables[this.theme]
 56             };
 57         },
 58         valueShow() {
 59             if (this.dataProvider.hasOwnProperty('show')) {
 60                 return this.dataProvider.show;
 61             } else {
 62                 return true;
 63             }
 64         },
 65         iconStyle() {
 66             return {
 67                 'background-color': this.dataProvider.iconColor || this.dataProvider.color || this.themeObject.icon
 68             };
 69         },
 70         labelStyle() {
 71             return {
 72                 color: this.dataProvider.labelColor || this.dataProvider.color || this.themeObject.label,
 73                 fontSize: (this.dataProvider.labelSize || 1) + 'rem'
 74             };
 75         },
 76         valueStyle() {
 77             return {
 78                 color: this.dataProvider.valueColor || this.dataProvider.color || this.themeObject.value,
 79                 fontSize: (this.dataProvider.valueSize || 1.5) + 'rem'
 80             };
 81         },
 82         aliasStyle() {
 83             return {
 84                 color: this.dataProvider.aliasColor || this.dataProvider.color || this.themeObject.alias,
 85                 fontSize: (this.dataProvider.aliasSize || 1) + 'rem'
 86             };
 87         },
 88         unitStyle() {
 89             return {
 90                 color: this.dataProvider.unitColor || this.dataProvider.color || this.themeObject.unit,
 91                 fontSize: (this.dataProvider.unitSize || 1) + 'rem'
 92             };
 93         },
 94         percentStyle() {
 95             return {
 96                 color: this.dataProvider.percent > 0 ? variables.red : variables.deepGreen
 97             };
 98         },
 99         trendStyle() {
100             return {
101                 color: this.dataProvider.trend > 0 ? variables.red : variables.deepGreen
102             };
103         },
104         percentFormatter() {
105             const _percent = this.dataProvider.percent;
106             if (!_percent || _percent === 0) {
107                 return '';
108             }
109             if (_percent > 0) {
110                 return '<i class="el-icon-caret-top" style="margin-right:3px;"></i>' + Math.abs(_percent) + '%';
111                 // return "⬆" + Math.abs(_percent) + "%"
112             } else {
113                 return '<i class="el-icon-caret-bottom" style="margin-right:3px;"></i>' + Math.abs(_percent) + '%';
114                 // return "⬇" + Math.abs(_percent) + "%"
115             }
116         },
117         trendFormatter() {
118             const _trend = this.dataProvider.trend;
119             if (!_trend || _trend === 0) {
120                 return '';
121             }
122             if (_trend > 0) {
123                 return '<i class="el-icon-caret-top" style="margin-right:3px;"></i>' + Math.abs(_trend);
124                 // return "⬆" + Math.abs(_trend) + "%"
125             } else {
126                 return '<i class="el-icon-caret-bottom" style="margin-right:3px;"></i>' + Math.abs(_trend);
127                 // return "⬇" + Math.abs(_trend) + "%"
128             }
129         }
130     },
131     methods: {
132         goTo() {
133             // if (this.dataProvider.href) {
134             // this.$refs.component.show(this.dataProvider);
135             // }
136             this.$emit('selected', this.dataProvider);
137         }
138     }
139 };
140 </script>
141 
142 <style lang="scss" scoped>
143 $blue: #0058a5;
144 $grey : #585858;
145 
146 .box {
147     display: flex;
148     .icon-wrapper {
149         flex: 0 0 20px;
150         display: flex;
151         align-items: center;
152         justify-content: center;
153         .icon {
154             width: 5px;
155             height: 50%;
156             background-color: $blue;
157         }
158     }
159     .box-wrapper {
160         flex: 1;
161         display: flex;
162         flex-direction: column;
163     }
164     .label {
165         color: $grey;
166         // font-size: 1rem;
167     }
168     .value {
169         color: $blue;
170         // font-size: 1.5rem;
171         font-weight: 550;
172         cursor: pointer;
173     }
174     .alias {
175         color: $blue;
176         // font-size: 1rem;
177         font-weight: 550;
178         cursor: pointer;
179         margin-left: 5px;
180     }
181     .percent {
182         color: $blue;
183         // font-size: 1rem;
184         font-weight: 550;
185         cursor: pointer;
186     }
187     .trend {
188         color: $blue;
189         // font-size: 1rem;
190         font-weight: 550;
191         cursor: pointer;
192     }
193     .unit {
194         color: $blue;
195         // font-size: 1rem;
196     }
197 }
198 </style>

 

调用案例:

<lilo-box-group :dataProvider="boxData" theme="blue" :col="6" lineGap="10px" @selected="boxGroupSelected"></lilo-box-group>
 1 boxData: [
 2                 {
 3                     label: '初等教育',
 4                     value: 12447,
 5                     unit: '人',
 6                     alias: '1.5%',
 7                     // percent: 0.3,
 8                     trend: -120,
 9                     valueSize: 0.9,
10                     aliasSize: 0.7
11                 },
12                 {
13                     label: '中等教育',
14                     value: 579160,
15                     unit: '人',
16                     alias: '69.6%',
17                     percent: 5.8,
18                     valueSize: 0.9,
19                     aliasSize: 0.7
20                 },
21                 {
22                     label: '高等教育',
23                     value: 66622,
24                     unit: '人',
25                     alias: '8%',
26                     // percent: -8.6,
27                     trend: -86,
28                     valueSize: 0.9,
29                     aliasSize: 0.7
30                 },
31                 {
32                     label: '研究生教育',
33                     value: 3734,
34                     unit: '人',
35                     alias: '0.4%',
36                     // percent: -0.2,
37                     trend: 189,
38                     valueSize: 0.9,
39                     aliasSize: 0.7
40                 },
41                 {
42                     label: '有房',
43                     value: 459386,
44                     unit: '人',
45                     alias: '55.2%',
46                     percent: -5,
47                     valueSize: 1.2,
48                     aliasSize: 0.9
49                 },
50                 {
51                     label: '有车',
52                     value: 63210,
53                     unit: '人',
54                     alias: '7.6%',
55                     percent: -6.3,
56                     valueSize: 1.2,
57                     aliasSize: 0.9
58                 }
59             ]

 1 boxGroupSelected(val) { 2 console.log(val); 3 } 

 

附录:variables.scss

// base color

// $blue: #324157;
// $menuBg: #304156;
// $menuHover: #263445;
// $subMenuBg: #1f2d3d;
// $subMenuHover: #001528;

$color-background-s: rgba(0, 0, 0, 0.9);

$blue: #00417a;
$commonBlue: #0000ff;
// $menuBg: #00417a;
$menuBg: #ffffff;
// $menuHover: #00355e;
$menuHover: #f4f4f4;
// $subMenuBg: #003462;
$subMenuBg: #ffffff;
// $subMenuHover: #002546;
$subMenuHover: #e7e7e7;
$menuBorderRight: 1px solid #c5c1c1;

$light-blue: #1890ff;
$red: #f56c6c;
$deep-red: #ff0000;
$blood-red: #d50000;
$pink: #dc67ce;
$green: #0bbd87;
$tiffany: #4ab7bd;
$yellow: #e6a23c;
$deep-yellow: #d69737;
$panGreen: #30b08f;
$purple: #6771dc;
$grey: #585858;
$light-grey: #f0f0f0;
$orange: #ff8037;
$deep-green: #07885f;
$light-green: #0bbd8722;
$light-purple: #6771dc22;
// sidebar
// $menuText: #bfcbd9;
$menuText: #002546;
// $menuActiveText: #409eff;
$menuActiveText: #08ac76;
// $subMenuActiveText: #f4f4f5;
$subMenuActiveText: #00417a;

$sideBarWidth: 230px;
$headerHeight: 54px;

// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
    blue: $blue;
    commonBlue: $commonBlue;
    lightBlue: $light-blue;
    red: $red;
    deepRed: $deep-red;
    bloodRed: $blood-red;
    pink: $pink;
    green: $green;
    deepGreen: $deep-green;
    lightGreen: $light-green;
    lightPurple: $light-purple;
    tiffany: $tiffany;
    yellow: $yellow;
    deepYellow: $deep-yellow;
    orange: $orange;
    panGreen: $panGreen;
    purple: $purple;
    grey: $grey;
    lightGrey: $light-grey;
    menuText: $menuText;
    menuActiveText: $menuActiveText;
    subMenuActiveText: $subMenuActiveText;
    menuBg: $menuBg;
    menuHover: $menuHover;
    subMenuBg: $subMenuBg;
    subMenuHover: $subMenuHover;
    sideBarWidth: $sideBarWidth;
}
View Code

 

 

参数解释:

col:每行的列数

theme:主题,这个是我的整个插件有个variables.scss,里面有很多主题色

lineGap:行间距

dataProvider:

[{

label: '初等教育', //标题
value: 12447, //主数值
unit: '人', //单位
alias: '1.5%', //别名
percent: 0.3, //百分比
trend: -120, //趋势值
labelSize: 1, //标题字体大小 rem
valueSize: 0.9, //主数值字体大小 rem
aliasSize: 0.7, //别名字体大小 rem
unitSize: 1, //单位字体大小 rem
color: '#000000', //所有文字颜色,颜色优先级为 具体子项颜色(下面的颜色参数)> color > theme
iconColor: '#000000', //左边边框颜色
labelColor: '#000000', //标题文字颜色
valueColor: '#000000', //主数值文字颜色
aliasColor: '#000000', //别名文字颜色
unitColor: '#000000', //单位文字颜色

}]

 

posted on 2023-08-24 15:02  Binyy_Wuhan  阅读(34)  评论(0编辑  收藏  举报