Echarts篇

基础

所有图都是一个dom,一个options的写法,主要的options的配置,太多了

html部分
<div
    :id="config.id"
    style="width:100%;height: 100%">
</div>

js部分
import echarts from 'echarts'
import 'echarts-wordcloud' // 词云图,单独引入
Vue.prototype.$echarts = echarts

const myChart = this.$echarts.init(document.getElementById(this.config.id))

myChart.setOption(this.config.option)

词云图

词云图是要另外安装依赖的,echarts里面并没有包含这个功能

"dependencies": {
    "axios": "^0.21.1",
    "core-js": "^3.15.2",
    "echarts": "^4.9.0", //本地
    "echarts-wordcloud": "^1.1.3",//就是这个
    "element-ui": "^2.4.5",
    "html2canvas": "^1.0.0-rc.7",
    "vue": "^2.6.14",
    "vue-router": "^3.5.2",
    "vuex": "^3.6.2",
    "xlsx": "^0.16.5"
  },

配置

// 声量情感分布-提及内容词云
      wordOption: {
        id: 'wordOption',
        option: {
          series: [
            {
              // 词的类型
              type: 'wordCloud',
              // 设置字符大小范围,那种字太多的有可能因为最小值太小不显示,可以设大一点
              sizeRange: [16, 58],
              // 每个字的倾斜角度
              rotationRange: [-45, 90],
              textStyle: {
                normal: {
                  // 生成随机的字体颜色
                  color: function () {
                    return 'rgb(' + [
                      Math.round(Math.random() * 160),
                      Math.round(Math.random() * 160),
                      Math.round(Math.random() * 160)
                    ].join(',') + ')'
                  }
                }

              },
              // 不要忘记调用数据
              data: [
                {
                  'name': '花鸟市场',
                  'value': 1446
                },
                {
                  'name': '汽车',
                  'value': 928
                },
                {
                  'name': '视频',
                  'value': 906
                },
                {
                  'name': '电视',
                  'value': 825
                },
                {
                  'name': '动漫',
                  'value': 486
                }
              ]

            }
          ]
        }
      },

柱状图

配置

// 声量情感分布
      volEmotionOption: {
        id: 'volEmotionOption',
        option: {
          color: ['#32B0F2', '#006699', '#4cabce'], // 这个是颜色
          xAxis: {
            type: 'category',
            data: []
          },
          yAxis: [
            {
              name: '声量',
              type: 'value'
            },
            {
              name: '占比',
              type: 'value',
              min: 0,
              max: 100,
              axisLabel: {
                formatter: '{value} %'
              }
            }
          ],
          legend: {
            bottom: '5%',
            data: ['总体', '正面', '负面']
          },
          series: [
            {
              name: '总体',
              type: 'bar',
              data: [100]
            },
            {
              name: '正面',
              type: 'bar',
              data: [200]
            },
            {
              name: '负面',
              type: 'bar',
              data: [300]
            }
          ]
        }
      },

柱状图的xAxis和series,legend的关系有点奇怪,emmm,我笔记懒的详细些了,我就大概说一下
xAxis.data,比如有3个,[1,2,3];
series的每一项的data也必须有3值,对应每个柱子的内容,注意不是series的个数,是series每一项的data这个个数;

series: [
            {
              name: '总体',
              type: 'bar',
              data: [100,200,300]  //是这个
            },

legend是图下面的图例,可以点击让柱子消失,但是它的data的值是对应series每一项的值的name,如果series的每一项没name,你怎么配置legend都没用

饼图

配置

officialVol: {
        id: 'officialVol',
        option: {
          color: ['rgb(114, 143, 223)', 'rgb(106, 203, 148)'],
          tooltip: {
            trigger: 'item'
          },

          legend: {
            bottom: '5%'
          },
          series: [
            {
              name: '占比',
              type: 'pie',
              radius: '50%',
              data: [
                { value: 1048, name: 'PGC' },
                { value: 735, name: 'UGC' }
              ],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)'
                }
              }
            }
          ]
        }
      },

漏斗图

配置

// 声量转化分析
      volConversion: {
        id: 'volConversion',
        option: {
          tooltip: {
            trigger: 'item',
            formatter: '{a} <br/>{b} : {c}%'
          },

          series: [
            {
              name: '占比',
              type: 'funnel',
              top: 60,
              width: '60%', // 大小
              left: '20%', // 跟上面奏够100,就是左右居中
              bottom: 60,
              min: 0,
              max: 100,
              minSize: '0%',
              maxSize: '100%',
              label: {
                position: 'right'
              },

              data: [
                { value: 60, name: '访问' },
                { value: 40, name: '咨询' },
                { value: 20, name: '订单' },
                { value: 80, name: '点击' }
              ]
            }
          ]
        }
      },

柱状图-堆积

配置

brandInfluence: {
        id: 'brandInfluence',
        option: {
          color: ['#32B0F2', '#006699', '#4cabce'],
          xAxis: {
            type: 'category',
            data: ['活动前', '活动中', '活动后']
          },
          yAxis: [
            {
              name: '声量',
              type: 'value'
            }
          ],
          legend: {
            bottom: '0%',
            data: ['安全的', '奥运的', '奥运的2', '奥运的3', '奥运的4']
          },
          series: [
            {
              name: '安全的',
              type: 'bar',
              barWidth: 40,
              stack: 'default', //随便设置一样字符串
              data: [100, 99, 88]
            },
            {
              name: '奥运的',
              type: 'bar',
              barWidth: 40,
              stack: 'default',//必须一样,
              data: [100, 99, 88]
            },
            {
              name: '奥运的2',
              type: 'bar',
              barWidth: 40,
              stack: 'default',
              data: [100, 99, 88]
            },
            {
              name: '奥运的3',
              type: 'bar',
              barWidth: 40,
              stack: 'default',
              data: [100, 99, 88]
            },
            {
              name: '奥运的4',
              type: 'bar',
              barWidth: 40,
              stack: 'default',
              data: [100, 99, 88]
            }
          ]
        }
      }

折线图

配置

// 活动声量变化趋势
      activityVolLine: {
        id: 'activityVolLine',
        option: {
          color: ['#32B0F2', '#006699', '#4cabce'],
          xAxis: {
            type: 'category',
            data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
          },
          yAxis: [
            {
              name: '声量',
              type: 'value'
            }
          ],
          legend: {
            bottom: '5%',
            data: ['Q3营销声量', 'Q3整合营销&品牌声量']
          },
          series: [
            {
              name: 'Q3营销声量',
              type: 'line',
              data: [120, 132, 101, 134, 90, 230, 210]
            },
            {
              name: 'Q3整合营销&品牌声量',
              type: 'line',
              data: [220, 182, 191, 234, 290, 330, 310]
            }
          ]
        }
      },

散点图,气泡图

配置

