Zrender 绘制树图,并且生成png下载

  1. 开发环境:VUE2
  2. 数据结构:  data: {value: 'xx', child: [{value: 'xx'}]}
  3. 实现代码
<!--
    props:  dataList <Object> 绘图数据
            draggable <Boolean> 图谱是否可拖拽
            initData <Object> 位置相关参数  默认设置
                r : 5, // 矩形圆角
                x : 200, // 起始x坐标
                y : 330, // 起始y坐标
                width : 200, // 矩形宽
                height : 60, // 矩形高
                typeRectWidth: 150, // 公司类型矩形宽
                typeRectHeight: 20, // 公司类型矩形高
                lineWidth: 60, // 线宽度
                lineHeight: 100, // 线长

            initStyle <Object> 样式相关参数 默认设置
                rectFillColor: '#14b8d4', // 矩形填充色
                rectStrokeColor: '#14b8d4', // 矩形描边色
                rectTextColor: '#333', // 矩形文字颜色
                typeRectFillColor: '#fff', // 类型矩形填充色
                typeRectStrokeColor: '#fff', // 类型矩形描边色
                typeRectTextColor: '#ffaa3d', // 类型矩形文字颜色
                circleFillColor: '#14b8d4', // 圆形填充色
                circleStrokeColor: '#14b8d4', // 圆形描边色
                circleTextColor: '#fff', // 圆形文字颜色
                circleR: 12, // 圆半径
                circleText: '投', // 圆形文字
                rectLineWidth: 2, // 描边线宽
                whiteColor: '#fff',
                baseColor: '#14b8d4',
                textFont: 14,
-->
<template>
    <div class="zrender">
        <button class="saveImgBtn" v-if="saveImg" @click="getCanvasImg">保存为图片</button>
        <div ref="canvasBox" id="zrender-canvas"></div>
    </div>
</template>

