css样式封装柱状图
<template>
<div class="xxc-charts" ref="charts" >
<div class="xxc-graph-x-axis" ref="xAxis" v-if="types === 0">{{optes.xAxis.name}}</div>
<div class="xxc-graph-y-axis" ref="yAxis" v-if="types === 0">
<p :class="['xxc-graph-y-axis-line', { 'xxc-graph-y-axis-name' : index === 0 }]" v-for="(item, index) in yAxisLines" :key="index">{{item}}</p>
</div>
<div class="xxc-graph-legend" v-if="types === 0 && showLegende">
<div class="xxc-graph-legend-item" v-for="(item, index) in legendes" :key="index">
<i class="xxc-graph-legend-item-icon" :style="{ 'background-color': optes.color[index] }"></i>{{item}}
</div>
</div>
<div class="xxc-graph-box" ref="graphBox">
<div class="xxc-graph" ref="graph"></div>
</div>
<div class="xxc-graph-tip" ref="graphTip" v-show="tips.show && types === 0">
<div class="xxc-graph-tip-text">
<p v-for="(item, index) in tips.data" :key="index">{{item.name}}:{{item.value + modes}}</p>
</div>
<i class="xxc-graph-tip-bottom-arrow"></i>
</div>
</div>
</template>
<script>
const deepAssgin = (o, s) => {
if (typeof s === "object") {
if (Object.entries(s).length > 0) {
for (let k in s) {
if (typeof s[k] === "object") {
if (!o[k]) o[k] = s[k] instanceof Array ? [] : {};
if (s[k]) deepAssgin(o[k], s[k]);
else o[k] = null;
} else if (typeof s[k] === "undefined") o[k] = undefined;
else o[k] = s[k];
}
} else {
if (o instanceof Array) o.splice(0, o.length);
else {
for (let k in o) delete o[k];
}
}
} else o = s;
};
const calc = num => {
let fs = Number(
document.querySelector("html").style.fontSize.replace(/px/, "")
);
return (num * fs) / 100;
};
const defaultOpt = [
{
color: ["#4a90e2", "#ff6200"],
grid: {
top: 0,
left: 0,
bottom: 0,
right: 0
},
xAxis: {
type: "category",
name: "月份",
nameTextStyle: {
color: "rgba(0, 0, 0, 0)"
},
axisLine: {
show: false
},
axisTick: {
length: 0
},
splitArea: {
show: true,
areaStyle: {
color: ["rgba(0, 0, 0, .02)", "rgba(0, 0, 0, 0)"]
}
},
axisLabel: {
interval: 0,
inside: true,
color: "rgba(0, 0, 0, .3)",
fontSize: calc(24),
margin: calc(4)
},
axisPointer: {
show: true,
type: "shadow",
shadowStyle: {
color: ["rgba(255, 98, 0, .1)"]
}
}
},
yAxis: {
show: false
},
series: [
{
zlevel: 10,
type: "line",
smooth: true,
seriesLayoutBy: "row",
symbol: "circle",
symbolSize: calc(4),
showSymbol: false,
lineStyle: {
width: calc(2),
shadowColor: "rgba(0, 0, 0, .2)",
shadowBlur: calc(8),
shadowOffsetY: calc(4)
}
},
{
zlevel: 10,
type: "line",
smooth: true,
seriesLayoutBy: "row",
symbol: "circle",
symbolSize: calc(4),
showSymbol: false,
lineStyle: {
width: calc(2),
shadowColor: "rgba(0, 0, 0, .2)",
shadowBlur: calc(8),
shadowOffsetY: calc(4)
}
}
]
}
];
export default {
name: "xxc-graph",
props: {
// echarts配置项
option: {
type: Object,
default() {
return JSON.parse(JSON.stringify(defaultOpt[0]));
}
},
// 数据
data: {
type: Array,
default() {
return [];
}
},
// 曲线图样式 默认0
type: {
type: [Number, String],
default: 0
},
// y轴名字
yAxisName: {
type: String,
default: ""
},
// y轴单位 0=>'', 1=>%, 2=>万, 3=>亿
mode: {
type: [String, Number],
default: ""
},
// x轴数据
itemXData: {
type: Array,
default() {
return [];
}
},
// 显示隐藏数据标题
showLegende: {
type: Boolean,
default: true
}
},
data() {
return {
opt: JSON.parse(JSON.stringify(defaultOpt[this.type])),
flag: false,
tips: {
show: false,
data: []
}
};
},
methods: {
init() {
this.setCharts(this.optes);
},
updateOpt() {
this.opt = JSON.parse(JSON.stringify(defaultOpt[this.types]));
if (Object.keys(this.option).length) deepAssgin(this.opt, this.option);
if (this.flag) {
deepAssgin((this.opt.dataset = {}), this.datasetes);
this.opt.yAxis = {
max: this.yAxises.max,
min: this.yAxises.min
};
}
},
setCharts(opt) {
if (this.chartses) {
this.chartses.on("updateAxisPointer", event => {
this.tips.show = typeof event.dataIndex === "number" ? true : false;
if (!this.tips.show) return;
this.tips.data = [];
for (let i = 1; i < this.datasetes.source.length; i++) {
this.tips.data.push({
name: this.datasetes.source[i][0],
value: this.datasetes.source[i][event.dataIndex + 1]
});
}
let xWidth =
this.$refs.graph.children[0].offsetWidth / this.xAxisDataes.length;
let tipsLeft =
this.$refs.graphBox.offsetLeft - this.$refs.graphBox.scrollLeft;
tipsLeft += xWidth * event.dataIndex;
tipsLeft += xWidth / 2;
tipsLeft -= this.$refs.graphTip.offsetWidth / 2;
let tipsTop = [];
for (let i = 1; i < this.datasetes.source.length; i++) {
tipsTop.push(this.datasetes.source[i][event.dataIndex + 1]);
}
tipsTop = 1 - Math.max.apply(null, tipsTop) / this.yAxises.max;
tipsTop =
this.$refs.graphBox.offsetHeight * tipsTop -
this.$refs.graphTip.offsetHeight;
this.$refs.graphTip.style.left = tipsLeft + "px";
this.$refs.graphTip.style.top = tipsTop + "px";
this.$emit("click", event.dataIndex);
});
this.chartses.setOption(opt);
this.chartses.resize({
width: this.widthes,
height: this.$refs.graphBox.offsetHeight
});
}
}
},
computed: {
chartses() {
if (this.$refs.graph) return this.$echarts.init(this.$refs.graph);
return null;
},
optes() {
this.updateOpt();
return this.opt;
},
datasetes() {
let dataset;
switch (this.types) {
case 0:
dataset = {};
if (this.data.length && this.xAxisDataes.length) {
dataset.source = [];
dataset.source.push(["product", ...this.xAxisDataes]);
let labels = ["去年", "今年"];
this.data.forEach((item, i) => {
dataset.source.push([item.name || labels[i] || "", ...item.data]);
});
}
break;
}
return dataset;
},
types() {
return this.type;
},
widthes() {
let width = "auto";
if (this.flag) {
if (this.datasetes.source[0].length > 13)
width = this.datasetes.source[0].length * calc(48);
}
return width;
},
styles() {
let style = {
width: "100%",
height: "100%"
};
if (this.datasetes.source[0].length > 13)
style.width = `${(this.datasetes.source[0].length * calc(48)) /
100}rem`;
return style;
},
legendes() {
let legends = [];
switch (this.types) {
case 0:
if (this.flag) {
this.datasetes.source.forEach((item, i) => {
if (i) legends.push(item[0]);
});
}
break;
}
return legends;
},
colores() {
let colors = [];
switch (this.types) {
case 0:
this.optes.color.forEach(item => {
colors.push(item);
});
break;
}
return colors;
},
yAxises() {
let yAxis = {
max: 0,
min: 0,
avg: 0,
interval: 0,
maxLabel: "",
minLabel: ""
};
if (this.flag) {
let y1 = JSON.parse(JSON.stringify(this.datasetes.source[1]));
let y2 = JSON.parse(JSON.stringify(this.datasetes.source[2]));
y1.splice(0, 1);
y2.splice(0, 1);
yAxis.max = Math.max(
Math.max.apply(null, y1),
Math.max.apply(null, y2)
);
yAxis.min = Math.min(
Math.min.apply(null, y1),
Math.min.apply(null, y2)
);
let zeros = ["", ""];
for (let i = 0; i < parseInt(yAxis.max).toString().length - 1; i++)
zeros[0] += "0";
for (let i = 0; i < parseInt(yAxis.min).toString().length - 1; i++)
zeros[1] += "0";
yAxis.max = Number(
Number(yAxis.max.toString().substr(0, 1)) + 1 + zeros[0]
);
yAxis.min = Number(
Number(yAxis.min.toString().substr(0, 1)) - 1 + zeros[1]
);
yAxis.avg = (yAxis.max + yAxis.min) / 2;
yAxis.interval = parseInt((yAxis.max - yAxis.min) / 7);
(yAxis.maxLabel = yAxis.max), (yAxis.minLabel = yAxis.min);
yAxis.max += yAxis.interval;
yAxis.min -= yAxis.interval * 2;
}
return yAxis;
},
yAxisLines() {
let max = this.yAxises.maxLabel;
let min = this.yAxises.minLabel;
let avg = this.yAxises.avg;
let unit = 1;
switch (this.modes) {
case "万":
unit = 10000;
break;
case "亿":
unit = 100000000;
break;
}
max = (max / unit).toFixed(1).toString() + this.modes;
min = (min / unit).toFixed(1).toString() + this.modes;
avg = (avg / unit).toFixed(1).toString() + this.modes;
return [this.yAxisName, "", max, "", avg, "", min, ""];
},
xAxisDataes() {
return this.itemXData;
},
modes() {
if (typeof this.mode === "string" && isNaN(this.mode)) return this.mode;
if (this.mode === "") return "";
let modess = ["", "%", "万", "亿"];
return modess[this.mode];
}
},
mounted() {
this.$nextTick(() => {
this.init();
});
},
watch: {
optes: {
handler(opt) {
this.setCharts(opt);
},
deep: true
},
datasetes: {
handler(dataset) {
this.flag = Object.keys(dataset).length;
},
deep: true
}
}
};
</script>
<style lang="less">
.size() {
width: 100%;
// height: 100%;
position: relative;
}
.xxc-charts {
.size();
min-height: 3.53rem;
.xxc-graph-box {
.size();
width: calc(100% - 1.74rem);
height: calc(100% - 0.4rem);
overflow-x: scroll;
position: absolute;
top: 0.4rem;
right: 0.8rem;
bottom: 0;
left: 0.94rem;
&::-webkit-scrollbar {
display: none;
}
}
.xxc-graph-x-axis {
position: absolute;
bottom: 0;
right: 0.32rem;
font-size: 0.24rem;
color: rgba(0, 0, 0, 0.7);
}
.xxc-graph-y-axis {
.size();
position: absolute;
top: 0;
left: 0;
.xxc-graph-y-axis-line {
width: 100%;
height: 0.4rem;
line-height: 0.4rem;
border-bottom: 1px dashed rgba(0, 0, 0, 0.1);
color: rgba(0, 0, 0, 0.3);
font-size: 0.24rem;
padding-left: 0.32rem;
text-align: left;
&.xxc-graph-y-axis-name {
line-height: 0.24rem;
color: rgba(0, 0, 0, 0.7);
}
}
}
.xxc-graph-legend {
display: flex;
justify-content: flex-end;
padding-bottom: 0.16rem;
.xxc-graph-legend-item {
height: 0.24rem;
line-height: 0.24rem;
font-size: 0.24rem;
color: rgba(0, 0, 0, 0.7);
margin-right: 0.32rem;
display: flex;
align-items: center;
.xxc-graph-legend-item-icon {
width: 0.12rem;
height: 0.12rem;
border-radius: 100px;
margin-right: 0.08rem;
}
}
}
.xxc-graph-tip {
position: absolute;
background-color: #fff;
border: 1px solid #ff6200;
border-radius: 0.08rem;
box-shadow: 0 0.04rem 0.1rem rgba(255, 98, 0, 0.2);
transition: all 0.2s;
.xxc-graph-tip-text {
position: relative;
z-index: 100;
background-color: #fff;
padding: 0.16rem;
box-sizing: border-box;
border-radius: 0.08rem;
p {
color: rgba(0, 0, 0, 0.7);
font-size: 0.24rem;
line-height: 0.24rem;
margin-bottom: 0.08rem;
text-align: left;
&:last-child {
margin-bottom: 0;
}
}
}
.xxc-graph-tip-bottom-arrow {
position: absolute;
bottom: 0;
left: 50%;
width: 0.24rem;
height: 0.24rem;
box-sizing: border-box;
border-bottom: 1px solid #ff6200;
border-right: 1px solid #ff6200;
box-shadow: 0 0 0.1rem rgba(255, 98, 0, 0.2);
transform: translate(-50%, 50%) rotate(45deg);
background-color: #fff;
border-radius: 0.02rem;
}
}
}
</style>
浙公网安备 33010602011771号