option = {
    xAxis: {
        name: '真粉数', //根据你的数据自动生成x轴值
         splitLine: { show: false } //显示格子线
    },
    yAxis: {
        name: '美誉度 (%)',//根据你的数据自动生成y轴值
        splitLine: { show: false } //显示格子线
    },
    series: [
        {
            name: '彭于晏',
            data: [
                ['1555',80] //二维数组,两个值够了
            ],
            type: 'scatter',
            symbolSize: 40, //控制气泡大小
            label: {
                normal: {
                    show: true,
                    formatter: function (param) {
                                return '彭于晏'
                            },
                    position: 'top'
                    
                }
            },
            itemStyle: {
                normal: {
                    shadowBlur: 10,
                    shadowColor: 'rgba(120, 36, 50, 0.5)',
                    shadowOffsetY: 5,
                    color: 'red'
                }
            },
            markLine: { //这个是在x,y轴任意位置画一个线出来
              silent: true,
              symbol: ["none", "arrow"],
              lineStyle: {
                color: "#5CC8FF",
                type: "dashed",
              },
              label: {
                position: "end",
                padding: [4, 4, 1, 4],
                color: "#5CC8FFFF",
                fontSize: 12,
                borderRadius: 4,
              },
              data: [
                {
                  xAxis: 600,
                },
              ],
          },
        },
        {
            name: '陈小春',
            data: [
                ['1355',60] //二维数组,两个值够了
            ],
            type: 'scatter',
            symbolSize: 10, //控制气泡大小
            label: {
                normal: {
                    show: true,
                    formatter: function (param) {
                                return '陈小春'
                            },
                    position: 'top'
                    
                }
            },
            itemStyle: {
                normal: {
                    shadowBlur: 10,
                    shadowColor: 'rgba(120, 36, 50, 0.5)',
                    shadowOffsetY: 5,
                    color: 'red'
                }
            }
        }
    ]
};

雷达图

配置

option = {
        radar: {
          indicator: [
            { name: '功效', max: 16000 },
            { name: '成分', max: 16000 },
            { name: '使用体验', max: 30000 },
            { name: '场景', max: 38000 },
            { name: '包装设计', max: 52000 },
            { name: '产品质地', max: 25000 }
          ],
          name:{
            formatter:'{value}',
            // 字体样式
            textStyle:{
                fontSize:14,
                color:'#666'
            }
          },
          //坐标轴线的设置
          axisLine: {
            show: true,
            lineStyle:{
                color:'#eee',
            },
          },
          splitLine:{
            show: true,
            lineStyle: {
              color: '#eee'
            }
          },
          //分割区域是否显示
          splitArea: {
              //分割区域的样式
              areaStyle:{
                  color: ['#fff', '#fff', '#fff', '#fff','#fff'],
                  opacity: 1,
              }
          },
   
        },
        tooltip:{
          trigger: 'item',
          backgroundColor:'rgba(0,0,0,0.5)',
          borderColor: 'rgba(0,0,0,0)',
          textStyle:{
            color: 'rgba(255,255,255,0.9)',
          },
          formatter: function (params) {
            return (
              '产品卖点类型分布' + 
              '<br/>' + 
              '功效 : ' + '1000' + 
              '<br/>' + 
              '功效 : ' + '1000' +
              '<br/>' + 
              '功效 : ' + '1000' +
              '<br/>' + 
              '功效 : ' + '1000' +
              '<br/>' + 
              '功效 : ' + '1000' +
              '<br/>' + 
              '功效 : ' + '1000'
            )
          }
        },
        series: [
          {
            name: 'Budget vs spending',
            type: 'radar',
            data: [
              {
                value: [4200, 13000, 20000, 35000, 50000, 18000],
                name: '产品卖点类型分布',
                itemStyle: { // 单个拐点标志的样式设置。
                  normal: {
                      borderColor: '#00BCBE',
                      borderWidth: 4,
                      color:'#00BCBE'
                  }
                },
                lineStyle: { // 单项线条样式。
                      normal: {
                          color: '#00BCBE',
                          opacity: 1 // 图形透明度
                      }
                  },
                areaStyle: {
                  opacity: 0.2
                }
              }
            ]
          },
        ]
      }

Echarts常用配置补充

太多太多太多了,没办法,用到就记一下
(1) 数据视图

toolbox: {
        show: true,
        feature: {
          dataView: {
              show: true,
              title: '名字', //这个是鼠标悬浮上去的文案
              readOnly: true,
              lang: [' ', '关闭 ',' '] //这个才是打开后的文案
          }
        }
    },

(2) 缩放
如果图表的数据太多什么的,全部都挤在一起显示,可以加这个

dataZoom:{
      show: true,
      height: 52, //高度
       bottom: '0', //位置,默认在底部
       start: 0, //不清楚
       end: 50, //不清楚
       fillerColor: 'red', //背景色
       handleColor: 'skyblue' //左右两边的小块
}

image.png

(3) 图例缩放
上面那个是表的缩放,如果是图例的,legend那个,可以加个

legend:{
  type: 'scroll'
}

image.png

(4) tooltip配置
鼠标悬浮上面的显示,formatter是自定义

 tooltip: {
              trigger: 'axis', //触发范围,一般写这个,不要写item,item的话触发面积太小,要点到线或者柱子才行
              axisPointer: {
                type: 'shadow' //这个是阴影,hover效果默认是一根线,加这个就变成阴影
              },
formatter: function (params) { //params这个有你需要显示的参数
                return 'lalala'
              }
            },

(5) x,y轴配置

yAxis: {
              name: '声量',
              type: 'value',
              axisLabel: {
              formatter: '{value} %' //如何显示
            },
            axisLine: { //y轴的连线
              show: true, //是否要连起来
              lineStyle: {
                color: 'red'
              }
            },
            axisTick: { //刻度,突出来的
              show: true
            },
            splitLine: { //间隔线
              lineStyle: {
                // 间隔色
                color: ['skyblue']
              }
            }
          }

(6) 如何在series加多点参数
需求是这样tooltip要显示一些额外的参数,如果能直接在format参数里面拿到就不用额外处理,注意
data里面的子项一定是对象才能这样做,如果是[100],是不行的

series: [
            {
              name: '总体',
              type: 'bar',
              data: [  //这个data一定是这个格式
                {
                  value: 100,
                  temtemtem: '自定义参数'
                }
              ]
            },
]
//上面你这样写了之后,你就可以
 tooltip: {
              trigger: 'item',
              formatter: function (params) {
                return '声量值: ' + params.data.temtemtem
              }
            },

(7) setoptions第二个参数问题
至少我这边是有遇过,当图表数据从少到多的时候,正常变化,但是,如果是从多变少,比如少一个数据,图表就不正常了,还是变少之前的样子
原因是在第二个参数,设置成true就可以,默认是false

this.myChart.setOption(copyconfig.option, true)

(8) 柱状图柱子间隙
这个应该比较少用到,不过还是记一下,情景我很难解释,大概是legend和series不能同时满足的时候,你必须清掉xais的data值让它可以正常显示,但是会挤在一起
barGap,每一个都要设置,而且是一样的值才生效

 xAxis: { 
            data: []
          },
legend: {
            bottom: '5%',
            data: ['总体','正面','负面']
          },
series: [
            {
              name: '总体',
              type: 'bar',
              
              barGap: '100%',
              data: [
                {
                  value: 100
                }
              ]
            },
           {
              name: '正面',
              type: 'bar',
              
              barGap: '100%',
              data: [
                {
                  value: 200
                }
              ]
            },
            {
              name: '负面',
              type: 'bar',
              
              barGap: '100%',
              data: [
                {
                  value: 300
                }
              ]
            },
          ]