<script>
    import zrender from 'zrender'

    export default {
        name: 'zrender',
        data() {
            return {
                zr: null,
                group: null,
                countData: {},
                styleObj: {
                    rectFillColor: '#14b8d4', // 矩形填充色
                    rectStrokeColor: '#14b8d4', // 矩形描边色
                    rectTextColor: '#333', // 矩形文字颜色
                    typeRectFillColor: '#fff', // 类型矩形填充色
                    typeRectStrokeColor: '#fff', // 类型矩形描边色
                    typeRectTextColor: '#ffaa3d', // 类型矩形文字颜色
                    circleFillColor: '#14b8d4', // 圆形填充色
                    circleStrokeColor: '#14b8d4', // 圆形描边色
                    circleTextColor: '#fff', // 圆形文字颜色
                    circleR: 12, // 圆半径
                    circleText: '投', // 圆形文字
                    rectLineWidth: 2, // 描边线宽
                    whiteColor: '#fff',
                    baseColor: '#14b8d4',
                    textFont: 14,
                },
                dataObj: {
                    r: 5,
                    x: 20,
                    y: 0,
                    width: 200, // 矩形宽
                    height: 60, // 矩形高
                    typeRectWidth: 150, // 公司类型矩形宽
                    typeRectHeight: 20, // 公司类型矩形高
                    lineWidth: 60, // 线宽度
                    lineHeight: 100, // 线长
                },
                rectArray: [],
                countBreak: 0,
                tip: null,
                tipsText: null,
                maxHeight: 0,
                minHeight: 0,
                maxWidth: 0,
            }
        },
        props: {
            saveImg: {
                type: Boolean,
                default() {
                    return true
                }
            },
            mapLength: {
                type: [String, Number],
                default() {
                    return 3
                }
            },
            dataList: {
                type: Object,
                default() {
                    return {}
                }
            },
            draggable: { // 是否可以拖拽
                type: Boolean,
                default() {
                    return true
                }
            },
            initData: { // 图形尺寸相关
                type: Object,
                default() {
                    return {}
                }
            },
            initStyle: { // 样式相关设置
                type: Object,
                default() {
                    return {};
                }
            }
        },
        mounted() {
            this.init();
        },
        methods: {
            init() {
                this.setInitData();
                this.computedData();
            },
            // 设置props相关值
            setInitData() {
                let tmpSytle = this.styleObj,
                    tmpData = this.dataObj;
                for (let s in this.initStyle) {
                    if (tmpSytle.hasOwnProperty(s)) {
                        tmpSytle[s] = this.initStyle[s];
                    }
                }
                for (let d in this.initData) {
                    if (tmpData.hasOwnProperty(d)) {
                        tmpData[d] = this.initData[d];
                    }
                }
            },

            // 处理数据
            computedData() {
                if (this.dataList.hasOwnProperty('child')) {
                    let computedChildData = [].concat(this.dataList.child);
                    this.addChildCounts(computedChildData);
                    this.addLevel(computedChildData);

                    this.dataList['childCount'] = computedChildData.length;
                    this.dataList['count'] = computedChildData.length;
                    this.dataList['level'] = 0;
                    this.drawChart(computedChildData);
                }
            },
            addChildCounts(childData) {
                let data = childData;
                if (data && data.length > 0) {
                    for (let i = 0; i < data.length; i++) {
                        if (data[i].hasOwnProperty('child')) {
                            data[i]['childCount'] = data[i]['child'].length;
                            data[i]['count'] = data[i]['child'].length;
                            this.addChildCounts(data[i]['child'])
                        }
                    }
                }
            },
            addLevel(data) {
                let levelData = data || [];
                if (levelData.length > 0) {
                    // 第一层层级都是1
                    for (let i = 0; i < levelData.length; i++) {
                        this.level = 1;
                        levelData[i]['level'] = 1;
                        // 当下面有child的时候调用递归循环 根据父级的level去给子集添加循环
                        if (levelData[i].hasOwnProperty('child')) {
                            this.setLevel(levelData[i].child, levelData[i]['level']);
                        }
                    }
                }
            },
            // 递归for循环时等添加对应的层级level标识
            setLevel(data, curLevel) {
                let curData = data || [];
                curLevel++;
                if (curData.length > 0) {
                    for (let x in curData) {
                        curData[x]['level'] = curLevel;
                        if (curData[x].hasOwnProperty('child')) {
                            this.setLevel(curData[x].child, curData[x]['level']);
                        }
                    }
                }
            },
            drawChart() {
                let that = this;
                this.zr = zrender.init(document.getElementById('zrender-canvas')
                    // ,{
                    // renderer: 'canvas',
                    // devicePixelRatio: '2',
                    // width: 'auto',
                    // height: 'auto'
                    // }
                );
                this.group = new zrender.Group();
                this.group.draggable = this.draggable; // 是否开启拖拽
                this.group.progressive = -1; // 是否渐进加载(用于加载元素较多时)
                this.group.silent = false; // 是否相应鼠标事件 文档是反的  false响应  true不响应
                this.drawBack();

                this.zr.add(this.group);
                this.zr.id = 'canvas1';
                window.onresize = function () {
                    if (that.zr !== 'null') {
                        that.zr.resize({
                            width: null,
                            height: null
                        });
                    }
                };
                let dataObj = this.dataObj;
                let tmpData = Object.assign({},this.dataList);
                this.drawRect(dataObj.r, dataObj.x, dataObj.y, dataObj.width, dataObj.height, false, tmpData);

                this.updateChart()
            },
            updateChart() {
                let that = this;
                that.rectArray = [];
                let maxYHeight = [],
                    maxXWidth = [];
                this.group.eachChild(function(data) {
                    if (data.name && data.name.indexOf('rect') > -1) {
                        maxYHeight.push(data.shape.y);
                        maxXWidth.push(data.shape.x);
                        that.rectArray.push(data)
                    }
                });
                this.maxHeight = 0;
                this.minHeight = 0;
                this.maxWidth = 0;
                this.maxHeight = (maxYHeight.sort(function(a, b) {
                    return b - a
                }))[0];

                this.minHeight = (maxYHeight.sort(function(a, b) {
                    return a - b
                }))[0];

                this.maxWidth = (maxXWidth.sort(function(a, b) {
                    return b - a
                }))[0];

                // 根据绘制内容 设置画布高度  主要是为了能保存完整图片 因为保存图片只保存画布大小的部分
                let canvasBox = this.$refs.canvasBox;
                let styleObj = 'height:' + (parseFloat(Math.abs(this.maxHeight)) + parseFloat(Math.abs(this.minHeight)) + 300) + 'px; width:' +  (parseFloat(this.maxWidth) + 300) + 'px';
                canvasBox.setAttribute('style', styleObj);
                // 根据画布高度调整内容的高度
                this.group.attr('position', [0, parseFloat(Math.abs(this.minHeight))+20]);

                let levelObj = {},
                    len = that.rectArray;
                if (len.length > 0) {
                    for (let i = 0; i < len.length; i++) {
                        levelObj[len[i].name] = [];
                    }
                    // 将同一等级的图的位置放到一个数组中
                    for (let key in levelObj) {
                        for (let ii = 0; ii < len.length; ii++) {
                            if (len[ii].name === key) {
                                levelObj[key].push({
                                    x: len[ii].shape.x,
                                    y: len[ii].shape.y,
                                    id: len[ii].id,
                                    text: len[ii].style.text,
                                    name: len[ii].name
                                })
                            }
                        }
                    }
                    // 拿到了同一等级 升序排序
                    for (let iii in levelObj) {
                        if (levelObj[iii].length > 0) {
                            levelObj[iii].sort(function(a, b) {
                                return a.y - b.y
                            })
                        }
                    }

                    let tmpReverseData = [],
                        reverseData = [];
                    for (let s in levelObj) {
                        tmpReverseData.push({
                            id: s.substr(4,15),
                            value: levelObj[s]
                        });
                    }
                    tmpReverseData.sort(function(a, b) {
                        return b.id - a.id
                    });

                    for (let ss in tmpReverseData) {
                        reverseData.push(tmpReverseData[ss]['value']);
                    }
                    // 拿到分组切排序好的数据后进行计算 n - n+1 如果间隔大于固定值证明没重叠 计算差值 小于固定值 代表图片叠加了 固定值就是线高 id较小的赋值updateId
                    let updateId = '';
                    for (let n  = 0; n < reverseData.length; n++) {
                        if (reverseData[n].length > 1) {
                            for (let nn = 0; nn < reverseData[n].length -1; nn++) {
                                let curId = reverseData[n][nn],
                                    nextId = reverseData[n][nn + 1];
                                let intervals = Math.abs(nextId.y - curId.y) - this.dataObj.lineHeight ;
                                if (intervals < 0) {
                                    if (nextId.id > curId.id) {
                                        updateId = curId.id;
                                    }else {
                                        updateId = nextId.id;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    this.getDataId(this.dataList.child, updateId, this.dataList);
                }
            },
            getDataId(dataList, updateId, dataAll) {
                let data = dataList || [];
                if (data && data.length > 0 && updateId) {
                    for (let i = 0; i < data.length; i++) {
                        if (data[i].hasOwnProperty('child')) {
                            for (let ii = 0; ii < data[i].child.length; ii++) {
                                if(data[i]['child'][ii]['id'] === updateId) {
                                    // 如果当前循环只有一个子集 或者是第一个的时候 向父级循环再找
                                    if (dataAll && dataAll.childCount > 1 && data[i].id !== dataAll.child[0].id) {
                                        // console.log(data[i], dataAll);
                                        this.countBreak++;
                                        if (this.countBreak > 5000) {
                                            console.error('递归内存溢出');
                                            break;
                                        }
                                        if(data[i].moreHeight) {
                                            data[i].moreHeight += 1.5*this.dataObj.lineHeight;
                                        }else {
                                            data[i].moreHeight = 1.5*this.dataObj.lineHeight;
                                        }
                                        // 子集包含了updateId 给这个数据往上加固定高度
                                        this.updateData(this.dataList.child);
                                        this.zr.clear();
                                        this.drawChart();
                                    }else {
                                        updateId = data[i].id;
                                        this.getDataId(this.dataList.child, updateId, this.dataList);
                                    }
                                    break;
                                }
                            }
                            this.getDataId(data[i]['child'], updateId, data[i])
                        }
                    }
                }
            },
            updateData(dataList) {
                let data = dataList || [];
                if (data && data.length > 0) {
                    for (let i = 0; i < data.length; i++) {
                        if (data[i].hasOwnProperty('child')) {
                            data[i]['count'] = data[i]['childCount'];
                            this.updateData(data[i]['child'])
                        }
                    }
                }
            },
            /**
             * @desc: 创建矩形
             * @params: r <number> 圆角
             * @params: x ,y <number> 左上角起点横纵坐标
             * @params: width,height <number> 宽度与高度
             * @params: data <object> 循环数据
             * @params: drawTypeRect <boolean> 是否绘制公司类型矩形框
             * @return: null
             */
            drawRect(r, x, y, width, height, drawTypeRect, data ) {
                let that = this;
                let fillColor = this.styleObj.rectFillColor,
                    strokeColor = this.styleObj.rectStrokeColor,
                    textColor = this.styleObj.rectTextColor;
                drawTypeRect ? fillColor = this.styleObj.whiteColor : fillColor;
                !drawTypeRect ? textColor = this.styleObj.whiteColor : textColor;
                let rect = new zrender.Rect({
                    shape: {
                        r: r,
                        x: x,
                        y: y,
                        width: width,
                        height: height
                    },
                    style: {
                        fill: fillColor, // 填充颜色
                        stroke: strokeColor, // 描边颜色,默认null
                        lineWidth: this.styleObj.rectLineWidth, // 线宽
                        text: data.orgName || '--', // 内部显示文字
                        fontSize: this.styleObj.textFont, // 字体大小
                        textFill: textColor, // 颜色
                        truncate: { // 文字溢出相关设置
                            outerWidth: width,
                            ellipsis: '...'
                        },
                    },
                    name: 'rect' + data.level,
                    onclick: function (value) {
                        that.$emit('rectClick', data)
                    },
                    onmouseover: function(value) {
                        let datas = value.target.shape;
                        datas.text = data.orgName;
                        if (that.tip === null) {
                            that.drawTips(datas)
                        }else {
                            let x = datas.x + this.dataObj.width - 60,
                                y = datas.y + this.dataObj.height -10,
                                textWidth = data.text.length * 14;
                            that.tip.attr('shape', {
                                x: x,
                                y: y,
                                width: textWidth,
                            });
                            that.tipsText.attr('shape', {
                                x: x +1,
                                y: y +1,
                                width: textWidth - 2,
                            });
                            that.tipsText.attr('style', {
                                text: datas.text
                            });
                        }
                    },
                    onmouseout: function(value) {
                        that.group.remove(that.tip);
                        that.group.remove(that.tipsText);
                        that.tip = null;
                        that.tipsText = null
                    },
                });
                this.group.add(rect);
                // 给数据赋值 唯一id用于计算是否重叠
                data.id = rect.id || '';

                // 绘制公司类型文字矩形框
                if (drawTypeRect) {
                    this.drawCompanyTypeRect(x, y, data);
                }
                // 有child绘制
                if (data.hasOwnProperty('child')) {
                    // 绘制总引导横线 只有一个child的话不绘制引导线
                    let noGuideLine = false;
                    if (data.child.length === 1) {
                        noGuideLine = true
                    }
                    this.drawGuideLine(x + width, y + height / 2, data, noGuideLine);
                }
            },
            /**
             * @desc: 公司类型矩形
             * @params: x ,y <number> 左上角起点横纵坐标
             * @params: data <object> 循环数据
             * @return: null
             */
            drawCompanyTypeRect(x, y, data) {
                let rect = new zrender.Rect({
                    shape: {
                        r: 0,
                        x: x + this.dataObj.width / 2 - this.dataObj.typeRectWidth / 2,
                        y: y - this.dataObj.typeRectHeight / 2,
                        width: this.dataObj.typeRectWidth,
                        height: this.dataObj.typeRectHeight
                    },
                    style: {
                        fill: this.styleObj.typeRectFillColor, // 填充颜色,默认#000
                        stroke: this.styleObj.typeRectStrokeColor, // 描边颜色,默认null
                        lineWidth: 1, // 线宽, 默认1
                        text: '非挂牌上市公司' || '--', // 内部显示文字
                        fontSize: this.styleObj.textFont, // 字体大小
                        textFill: this.styleObj.typeRectTextColor, // 颜色
                        truncate: { // 文字溢出相关设置
                            outerWidth: this.dataObj.typeRectWidth,
                            ellipsis: '...'
                        }
                    }
                });
                this.group.add(rect);
            },
            /**
             * @desc: 绘制总引导横线 y轴相同  x轴不同
             * @params: x ,y <number> 起点横纵坐标
             * @params: data <object> 循环数据
             * @params: noGuideLine <boolean> 当只有一个子集  不绘制引导线
             * @return: null
             */
            drawGuideLine(x, y, data, noGuideLine) {
                let GuideLineWidth = noGuideLine ? 10 : this.dataObj.lineWidth;
                let line = new zrender.Line({
                    shape: {
                        x1: x, // 起点x
                        y1: y, // 起点y
                        x2: x + GuideLineWidth, // 终点x
                        y2: y // 终点y
                    },
                    style: {
                        stroke: this.styleObj.baseColor, // 填充颜色,默认#000
                    }
                });

                this.group.add(line);

                if (data.hasOwnProperty('child')) {
                    let child = data.child || [],
                        totalCount = 0;

                    // 计算有子集需要撑开的总高度 child长度为1、最后一个元素child有值不计算
                    for (let x  = 0; x < child.length; x++) {
                        if (child[x].hasOwnProperty('child') && child[x].child.length !== 1 && x !== child.length -1 ) {
                            totalCount += child[x].child.length;
                        }
                    }
                    totalCount +=  data.child.length;

                    // 计算起点 为总高度的一半  因为最后一个不画所以往上移动半个距离
                    let yStart= y + this.dataObj.lineHeight * ((totalCount / 2) + 0.5);

                    this.drawColumnLIne(x + GuideLineWidth, yStart, noGuideLine, data);
                }
            },
            // 绘制竖线 y轴不同 x轴不同
            drawColumnLIne(x, y, noGuideLine, data) {
                let x1 = '',
                    y1 = '',
                    x2 = '',
                    y2 = '',
                    lineColor = '';

                // 绘制竖线 起点终点的x坐标都是一个 只需确认起点Y与终点Y
                x1 = x2 = x;
                y1 = y;
                y2 = y1 - this.dataObj.lineHeight;

                let child = data.child || [];

                let lastTab = true;
                if (child[data.count - 1] && child[data.count - 1].hasOwnProperty('child') && data.childCount !== 1) {
                    y2 = y2 - ((child[data.count - 1].child.length - 1) * this.dataObj.lineHeight);
                    // 数据是最后一个时 不在增加全部高度
                    if (data.count === data.childCount) {
                        lastTab = false;
                        y2 = y2 + (((child[data.count - 1].child.length)/2) * this.dataObj.lineHeight);
                    }
                }
                // 当前模块里面有moreHeight 增加计算距离--------
                if (child[data.count] && child[data.count].hasOwnProperty('moreHeight')) {
                    y2 = y2 - parseFloat(child[data.count].moreHeight);
                }
                // 重新定义结束y2 作为下一次的起点y1
                data.yStart = y2;

                data.count === data.childCount && lastTab? lineColor = 'transparent' : lineColor = this.styleObj.baseColor;
                let line = new zrender.Line({
                    shape: {
                        x1: x1, // 起点x
                        y1: y1, // 起点y
                        x2: x2, // 终点x
                        y2: y2  // 终点y
                    },
                    style: {
                        stroke: lineColor, // 填充颜色
                    }
                });
                this.group.add(line);
                // 递减当前数据递归
                data.count--;

                // 如果高度是计算了child的高度 将横线位移到中间
                if (child[data.count] && child[data.count].hasOwnProperty('child') && data.count !== 0 && data.count !== data.childCount -1) {
                    let computedY =  data.yStart + ((child[data.count].child.length - 1) / 2) *this.dataObj.lineHeight;
                    this.drawRowLine(x1, computedY, data.child[data.count]);
                }else if (!lastTab) {
                    this.drawRowLine(x1, y1, data.child[data.count]);
                }else {
                    this.drawRowLine(x1, data.yStart, data.child[data.count]);
                }
                if (data.count > 0) {
                    this.drawColumnLIne(x, data.yStart, false, data)
                }

            },
            // 绘制横线 y轴相同  x轴不同
            drawRowLine(x, y, data) {
                let x1 = '',
                    y1 = '',
                    x2 = '',
                    y2 = '';
                x1 = x;
                y1 = y;
                x2 = x + this.dataObj.lineWidth;
                y2 = y1;
                let line = new zrender.Line({
                    shape: {
                        x1: x1, // 起点x
                        y1: y1, // 起点y
                        x2: x2, // 终点x
                        y2: y2  // 终点y
                    },
                    style: {
                        stroke: this.styleObj.baseColor, // 填充颜色,默认#000
                    }
                });
                this.group.add(line);
                this.drawCircleHasText(x1 + this.dataObj.lineWidth / 2, y);
                this.drawRect(this.dataObj.r, x1 + this.dataObj.lineWidth, y - this.dataObj.height / 2, this.dataObj.width, this.dataObj.height, true, data, )
            },
            // 绘制有文字的圆
            drawCircleHasText(x, y) {
                let Circle = new zrender.Circle({
                    shape: {
                        cx: x, // 圆心起点x
                        cy: y, // 圆心起点y
                        r: this.styleObj.circleR
                    },
                    style: {
                        fill: this.styleObj.circleFillColor,
                        stroke: this.styleObj.circleStrokeColor, // 填充颜色,默认#000
                        text: this.styleObj.circleText,
                        textFill: this.styleObj.circleTextColor, // 颜色
                        fontSize: this.styleObj.textFont, // 字体大小
                    },
                });
                this.group.add(Circle);
            },
            drawLoadMore(x, y) {
                let that = this;
                let Circle = new zrender.Circle({
                    shape: {
                        cx: x, // 圆心起点x
                        cy: y + 2, // 圆心起点y
                        r: this.styleObj.circleR
                    },
                    style: {
                        fill: this.styleObj.whiteColor,
                        stroke: this.styleObj.circleStrokeColor, // 描边颜色,默认#000
                        text: "+",
                        textFill: this.styleObj.circleStrokeColor, // 颜色
                        fontSize: 20, // 字体大小
                        fontWeight: '800',
                        lineWidth: 2
                    },
                    onclick: function() {
                        // that.group.eachChild(function (item) {
                        //     that.zr.remove(item)
                        // })
                        // that.maxLength = that.maxLength + that.mapLength;
                        // console.log(that.maxLength)
                        // that.drawChart();
                    }
                });
                this.group.add(Circle);
            },
            drawBack() {
                let rect = new zrender.Rect({
                    shape: {
                        r: 0,
                        x: 0,
                        y: parseFloat(this.minHeight) - 200,
                        width: this.zr.getWidth(),
                        height: this.zr.getHeight() + parseFloat(Math.abs(this.minHeight))
                    },
                    style: {
                        fill: '#fff', // 填充颜色,默认#000
                        stroke: 'transparent', // 描边颜色,
                        lineWidth: 0, // 线宽,
                    },
                    z: -1
                });
                this.group.add(rect);
            },
            drawTips(data) {
                let x = data.x + this.dataObj.width - 60,
                    y = data.y + this.dataObj.height -10,
                    textWidth = data.text.length * 14;
                this.tip = new zrender.Rect({
                    shape: {
                        r: 0,
                        x: x,
                        y: y,
                        width: textWidth,
                        height: 26
                    },
                    style: {
                        fill: '#000', // 填充颜色
                        stroke: '#000', // 描边颜色,默认null
                        lineWidth: 2, // 线宽,
                    },
                    z: 2
                });
                this.group.add(this.tip);

                this.tipsText = new zrender.Rect({
                    shape: {
                        r: 0,
                        x: x +1,
                        y: y +1,
                        width: textWidth - 2,
                        height: 24
                    },
                    style: {
                        fill: '#fff', // 填充颜色
                        stroke: '#fff', // 描边颜色,默认null
                        lineWidth: 2, // 线宽
                        text: data.text || '--', // 内部显示文字
                        fontSize: 12, // 字体大小
                        textFill: '#000', // 颜色fff
                        textVerticalAlign: 'middle',
                    },
                    z: 3
                });
                this.group.add(this.tipsText);
            },
            getCanvasImg() {
                let canvas = document.querySelector("#zrender-canvas canvas");
                if (canvas) {
                    let strDataURI = canvas.toDataURL("image/jpeg");
                    let a = document.createElement("a");
                    a.download = 'canvasImg';  //下载的文件名,
                    a.href =strDataURI;
                    document.body.appendChild(a);
                    a.click();
                    a.remove();
                }
            }
        }
    }
</script>

<style>
    .zrender {
        height: 500px;
    }
    .saveImgBtn {
        position: absolute;
        top: 0;
        right: 15px;
        padding: 5px;
        border: 1px solid #14b8d4;
        background-color: #fff;
        color: #14b8d4;
        border-radius: 4px;
        cursor: pointer;
    }
</style>

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值