做数据可视化的时候,经常碰到需要很灵活的数字形式展示。
先上个效果图:
如图包括名称,数量,别名,单位,上升下降,环比等等的复合数据展示,并且需要支持样式灵活配置。
此组件包括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.css
// 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.css,里面有很多主题色
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', //单位文字颜色
}]
详细源码请移步 https://gitee.com/binyylovesino/lilo-ui
欢迎各路大佬指导、提问~
标签:blue,Vue,return,color,percent,复合,可视化,dataProvider,unit From: https://www.cnblogs.com/loveFlex/p/17654170.html