(9) 双y轴
一般双y轴,右边的要有另外一组series数据,一般是柱状图配折线图我遇到的比较多
用yAxisIndex定位y轴
image.png

yAxis: [
          {
            name: "互动量",
            type: "value",
            axisLine: {
              lineStyle: {
                color: "#A9A9A9",
              },
            },
            splitLine: {
              lineStyle: {
                // 间隔色
                color: ["#F5F5F5"],
              },
            },
          },
          {
            name: "贡献度(%)",
            type: "value",
            min: 0,
            max: 100,
            axisLabel: {
              formatter: "{value} %",
            },
            axisLine: {
              lineStyle: {
                color: "#A9A9A9",
              },
            },
            splitLine: {
              show: false,
            },
          },
        ],
series: [
          {
            name: "互动量",
            type: "bar",
            barWidth: 40,
            data: [],
          },
          {
            name: "贡献度",
            data: [],
            type: "line",
            yAxisIndex: 1, //用这个定位
          },
        ],

(10) 饼图为0隐藏显示
这种做法一般有两种,一种就是常见的过滤数据,我下面这种是不过滤的,通过控制label和labelLine
但是经过测试发现,label的show关了后,labelLine也会关,不过保险起见,两个都写

series: [
    {
      name: 'Access From',
      type: 'pie',
      radius: '50%',
      data: [
        { 
          value: 0, //不用过滤,0就0
          name: 'Search Engine',
          label:{
            show: 0? true:false, //通过控制show
            color: 'skyblue'
          },
          labelLine:{
            show: 0? true:false, //同上
            length: 30,
            lineStyle:{
              color: 'red'
            }
          }
          
        }]
}],

(11) 图表的resize设置
一般窗口变化的时候,你按F12拖动窗口,这个时候如果图表没有设置这个resize,图表会飘
window监听resize事件,最后一个参数表示要捕获true还是冒泡false
我本来以为设置同个resize会覆盖掉前面那个,其实不会

mounted(){
window.addEventListener('resize', this._resizeEventHandler, false)
}
methods:{
_resizeEventHandler() {
      // 这里加多个判断,容错
      if (this.myChart) {
        // 这个节流,我是比较倾向用防抖,这个是我拷贝其他项目的做法
        // 核心,this.myChart.resize(),实例自带的方法,调用一下就行
        this.$common.throttle(this.myChart.resize(), 300)
      }
    },
}
beforeDestroy() {
    if (this.myChart) {
      this.myChart.dispose() // 图表实例销毁
    }
    window.removeEventListener('resize', this._resizeEventHandler, false) //事件移除
  }

(12) 图表离y轴太近,调远一点
image.png
就是上面这种,然后解决设置xAxis
image.png

(13) echarts添加点击事件,触发多次
给一个词云图加点击事件联动,发现点一次触发几次,用下面这个

let myBarChart = this.$echarts.init(this.$refs.majorHidTroBar)
       myBarChart.setOption(this.barOption)
       myBarChart.off('click') //图表渲染前销毁点击事件
       myBarChart.on("click", function(value){
           this.leftTypeName = value.name
          _this.getqueryImportantI(this.leftTypeName)
        })

(14) 修改图那个拐点的样式

let option = {
        tooltip : {
          trigger: 'axis',
          triggerOn:'click',
          formatter:function(params){
            let text = `${utils.charAdjust(params[0].name, 6)}\n${params[0].marker}${params[0].seriesName}   ${params[0].data}\n${params[1].marker}${params[1].seriesName}   ${params[1].data}%`
            return text
          }
        },
        grid: {
          left: '5%',
          right: '5%',
          top: '3%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
            data: xlist,
            axisTick: {
              show: false
            },
            axisLine: {
              lineStyle: {
                color: '#EEEEEE'
              }
            },
            axisLabel:{
              color:'#4E5969',
              interval: 0,
              formatter: function (params) {
                return utils.charAdjust(params, 4, false)
              }
            }
        },
        yAxis: [
          {
            name: "",
            type: "value",
           axisLabel:{
              color:'#4E5969',
              formatter: function(value){
                if (value >= 100000) {
                  return value / 10000 + "w";
                }else if(value >= 1000){
                  return value / 1000 + "k";
                }else{
                  return value
                }
              }
            },
            splitLine: {
              lineStyle: {
                color:['#EEEEEE']
              }
            },
          },
          {
            name: "",
            type: "value",
            max: maxValue + 10,
            axisLabel: {
               color:'#4E5969',
              formatter: "{value} %",
            },
            splitLine: {
              show: false,
            }
          },
        ],
        series: [
          {
            name: this.data.filter.quota,
            type: "bar",
            barWidth: 24,
            data: vollist,
            itemStyle: {
              normal: {
                color:{
                  customConfig:{
                    type: 'Gradient',
                    color:['#165DFF','#3C8BFF']
                  }
                },
                barBorderRadius: [4,4,0,0]
              }
            }
          },
          {
            name: "贡献率",
            data: ratelist,
            type: "line",
            yAxisIndex: 1, //用这个定位
            lineStyle: {
              color: '#86DF6C'
            },
            symbol: 'circle',
            symbolSize:10,//拐点大小
            itemStyle: {
              normal: {
                color:'#86DF6C',//拐点颜色
                borderColor:'#fff',//拐点边框颜色
                borderWidth:2,//拐点边框大小
                label: {
                  show: true,
                  formatter: "{c}%",
                }
              }
            }
          },
        ]
      }

还有一点,如果最大值刚好等于y轴最大值,有时候会遮住label,需要给y轴设置一个max值
这里还有一个关于柱状图渐变色的配置

item.itemStyle.normal.color = new echarts.graphic.LinearGradient(0, 0, 0, 1,[{
              offset: 0,
              color: gradientConfig.color[0]
            },
            {
              offset: 1,
              color: gradientConfig.color[1]
            }])

这里还还有一个,就是tooltip太长,需要处理换行的时候,这个方法

// 文字换行
const charAdjust = function (charVal,num,wrap = true) {
  var newParamsName = ""; // 最终拼接成的字符串
  var paramsNameNumber = charVal.length; // 实际标签的个数
  var provideNumber = num; // 每行能显示的字的个数
  var rowNumber = Math.ceil(paramsNameNumber / provideNumber); // 换行的话,需要显示几行,向上取整

  if(!wrap){
    newParamsName = charVal.substr(0,provideNumber) + (rowNumber > 1? '...':'')
    return newParamsName
  }
  // 条件等同于rowNumber>1
  if (paramsNameNumber > provideNumber) {
      /** 循环每一行,p表示行 */
      for (var p = 0; p < rowNumber; p++) {
          var tempStr = ""; // 表示每一次截取的字符串
          var start = p * provideNumber; // 开始截取的位置
          var end = start + provideNumber; // 结束截取的位置
          // 此处特殊处理最后一行的索引值
          if (p == rowNumber - 1) {
              // 最后一次不换行
              tempStr = charVal.substring(start, paramsNameNumber);
          } else {
              // 每一次拼接字符串并换行
              tempStr = charVal.substring(start, end) + "\n";
          }
          newParamsName += tempStr; // 最终拼成的字符串
      }

  } else {
      // 将旧标签的值赋给新标签
      newParamsName = charVal;
  }
  //将最终的字符串返回
  return newParamsName
}

(15) 折线图最大值标点
折线图最大值标出来,其他隐藏,也可以全部都标出来,最大那个就亮一点,要在data里面设

let option = {
        tooltip : {
          trigger: 'axis',
          triggerOn:'mousemove',
          formatter:function(params){
            that.getViewpointData(params[0].name)
            let text = `${params[0].name}\n${params[0].marker}${params[0].seriesName}   ${params[0].data}`
            return text
          }
        },
        grid:{
          left:'5%',
          right:'5%',
          top:'3%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: xlist,
          axisTick: {
            show: false
          },
          axisLine: {
            lineStyle: {
              color: '#EEEEEE'
            }
          },
          axisLabel:{
            color:'#4E5969'
          }
        },
        yAxis: [
          {
            name: '',
            type: 'value',
            splitLine: {
              lineStyle: {
                color:['#EEEEEE']
              }
            },
            axisLabel:{
              color:'#4E5969',
              formatter: function(value){
                if (value >= 100000) {
                  return value / 10000 + "w";
                }else if(value >= 1000){
                  return value / 1000 + "k";
                }else{
                  return value
                }
              }
            }
          }
        ],
        series: [
          {
            name: this.data.filter.quota,
            type: 'line',
            data: ylist,
            lineStyle: {
              color: '#3C8BFF'
            },
            
            // symbol:'circle',//拐点设置为实心
            symbol:function(value, params){
              if(+value === maxValue){
                return 'circle';
              }
              return 'none';
            },
            symbolSize:16,//拐点大小
            showAllSymbol: true,
            itemStyle: {
              normal: {
                color:'rgb(255,148,50)',//拐点颜色
                borderColor:'rgba(255,148,50,0.4)',//拐点边框颜色
                borderWidth:9,//拐点边框大小
              }
            },
          }
        ]
      }

element篇

表格固定第一行

官方的例子是固定最后一行,如何把这一行放到第一行显示呢

.hotList /deep/ .el-table {
  display: flex;
  flex-direction: column;
}
.hotList /deep/ .el-table__body-wrapper {
  order: 1;
}

表格设置自适应

设置width为auto,你也可以给某一列,比如第一列设死宽度,

<el-table :data="top10Article.tableData" border>
          <el-table-column
            align="center"
            show-overflow-tooltip
            label-class-name="el-th"
            v-for="item in top10Article.colum"
            :key="item.prop"
            :prop="item.prop"
            :width="item.width" //这个配置
            :label="item.label"
          />
        </el-table>


var TableWidth = "auto";
              if (item === "帖子") {
                TableWidth = "740";
              }
              colum.push({
                label: item,
                width: TableWidth,
                prop: item,
              });

el-date-picker设置选择范围

比如我只能选1个月,30天这种限制

<el-date-picker
            v-model="value2"
            type="datetimerange"
            size="small"
            :picker-options="pickerOptions"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            align="right">
        </el-date-picker>


choiceDate0: '',
            pickerOptions: {
                // 设置不能选择的日期
                onPick: ({ maxDate, minDate }) => {
                    this.choiceDate0 = minDate.getTime();
                    if (maxDate) {
                        this.choiceDate0 = '';
                    }
                },
                disabledDate:
                    (time) => {
                        let choiceDateTime = new Date(this.choiceDate0).getTime();
                        const minTime = new Date(choiceDateTime).setMonth(new Date(choiceDateTime).getMonth() - 1);
                        const maxTime = new Date(choiceDateTime).setMonth(new Date(choiceDateTime).getMonth() + 1);
                        const min = minTime;
                        const newDate = new Date(new Date().toLocaleDateString()).getTime() + 24 * 60 * 60 * 1000 - 1;
                        const max = newDate < maxTime ? newDate : maxTime;
//如果已经选中一个日期 则 返回 该日期前后一个月时间可选
                        if (this.choiceDate0) {
                            return time.getTime() < min || time.getTime() > max;
                        }
//若一个日期也没选中 则 返回 当前日期以前日期可选
                        return time.getTime() > newDate;
                    }
            },

el-form表单,一行多列

有时候需要表单的某两列并排
image.png
使用span,一共24格,自己合理设置
如果需要3列,就设置8
image.png

el-select设置绑定值为对象

image.png
注意value绑对象,然后select那里value-key也要绑定对象的某个值,不过我忘了是要跟label一样还是怎么,反正是需要,不然显示有问题

el-popover和el-select共存

点击彼此的下拉,另外一个不会收起来,被我遇到了,百度解决的方法是这样
image.png
image.png
引入element自带方法,设置指令,然后给popover加上

el-menu菜单换成img

demo用的是icon,需求是要用img的
image.png
然后src绑定的你就判断下显示默认还是激活的
image.png

el-select渲染太多太卡了

行,被我遇到了,大概1000多个dom,直接渲染出来会卡死,所以需要采用加载一部分,滑动再加载下一部分。
当然,原理是没有变的,如果用户一直滑一直滑,把1000多个dom加出来一样会卡,但是用户通常是lazy的,不会有这种情况,最多就搜索。

这里的解决方案要两步
第一步:要用懒加载,不能全部渲染,具体是这样
先定义个lazyLoadOptions,这个变量是什么呢

<yi-option
                  v-for="item in lazyLoadOptions"
                  :key="item.id"
                  :label="item.objectName"
                  :value="item" />

能看到,是computed一个方法,然后有3个变量

monitorNameOptions: any = [] // 所有数据
searchTotal: any = [] // options展示的数据
rangeNumber = rangeNumber


get lazyLoadOptions() {
    return this.searchTotal.slice(0, this.rangeNumber)
  }

很明显看的出,searchTotal是总数,rangeNumber是翻页,所以

<yi-select
                v-model="ruleForm.monitorName"
                v-el-select-loadmore:rangeNumber="loadMore(rangeNumber)"

需要有一个指令,计算options滚动底部加载,这段是抄网上的,好使

@Component({
  directives: {
    'el-select-loadmore': {
      bind(el, binding, vnode) {
        // 获取element-ui定义好的scroll盒子
        // 这里实现懒加载
        SELECTWRAP_DOM = el.querySelector('.ob-select .yi-select-dropdown__wrap')
        SELECTWRAP_DOM.addEventListener('scroll', () => {
          /**

              * scrollHeight 获取元素内容高度(只读)

              * scrollTop 获取或者设置元素的偏移值,常用于计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.

              * clientHeight 读取元素的可见高度(只读)

              * 如果元素滚动到底, 下面等式返回true, 没有则返回false:

              * ele.scrollHeight - ele.scrollTop === ele.clientHeight;

              */
          const condition = SELECTWRAP_DOM.scrollHeight - SELECTWRAP_DOM.scrollTop - SELECTWRAP_DOM.clientHeight
          if (condition <= 0 && vnode.context.searchTotal.length > rangeNumber) {
            console.log('看看', binding.value)
            binding.value()
          }
        })
      }
    }
  }
})

// 这是loadmore方法
loadMore() {
    // 每次滚动到底部可以新增条数
    // return () => this.rangeNumber += 10
    return () => {
      this.rangeNumber = this.rangeNumber + rangeNumberAdd
      return this.rangeNumber
    }
  }

到这里解决了懒加载,还有一个搜索功能

 <yi-select
                v-model="ruleForm.monitorName"
                v-el-select-loadmore:rangeNumber="loadMore(rangeNumber)"
                value-key="objectName"
                multiple
                filterable
                :filter-method="filterCheckPerOptions"

// 方法
filterCheckPerOptions(query = '') {
    SELECTWRAP_DOM.scrollTop = 0
    this.rangeNumber = rangeNumber
    this.searchTotal = this.monitorNameOptions.filter(item => {
      return item.objectName.includes(query)
    })
    console.log('搜索结果集', this.searchTotal)
  }

简单总结下,
1 需要2个数组,一个数组装所有的数据,一个数组装从所有的数据里面搜索出来的数据
2 需要1个显示数组,就是coputed那个方法,从搜索数组里面切割数据,给eloptions用的
3 需要一个指令,计算options是否滚动到底部,然后加载

el-table序号的自增

有序号没自增功能,我也觉得很奇怪
image.png
image.png

Vue篇

vue指令

我写了一个简单的图片替换的,就是如果src空或者加载失败,就替换默认图
因为指令只能传一个参数,其他参数我不知道怎么传,所以就用了data-这样传

使用
<img :src="item.pic" v-imgload="item.pic" data-imgloadfix='1'>

代码
let imgError = require('@/images/emptyImage.png')
// 部分图片替换需要一些额外的操作
function imgFixHandle(type,el){
  if(type == 1){
    el.style = 'width:60px;';
    el.parentNode.style = 'background:#eee;text-align:center;'
  }
}
export default {
  bind(el, binding) {
    let imgUrl = binding.value
    let imgFix = el.getAttribute('data-imgloadfix') 
    // let imgUrl = 'https://cxp-test.yili.com/resources/images/logo-default@2x.png'
    if(!imgUrl){
      el.setAttribute('src',imgError)

      // 额外操作
      setTimeout(() => {
        imgFixHandle(imgFix,el)
      },0)

      return 
    }
    let img = new Image()
    img.onload = function(){
      el.setAttribute('src',imgUrl)
      img = null
    }
    img.onerror = function(){
      el.setAttribute('src',imgError)

      // 额外操作
      imgFixHandle(imgFix,el)

      img = null
    }
    img.src = imgUrl
  }
}

我就用了一个钩子,其他没用到,只能百度先写下来
钩子+参数

bind(el,binding,vnode,oldVnode):             首次绑定执行一次,负责初始化
inserted:          被绑定元素插入父节点时候调用,仅保证父节点存在,不一定已被插入文档
update:            组件VNode更新时,可能在其子VNode更新前
componentUpdated:  组件VNode及其子VNode都更新后调用
unbind:            解绑时调用

el:       绑定的元素
binding:   绑定对象,包含参数值,value和oldValue
vnode:     当前虚拟节点
oldVnode:  上一个虚拟节点,仅在update和componentUpdate钩子可用

区别
bind 和 inserted

共同点: dom插入都会调用,bind在inserted之前

不同点:
    bind 时父节点为 null
    inserted 时父节点存在。
    bind是在dom树绘制前调用,inserted在dom树绘制后调用

bind: function (el) {
    console.log(el.parentNode)  // null
    console.log('bind')
},
inserted: function (el) {
    console.log(el.parentNode)  // <div class="directive-box">...</div>
    console.log('inserted')
}

update 和 componentUpdated
跟vue组件的up,bup差不多

update(el, binding,vnode,oldVnode){
    console.log(el.innerHTML);       // <div>!</div>
}

componentUpdated(el, binding,vnode,oldVnode){
    console.log(el.innerHTML);       // //<div>!!</div>
}

还有还有,如何在指令里面拿到this,要借用vnode

bind(el, binding, vnode) {
    vnode.context.searchTotal //vnode.context相当于this
}

proxytable的深刻认识

这个到我的文章,跨域那个看

v-modal用法

这个是语法糖,有两部分,一般可以这样联想,v-model是双向绑定,一般演示的时候都是用input的,input的value值就是当前值,然后输入的时候,就是监听了@input方法,所以

<input type="text" v-model="text">
等同于
<input type="text" :value="text" @input="function(e){text = e.value}">

很多第三方UI库用v-modal都有用来作显示隐藏,就是外层传一个boolean,组件里面emit出来后,父组件不用接受,不用改,让组件自己改这个值,大概这样

// 传一个temFlag
<PartOne :filterParam="filterParam" v-model="temFlag"/>

// 组件
<div v-if="value">测试v-</div>
// 抛出input事件,也是固定死的
<div @click="() => {this.$emit('input',false)}">关闭、打开</div>
<div @click="() => {this.$emit('input',true)}">关闭、打开</div>
    
props: {
    value: { // 一定叫这个名字,固定死的
      type: Boolean,
      default: true
    },
    filterParam: {
      type: Object,
      default: () => {}
    }
  },

vue打包的assetsPublicPath属性

这个属性是针对资源,也就是css,img,js这些资源的引入路径写法,
并且要跟,这个dist文件夹,在服务器目录的层级,保持一致,
比如dist文件扔在根目录,访问xxx.com/index.html
那么assetsPublicPath是 / 或者 ./ 都行 ,
如果放了几层目录,xxx.com/ra/ba/cd/index.html,则这个属性就要变了,要一样
assetsPublicPath: '/ra/ba/cd/'

vue数组用索引修改字符串,数组不生效

这个以前真没注意,数组用索引改是不生效的,视图不会更新,看了一下好像是数组无法用object.defineproperty劫持set,get方法

tem = [1,2,3]
this.tem[1] = 5 //视图不会更新

vue组件style的关系

style加了scoped属性,会在所有dom上面加标识,比如,

data-v-101b773d这个就是标识
<div data-v-101b773d="">数据哦</div>

然后所有的样式也都是默认加这个

虽然写是这样写,
.tem {
  color:skyblue
}
但是编译后,浏览器看到的是这样
.tem[data-v-101b773d] {
  color:skyblue
}

这就是为什么加了这个属性后,不会影响到这个组件里面套入的其他组件,因为其他组件的dom没有data-v-101b773d这个属性,就算其他组件也加了scoped,它的data-v-xxxxxx也是不同的

然后,继续说一下,组件的style是怎么加载的
每个组件的style都是按需下载,并且是会插入html的header标签,不过有点不同,我发现dev的时候,是style形式插入,而build后,是link标签下载

我为什么说这个呢

因为我发现如果某个组件的style不加scoped,然后加载的时候,header标签插入,然后你其他组件的样式,就被它影响了!!

怎么避免
一般来说,不加scoped是为了让整个组件的样式都影响内部组件,或者是改UI库,比如elm的,

  1. 要么你就在类前面加个父类

    .fu .target {}
  2. 要么就不要去掉scoped,用/deep/,这个/deep/编译后的原理其实是吧data-v放在最开始的类,后面的不会有

    .fu /deep/ .target {}
    
    // 编译后
    .fu[daa-v-xxxxx] .target {}

vue-cli3打包去掉consol

这个配置不用额外install依赖,本身你用脚手架3构建的vue项目已经内置了
把这个插到vue.config.js就行了

// vue-cli3内置webpack的TerserPlugin,可以用来处理js
  chainWebpack(config) {
    // 非开发环境,去掉console
    config.when(process.env.NODE_ENV !== 'development', config => {
      config.optimization.minimizer('terser').tap(options => {
        options[0].terserOptions.compress.drop_console = true
        return options
      })
    })
  },

vue-cli2打包去掉consol

这个版本还是有很多项目在用的,百度了一下,也是内置好了,这样配
在build文件夹的webpack.prod.conf的配置里面,找到UglifyJsPlugin
image.png

动态组件全局提示如何传参

我们常用的UI组件,element的全局loading,提示,都是用动态组件写的,如何写的呢?
写说下原理,其实就是用js创建dom,然后插入到html上,完了,

let dom = document.createElement('div')
        dom.className = 'tips-cover tips-cover-auth'
        let dom2 = document.createElement('div')
        dom2.className = 'tips-box tips-box_fail'
        let dom3 = document.createElement('i')
        dom3.className = 'tips-box__icon'
        let dom4 = document.createElement('span')
        dom4.className = 'tips-box__content'
        dom4.innerHTML = '此账号无权限访问,请联系管理员'
        dom.appendChild(dom2)
        dom2.appendChild(dom3)
        dom2.appendChild(dom4)
        document.documentElement.appendChild(dom)

但是这样写肯定有各种不方便,所以下面说的是vue的写法,elemnt的UI也是用vue写的,服务于vue
首先,正常写个组件
image.png
然后搞一个js文件,有几个步骤

// 第一步,引入
import AuthTip from './AuthTip.vue'
// 第二步,extend构建
const AuthTipConstructor = Vue.extend(AuthTip)
// 第三步,实例化
let instance = new AuthTipConstructor({
      propsData: { //注意这里是传参的写法,就是pros那些配置
        type: config.type,
        text: config.text
      }
    })
// 第四步,挂载(理解应该是js结构的dom对象转换成真正的dom)
instance.$mount()
// 第五步,插入到html
document.body.appendChild(instance.$el)

上面就是创建整个组件的过程,然后要销毁的话就这样

document.body.removeChild(instance.$el)

webpack打包,锁版本,静态资源抽离出来,html引入,不参与打包

首先说下打包

我们知道,打包的时候,webpack是爬你js文件,然后看到你有import其他文件,再继续爬,然后通过你的loader,处理文件,最后全部塞进去一个js文件,或者几个js文件里
然后这里要分两种情况,一种是资源放cdn的,一种是还在你的体积包
这次项目中,我把echarts抽了出来,就是网上找一个min.js的版本,然后,在html引入
image.png
然后,可以看到,我把文件丢到了public文件夹,这个文件夹作用就是vuecli2的那个static文件夹,这个文件夹下面所有文件不参与打包,原封不动搬到打包之后的文件夹里,所以不用担心引入路径出问题
image.png

这里说一下,正规的话,像这种资源应该要用cdn的,减少包体积

然后,html引入的js,都是会默认挂载到window上面?(至少echars是的,其他js文件不太清楚),所以在代码里面,我们可以这样

Vue.prototype.$echarts = window.echarts

还没完,因为代码里面可能还有这种import的代码,所以你还需要在webpakc这样配置

// 某些第三方库,如果使用了html引入js的话,那webpack就不用打包这个文件进入js
  // 一般这种都是用cdn引入,可以减少体积,但是现在这个echarts我只是抽出来而已,放到public静态文件夹
// 个人分析,这里怎么写,取决于你平时import是怎么写,比如echarts就是这样,import echarts from 'echarts'
  configureWebpack: {
    externals: {
      echarts: 'echarts'
    }
  }

配了上面那个后,就算代码里面你写 import ,也不会参与打包

锁版本

我们知道package.json第一次安装依赖后,会生成一份package-lock.json,这个lock文件的记录是全的,就是a依赖包依赖b包,b包也依赖c包,记得清清楚楚,到时候npm i 的时候,就会走这个文件,不读packagejson;

但是呢,如果你packagejson更新了,比如eacharts从'3.0.0',你写成了'4.0.0',然后执行npm i ,这个时候呢,就会读pj,然后再更新plj;

plj就是锁版本,还有cnpm貌似是不会读这个文件,需要额外的设置?

最后这个也是刚知道的,就是pj都统一把依赖包那个箭头搞掉,带^或者~,都会是去更新版本,只有写死了才不会

 "axios": "^0.21.1", // no no no
    "core-js": "3.15.2", //yes yes yes

最后说一个疑问,pj里面的依赖,只记录的第一层,而plj是记录了所有层,但是,理论上,我pj写死了版本,只要不是这个包的作者搞事把这个a包里面的依赖改了版本,那我理论上只用pj也是可以的?。

好像是不行,刚才我稍微看了下,比如autoprefixer这个依赖,它里面依赖别人的都是带^的,卧槽,那就只能是packagelock最稳妥

对了,生成plj之后,以后install都是会默认读这个,除非是你pj和plj的信息不对称,比如你更新了pj的版本,它才读这份,至于怎么判断的,不知道

slot插槽

用的很少,最近看到代码里面有,记录一下

// 父组件
<child>
  <div slot="name">你好</div>
// 这样写也行
  <template slot="temtitle222">
          <div>我是标题2222</div>
        </template>
</child>

// 子组件
<div>
    <slot name="name"></slot>
    <slot name="temtitle222"></slot>
</div>

最近看到一个新的,slot-scope,作用就是,父组件可以用上,你在slot标签上面传过来的参数,

// 子组件
<div>
    <slot name="name" title1="1" number="2"></slot>
    <slot name="temtitle222"></slot>
</div>

// 父组件
<child>
  <div slot="name" slot-scope="item">你好{{item}}</div> // { "title1": "1", "number": "2" }
</child>

visible.sync原理,跟v-modal区别

准确来说,应该是.sync,visible这个只是一个参数名,你设置show都行,然后这个如果加了这个sync后,跟v-model很像,都是语法糖

// 父组件
<child :show="flag"></child>
data: {
flag: true
}

// 子组件
<div v-if="flag"></div>
props:{
flag:{
 type: Boolean,
 default: false
}
}

正常来说,子组件内部要改,要emit抛出来,父组件接收,它来改,如果直接改,就跟外面的不同步,所以当你加了.sync之后

// 父组件
<child :show.sync="flag"></child>

// 子组件
<div @click="_ => {this.$emit('update:show',false)}">按钮触发</div>

加了sync后,你emit的时候,固定写updata:变量名,父组件就不用接收了,其实也是语法糖而已

// 父组件
<child :show.sync="flag" @update:show="val => flag = val"></child>

vue关于驼峰和带横线的写法

一直有点疑惑,就是组件注册了之后,为什么说建议用my-father,而不是myFather这种
网上搜了下,自己总结了下,两个原因把,第一点,
html不区分大小,怎么理解?

<html>
 <div ID="1" DaTa="2"></div>
</html>

这个,最终给浏览器解析的时候,最终都是变成 id,data这种,这个说完了,结合下第二点
vue的编译

<template>
  <div class="g-container">
    <HeadNav/>
    <div style="height: 100%;padding-top:50px;" >
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
import HeadNav from '@/components/HeadNav';
export default {
  name: 'navpage',
  props:{
    'myName':{
      type: String,
      default: ''
    }
  },
  components: {   
    HeadNav
  }
}
</script>

他这个模板,在最终转成真正的dom的时候,有一系列处理,其中肯定是有把这个HeadNav,转成head-nav这种,另外在components注册组件的时候,也肯定有转
'head-nav':HeadNav的逻辑存在,所以我们在template的时候,才能既可以用<HeadNav/>,也可以用<head-nav/>,还有props也是一样的道理,所以你在组件传参数的时候也可以用驼峰,也可以用带横线

总结
除了html给浏览器解析的时候,会全部变成小写,这个是强制的,剩下的那些规范都是vue自己定的,当然如果我们按照他这个规范开发,举个例子,在template转dom的时候,你按照规范写,就会节省了他转成带横线的步骤,四舍五入就是性能优化???

vue路由

重复跳转同个路由报错

这个报错是控制台报红,不影响使用,不知道为什么有些项目会,有些不会报,如果有,就全局的catch掉那个错误,控制就不报红

const router = new Router({ routes: ROUTES });

let temp = Router.prototype.push
Router.prototype.push = function(location){
  return temp.call(this,location).catch(err => {})
}

又找了一个写法,应该准一点吧

const originalPush = VueRouter.prototype.push
 
VueRouter.prototype.push = function push(location, onResolve, onReject) {
 
  if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
 
  return originalPush.call(this, location).catch(err => err);
 

注意因为哪个报错,就把那个push,replace换成对应的

关于node-sass,sass-loader经常安装报错问题

这两个东西太坑了,你拉项目的时候,你要注意看pj那,这两个版本是多少,然后用对应的node版本就安装依赖,不然跑不起来
最近项目接触了几个,我都是用node10安装的,版本分别是node-sass@4.14.1,sass-loader@8.0.2,这样下来没问题
然后尝试用vuecli3,很新的node版本 16.3.0,初始化项目的压根跑不起来,报错了
然后我的做法,把这个两个uninstall删掉,
安装上面说的两个版本,运行报错,接着,我执行了

npm rebuild node-sass

然后就可以了,这句指令好像是更新node-sass里面的依赖,注意是里面的,本身的版本不变,反正执行了就可以了

v-html插入文本换行符却不换行

直接放进去发现不行,发现得加点下面的手段才生效
加样式

<p v-html="text" style="white-space:pre-wrap"></p>

pre标签包裹(没试过

<pre><p v-html="text"></p></pre>

正则替换(没试过

<p v-html="text.replace(/\n/g,'<br/>')"></p>

代理篇

whsitle的使用

安装我就不说了,可以看这个 https://imweb.io/topic/596480af33d7f9a94951744c
我说下目前在尝试使用的
控制台w2把whistle开了之后,相当于开了一个本地服务器,127.0.0.1:8899
浏览器打进去,可以开始写规则
image.png

看到我蓝字那部分,我是替换vue项目,就是访问线上环境,然后指向本地项目看效果,然后我发现,要具体替换某个js才行,直接一个域名替换并不可以
而且用的是$,代表精准匹配,更多规则看http://wproxy.org/whistle/rules/proxy.html

https://abc.cde.com 127.0.0.1:8080 不行
https://abc.cde.com/app.js 127.0.0.1:8080/app.js 才可以

然后说下怎么看请求
image.png
这个填你要看的接口请求的域名,填一个回车另一个就行,你不设置的话一堆请求

常用操作

注意,域名只能填域名,或者ip带端口那种,用locahost这种,127.0.0.0这种没用
还有一点要说,就是匹配替换的时候,只请求了一次,就是准备发的时候,规则命中,然后走规则后面那个真正的请求地址,network那里的表现是一次,并没有请求两次,然后拿后面那个替换前面的
1 请求太多,怎么过滤
两种,一种是直接在network下面,有个快速过滤
(1) 如果url,host过滤的话,直接写,貌似是模糊查找,比如

.js  //看js文件
.png //看图片
/\.png/i  // 正则也行
/radar/    // 带关键字,这些查找host也会查,但是不知道哪个优先

(2) 如果是请求方法的话,带m,状态码的话,带r

m:post  // 要加个m关键字
r:403   // 状态

上面那个都是请求后过滤的,还有这种有个缺陷只能有一个规则,不能有多个,比较蛋疼,下面这种就可以多种,但是要重新抓包才会生效
(1) 在settings那里,就是我上面那个截图,一个exclude,不要,一个include,要的

Exclude Filter
/\.js/i
/\.css/i
/\.png/i

Include Filter
hm.baidu.com

这个应该是常用的,就是不要js,css,图片,只显示接口,同时还要hml.baidu.com这个域名的,这个域名你可以改成你项目那个请求地址的域名,注意是请求地址,不是页面url

2 匹配替换
这个跟我上面那个精准匹配说的一样,场景一般就是用你访问线上的环境,然后里面的请求的js或者其他资源变成你本地或者其他,取决你怎么设置,但是又很麻烦,因为要精准匹配,然后替换某个js,对于vue项目来说,很奇怪为什么网上的教程直接替换域名是可以的,可能是他们项目结构比较简单把,我这个各种iframe乱的要死
(1) 精准匹配

$https://abc-def.com/temtemtem/tryLogout https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.min.js

(2) 正则匹配
请求里边有/abc/def,关键字,这里又有问题了,暂时没发现怎么设置多个条件,比如我想再加个要get请求的,难受

/\/abc\/def/i https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.min.js

git篇章

强制推送

我也是最近才知道的,原来强推会把远程别人的改动干掉,并且不会在gitlog那里留下证据,简直了,
要用命令行git reglog才能看,但是我自己实验一波,发现看不到,不知道是不会看还是看漏眼了
这个命令有个使用场景,就是你把本地分支干掉,但是你的提交还没推远程,或者还没合并到某个分支

要留后备分支

最近终于体验了一次把代码错误发版到线上,接口没上,导致报错,我没有采用什么回滚,reset之类,尤其是回滚,如果某个分支通过合并你的分支发版,然后它回滚了这个合并操作的话,那它再也没法再合并你的分支
我是用一个没合代码前的分支发版,所以我觉得release分支要维护好,当你用某个分支a发版了之后,错了,用relsase分支直接发版还原,后面修改好了,再用a分支发版就好

仓库太大,拉不下来

有一个2g的,用sourcetree拉下拉报错了不行,后来要设置postbuffer

要么这个
git config http.postBuffer 524288000
或者这个
git config --global http.postBuffer 524288000

忘了当时是设置哪一个了,但是这样设置了之后,确实拉下来,但是后面不知道为什么,push的时候,出现奇奇怪怪的问题,push失败,然后把它删了
image.png

合并了别人的分支,如何不要

如何重置别人的分支合并,合了之后,选上一个记录,重置,选强,提示远程有更新,不要拉,直接强制推送

css篇章

link标签

注意这个标签的是href插入资源,跟script标签的src不同
link标签的那个ref属性,原来有来头,比如
ref=icon,href跟的是ico,就是页签的那个小图标,这个ico格式的可以找工具,通过图片转换
ref=stylesheet,就是常用的css
还有一个,ref=preload,我在最新vue项目看到的,打包后dist有这个,百度了下是资源预加载,
加了这个还要加as=style,要指明资源类型,as=script,
ref=preload,as=style,href=xxxx.css
还能加载js,这个是第一次见,不知道跟script标签的有啥区别,没去研究
ref=preload,as=script,href=xxxx.js

空心倒三角

这种之前没写过
image.png
原理是写两个div,只漏出上边的三角,两个重叠,其中一个top位移一点,设白色盖住它

.nav-arrow {
  position: relative;
  padding-right: 25px;
}
.nav-arrow::before {
  content: "";
  position: absolute;
  top: 22px;
  right: 8px;
  z-index: 1;
  width: 0;
  height: 0;
  border: 6px transparent solid;
  border-top-color: #7a7a7a;
}
.nav-arrow::after {
  content: "";
  position: absolute;
  top: 19px;
  right: 8px;
  z-index: 2;
  width: 0;
  height: 0;
  border: 6px transparent solid;
  border-top-color: #fff;
}

补充篇

a标签的下载文件

<a href="./static/cpgnkw.doc" style="cursor:pointer;color:blue;" download="产品概念口味包装测试标准化方法的补充说明_F.doc">《“产品概念口味包装测试标准化方法”的补充说明_F》</a>

下载的href路径一定要英语,不能中文,download写文件名加后缀

dist缓存,hash值更新

(1)F12那里有个DOC,可以看到当前页面是加载了哪个html,然后看响应,看它资源hash值判断发版有没有生效;

(2)而且通过这个我们可以发现,我们本地run dev,其实做的事情,跟run build一样,也是打包了,只是这个打包没有放出来,我们看不到而已;

(3)ctrl+f5 ,强制清缓存,清不了iframe的,勾disable cache加c+f5,才能清除

(4)打包关掉sourcemap,体积会小很多

iframe

比如A域名嵌套iframe,B域名
(1)如果B域名跟A同域,也就是

A.html
<iframe src="/xxx/xxx/#/"> //这个是B地址

如果是这样写,B的域名会自动拼接A域名的,也就是A登陆后的cookie,在B的里面请求的时候,也会自动携带上,在请求头里面,header.cookie: 'xxxxx'
因为是同域名的请求;

(2) 如果不同域名
不同域只能cookie拿,然后手动拼到请求头里面,作为token

// 这个是拿全部的,所有cookie的挤在一起,要自己切割拼接
document.cookie

// 大概这个感觉
document.cookie.split(`; ${'_department'}=`)

(3)B域名如何控制浏览器地址栏
目前遇到的,如果B域名携带的token过期了,要跳转A域名的登录

// window.parent.location.href = '/login'

node切换

安装nvm
这是个好东西,很多项目可能要不同版本的node安装依赖才能跑起来,因为npm是跟随node的,node版本多少,npm版本也多少,最近项目大部分都是10.0.0才能正常跑

不同node版本安装的全局包

我最近才发现,我在node10全局安装了一个whistle,结果我切换到node16,运行竟然说找不到sh命令?这全局安装原来还跟版本的吗?
一般报无法xx作为命令都是环境变量没配对的问题,右键电脑系统高级属性配置
最近遇到一个‘sh’ 不是内部或外部命令,也不是可运行的程序,这个指的是git的配置没配对

前端excel导出

有插件实现

"dependencies": {
    "xlsx": "^0.16.5" // 这个
  },
  
代码
// excel表格数据格式,二维数组
            // [
            // ['标题名']
            // ['表头','表头','表头']
            // ['值','值','值']
            // ]
            // ws是sheet对象,可以操作sheet合并单元格,设置单元格宽度等
            let ws = XLSX.utils.aoa_to_sheet(exportData)
            const wb = XLSX.utils.book_new()
            XLSX.utils.book_append_sheet(wb, ws, 'sheet')
            XLSX.writeFile(wb, '营销资源扫描图表数据' + '.xlsx')

html2canvas疑惑

这个搁置了

请求头字段不区分大小写

是的,不区分,headers['token']和headers['TOKEN']是一样的

关于时间new Date用-原来默认是8点,只有用/才是正常的0点

new Date('2021-08-11')
Wed Aug 11 2021 08:00:00 GMT+0800 (中国标准时间)

new Date('2021/08/11')
Wed Aug 11 2021 00:00:00 GMT+0800 (中国标准时间)

axios设置content-type,如果不传data设置没用

时间处理js文件moment

这次的项目有用上,觉得挺好用还行,稍微记一下
image.png
这个是地址http://momentjs.cn/docs/#/manipulating/start-of/
(1)格式化

this.$moment(+this.timeHorizon[0]).format(
          'YYYY/MM/DD'
        )

(2)提前多少

moment().subtract(1, 'days') //提前1天

(3)延后多少

moment().add(1, 'days') //延后1天

(4)以什么开头

moment().subtract(2, 'days').startOf('day') //两天前,00:00:00

(5)以什么结尾

moment().add(2, 'days').endOf('day') //两天后,23:59:59

svg图片互相影响

如果svg正常用img标签使用不会有问题,最近遇到图片换色,要用svg实现,但是这个svg,还要用组件的方式使用
首先要引入插件
image.png
然后代码这样
image.png
然后变色就是改它的css,叫什么类名你就看svg的长什么样
image.png

然后就是有个坑,两张图的组件放一起会互相影响,真的是莫名其妙,很奇葩,第二张图的类会拿到第一张图的
我是稀里糊涂解决的,就是尽量让这些svg看起来完全不一样,
image.png
image.png
然后类名也手动调整
image.png

npm的包默认是公开

平时安装依赖npm i 都是畅通无阻,因为这些包都是公开库,有一些公司的包是私有的,所以

npm login --registry=xxxxxxxxxxxxxxxxxx
然后提示你输入账号密码,输入完之后
npm i --registry=xxxxxxxxxxxxxxxxxx
这样就ok了

yarn

yarn是npm的一个包,你要用npm全局安装yarn,但是yarn的功能做的东西跟npm一样,就是yarn里面有自己的一套包管理,不知道这样理解对不对
yarn init是初始化,要加个yarn run init才可以

上课篇

.vue文件import组件,打印出来是什么

组件导进来的.vue,打印是对象,就是expordeafftaul那些东西,
所以你可以写js对象,export {},然后里面不用tempateel,写一个render函数,用这个语法写template东西,效果一样,大概这样

var obj = {
    data:function(){
        return {
            name: ''
        }
    },
    methods:{}  // 这些配置跟.vue文件写法一样,唯一不同是template
    render:function(create){  // 这个其实是template经过vue-loader处理后生成的东西,你直接这样写就省了一个转换的步骤
        create({

        })
    }
}

单点登录

cookie设置domain,单点登录就是你登录了a.boss.com之后,去b.boss.com也相当于登录了,可以设置cookie让b.boss.com也能拿到a.boss.com的cookie
cookie不同域名是不能自动拿的,理论上b不能拿a,但是可以通过设置cookie的某些属性

eslint篇

忽略

只忽略一行
image.png

忽略这一段
image.png

也能作用在html,但是是下面全部都忽略
image.png

ts的忽略

最近有个项目很急,打包的时候因为ts的问题打包失败,于是就搜如何忽略ts

单行忽略
// @ts-ignore

忽略全文
// @ts-nocheck

取消忽略全文
// @ts-check

我用到了第二个,在每个vue文件的最上面这样写
image.png


bug之所措
406 声望13 粉丝

« 上一篇
nginx的认识