Skip to content

1.由于 node 版本问题下载的CopyWebpack版本不正确等原因导致的 bug 的解决

报错:

bash
Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
options[0] should be an object:
   object { patterns, options? }
   ValidationError: Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.

经资料查找等工作发现是vue.config.js的plugin配置产生的问题:

原本的配置:

js
configureWebpack: {
  plugins: [
    new CopyWebpackPlugin([
      { from: 'nVVYnBJax5.txt'},
    ]),
  ],
},

修改为:

js
configureWebpack: {
  plugins: [
    new CopyWebpackPlugin({
        patterns:[
          { from: 'nVVYnBJax5.txt'}
        ]
      }     
    ),
  ],
},

2.npm fixed之后还是报错的解决方案

注意:npm i之后,如果使用了 npm fixed等命令进行了版本修复等工作(大概率是因为 node 版本不匹配),并且运行时还是报错,那么此时无论怎么修改(就算切换了 node 版本)还是大概率会报错,因为 fixed会修改 package.json里面的版本号,所以我们此时最好重新拉取项目,得到本来的 package.json文件,然后更换 node 版本,重新进行 npm i,此时问题大概率会解决掉

注意:经测试,node 版本16.15.1的兼容性较高,比较好用!

3.vscode 的列选择配置项

这个东西千万不要打开:非常不好用!

image-20230609125903185

4.mac 的 clash 切换代理的注意事项

mac上面的 clash 不是双击出现数字就可以了,这样并没有切换过来

必须要让这个代理的背景颜色变深,这样才是切换了过来

image-20230609130834401

4.mac 的 vacode 对于快捷键的设置(终端)

切换终端快捷键的设置

mac 的 vscode 我们把调出终端的命令设置为了``

image-20230609131727502

因为 command+`设置不成功,不知道为什么!

后面经过测试,设置为``会影响我们输入这个模板字符串

所以改成ctrl+`

option+`是调出 itrem2 的!

5.小程序中跳转按钮列表的构建策略

html
<view class="block-wrap">
  <view class="title">统计分析</view>
  <view class="block">
    <block wx:for="{{blockList3}}" wx:key="index">
      <view class="item {{item.match == 'notopen'?'not-open':''}}" wx:if="{{!item.isShow}}" data-item="{{item}}" bindtap="handleJumpRoute">
        <image src="{{httpImage}}{{item.icon}}" />
        <text>{{item.name}}</text>
      </view>
    </block>
  </view>
</view>
js
blockList3: [
  {
    icon: '/home_icon8.png',
    url: '/subpackages/home/goodsDetail/index',
    name: '备货铺货情况',
    match: 'goodsDetail'
  },
  {
    icon: '/home_icon10.png',
    url: '',
    name: '后端服务情况',
    match: 'notopen'
  },
  {
    icon: '/home_icon12.png',
    url: '/subpackages/home/agentRankingList/index',
    name: '代理商打地基排行榜',
    match: 'activeDetail'
  },
  {
    icon: '/home_icon14.png',
    url: '',
    name: '打地基异常处理数据',
    match: 'notopen'
  }
],
  
  
// 跳转路由
async handleJumpRoute(e) {
  let item = e.currentTarget.dataset.item
  if(item.match == 'notopen'){
    Common.showBox('暂未开放')
    return
  }
  let url = item.url
  await wx.navigateTo({ url })
}
css
.page-wrap .content .block-wrap .not-open{
  opacity: 0.3;
}

6.对于wx.showToast的封装

js
function showBox (text, delayTime = 2000, fn=()=>{}){
  wx.showToast({
    title: text,
    icon: 'none',
    duration: delayTime,
    success() {
        fn()
    }
  })
}

7.小程序中的nav-bar

小程序中的nav-bar就是最上面的标题栏,带有回退按钮的那个!基本上是全局统一的一个组件!

一般放在每个页面的 index.json里面

image-20230609161607794

8.text标签实现换行类似 p 标签

text标签可以实现类似 p 标签的换行功能

因为 text 标签里面可以写\n,是可以被解析为换行的!

例子:

html
<text class="title-text">智能硬件代理商\n打地基五星高活榜</text>

使用 css 样式white-space: pre-line;也可以实现 p 标签换行

使用 CSS 样式:可以利用 CSS 样式来实现换行效果。例如,将文本容器的样式设置为 white-space: pre-line;,这样文本中的换行符会被保留并显示为换行。例如:

html
<text style="white-space: pre-line;">
  第一行
  第二行
  第三行
</text>

view+text标签也可以实现换行

html
<view>
  <text>第一行</text>
  <text>第二行</text>
  <text>第三行</text>
</view>

9.小程序两种接收事件传参的方法

绑定了bindtap=“test”,并设置data-index=“111”

在test(e){}函数中,可以用e.currentTarget.index或e.detail.value接收,都可以以获取到 index 变量的值!

10.女娲后台项目不具有mac和win的适配性,要适配 win 的方法

本来是适配 mac 的,要适配 win 需要做如下的修改:

package.json本来的样子:

image-20230609174627561

改为:

image-20230609174615849

vue配置文件也要进行修改:

image-20230609174644146

11.element-ui的级联选择器懒加载

级联选择器懒加载,实现省市区的三级联动

在 data 中定义如下内容:

js
cityProps: {
  lazy: true,
    checkStrictly: true,
      lazyLoad(node, resolve) {
      const { level, data } = node;
      let params = {
        param: {}
      };
      if (data && data.value) {
        params.param["parentId"] = data.value;
      } else {
        params.param["parentId"] = 0;
      }
      queryCityListByParentId(params).then(res => {
        if (res.code == 0) {
          let cityData = [];
          cityData = res.data.map(item => {
            return {
              value: item.id,
              label: item.name,
              leaf: level >= 2 //修改这个参数即可实现不同的层级
            };
          });
          resolve(cityData);
        }
      });
    }
},

leaf参数的含义

表示什么条件下为叶子节点,叶子结点不可以再向下衍生出节点,不是叶子节点的都可以继续调用懒加载函数,如果是三级,则设置leaf: level >= 2

image-20230609214259500

定义 html:

html
<div class="search-item">
  <!-- 省市区选择 -->
  <el-cascader
               :props="cityProps"
               placeholder="请选择代理商所在地"
               @change="changeCity"
               clearable
               ></el-cascader>
</div>

定义如下方法:其中 cityNode 是一个包含了所选的省市区的 id 的列表

js
// 切换地点
changeCity(cityNode) {
  // console.log(cityNode);
  if (cityNode.length == 1) {
    this.provinceId = cityNode[0];
  } else if (cityNode.length == 2) {
    this.provinceId = cityNode[0];
    this.cityId = cityNode[1];
  } else if (cityNode.length == 3) {
    this.provinceId = cityNode[0];
    this.cityId = cityNode[1];
    this.districtId = cityNode[2];
  }
  if (!cityNode.length) {
    //如果city被清空,那么清空表单
    this.provinceId = null;
    this.cityId = null;
    this.districtId = null;
  }
},

为什么 cityNode 会是列表呢?

这里的 value 是 id 所以 changeCity得到的就直接是 id了

image-20230609195026908

12.Js数字置空

在 JavaScript 中,数字无法直接置空,因为数字是基本数据类型,它们是不可变的。然而,你可以通过赋予特定的值来表示数字为空或无效。

也就是说Js中数字没有空类型!

Js数字要置空的话,可以设置为 null 或者 undifined,而NaN 代表不是一个数字的空类型

13.AgentTree 组件

AgentTree 看一下原理是什么

html 代码:

html
<template>
  <div class="search-item">
    <span v-if="isShowLabel" class="item-label">代理商:</span>
    <!--
			ref用于让自己作为弹出框的标志
			placement是弹出的位置
			@show是弹出框被打开的事件
			@hide是弹出框被隐藏的事件
		-->
    <el-popover
      placement="bottom-start"
      height="300"
      ref="choseAgentPopover"
      trigger="click"
      @show="showAgentDepartmentPopover"
      @hide="hideAgentDepartmentPopover"
      :visible-arrow="false"
    >
      <div class="c-popover-tab-pane">
        <!--这是小搜索框-->
        <el-input
          class="keyword-search"
          :placeholder="searchPlaceholder"
          v-model="filterAgentTextClass"
        >
        </el-input>
        <!--
					data 用于指定数据源
					props 属性用于指定展示出的效果和内容,将 data 源数据的每一项格式化为含有 label、children 等属性的对象,用于树控件进行识别并进行页面的展示
					filter-node-method是用于搜索节点进行过滤的(当小搜索框进行搜索时)
					default-checked-keys是表示默认被选中的节点有哪些(用于本次选择之后下一次再打开时的效果)
					auto-expand-parent是表示是否自动打开父节点,默认开启,这里我们关闭
					check是点击勾选选中框时触发的事件
					show-checkbox展示选择框
					check-on-click-node被点击时被选中
					ref用来帮助获取 el-tree实例对象
				-->
        <el-tree
          :data="agentTreeData"
          :props="defaultProps"
          height="100px"
          show-checkbox
          check-on-click-node
          node-key="value"
          ref="agentClassTree"
          :filter-node-method="filterAgentNode"
          :default-checked-keys="selectAgent"
          :auto-expand-parent="false"
          @check="treeCheck"
        >
        </el-tree>
      </div>
    </el-popover>
    <!--这里是大搜索框,v-popover绑定点击之后弹出的弹出框-->
    <el-input
      class="selectClass"
      :placeholder="inputPlaceholder"
      readonly
      v-model="lstAgentId"
      v-popover:choseAgentPopover
    >
      <span class="el-input__suffix" slot="suffix">
        <span class="el-input__suffix-inner">
          <!--这里的 class 可以实现图标的动态翻转-->
          <i
            class="el-select__caret el-input__icon el-icon-arrow-up"
            :class="{
              'is-reverse': showAgentDropDown
            }"
          ></i>
        </span>
      </span>
    </el-input>
  </div>
</template>

js代码:

js
import { getDeptTreeInfo } from "@/api/index.js";
//封装的防抖函数
const debounce = (function() {
  let timer = 0;
  return function(func, delay) {
    clearTimeout(timer);
    timer = setTimeout(func, delay);
  };
})();
export default {
  data() {
    return {
      //用于配置 props 属性,指定 label 为 data 的 title 拼接 value,指定节点的children孩子节点为 node 对象的 children属性
      defaultProps: {
        children: "children",
        label: function(data) { //指定 label为 node 对象的 value 属性与 data 属性进行拼接(label 就是显示在页面上面的内容)
          let value = 0;
          if (data.value.split("_")[1] - 0 > 0) {
            value = data.value.split("_")[1] - 0;
          }
          return value + "-" + data["title"];
        }
      },
      selectAgent: [],
      selectAgentStr: "",
      filterAgentTextClass: "",
      agentTreeData: [],
      showAgentDropDown: false,
      lstAgentId: "",
      radios: []
    };
  },
  props: {
    isShowLabel: {
      type: Boolean,
      default: false
    },
    inputPlaceholder: {
      type: String,
      default: "请选择代理商ID/代理商名称"
    },
    searchPlaceholder: {
      type: String,
      default: "输入代理商ID/代理商名查找"
    },
    isSingleChoice: { //是否开启单选模式
      type: Boolean,
      default: false
    }
  },
  watch: {
    filterAgentTextClass(val) { //监控小搜索框输入的数据,动态进行树控件的数据展示
      if (this.showAgentDropDown) {
        //在箭头函数内部,通过this.$refs.agentClassTree.filter(val)来调用$refs.agentClassTree引用的<el-tree>组件的filter方法(也就是filterAgentNode方法),并传递了val作为参数。
        //debounce是一个防抖函数,用于延迟执行一个函数并控制其调用频率。只有当在300毫秒内没有再次调用该函数时,才会真正执行。
        debounce(() => {
          this.$refs.agentClassTree.filter(val);
        }, 300);
        if (val != "") {
          this.$set(this.agentTreeData[0], "disabled", true);
        } else {
          this.isExpand = false;
          this.$set(this.agentTreeData[0], "disabled", false);
        }
      }
    }
  },
  mounted() {
    this.queryAgentTree(); 
  },
  methods: {
    //获取树控件的源数据
    queryAgentTree() {
      const params = {
        param: {}
      };
      getDeptTreeInfo(params).then(res => {
        if (res.code === 0) {
          let treeListdata = res.data;
          this.agentTreeData = treeListdata;
        } else {
          this.$message.error(res.message);
        }
      });
    },
    // 弹出框被展示的时候
    showAgentDepartmentPopover() {
      this.showAgentDropDown = true; //用于旋转图标
    },
    // 重点逻辑在这里,当弹出框被关闭的时候
    hideAgentDepartmentPopover() {
      this.selectAgent = [];
      this.lstAgentId = "";
      this.showAgentDropDown = false;
      //注意:使用getCheckedKeys()和getCheckedNodes()方法必须在组件上有node-key="属性"这个配置,其中属性指的是绑定的源数据 data 中每一项的那个对应属性
      let selectArr = this.$refs.agentClassTree.getCheckedKeys();//得到选中的 key 列表
      let selectNodeArr = this.$refs.agentClassTree.getCheckedNodes();//得到选中的 node 节点列表
      let selectPeopleName = [];
      if (selectArr.length > 0) {
        let selectPeopleArr = selectArr; //将 key 列表进行存储
        selectNodeArr.map(item => {
          selectPeopleName.push(item["title"]);//得到所有选中的节点的 title 属性,即名字
        });
        let selectAgentStr = selectPeopleName.join(","); //拼接为 string,用于展示在 input 框中
        this.selectAgent = selectPeopleArr; //将 key 列表赋值给selectAgent,用于下一次的默认选中的标志
        this.lstAgentId = selectAgentStr;
      } else {
        this.selectAgent = [];
        this.lstAgentId = "";
      }
      let lastArr = []; //存储被切分处理后的 key 列表,将来用于传参到后端进行搜索
      this.selectAgent = this.selectAgent.map((item, index) => { //将 key 列表进行切分处理,得到需要的 key 的部分!
        if (item.split("_")[1] - 0 > 0) {
          lastArr.push(item.split("_")[1] - 0);
        }
      });
      this.$emit("selectedAgentDepartment", lastArr); //触发事件,进行搜索,传 key 列表为参数
    },
    filterAgentNode(value, data) { //用于搜索过滤,只留下 title 或者 value 中有对应搜索值的节点
      //value 为搜索值,data 为 node 列表
      if (!value) {
        return true;
      }
      return (
        data.title.toString().indexOf(value) !== -1 ||
        data.value.toString().indexOf(value) !== -1
      );
    },
    treeCheck(node, list) { //该逻辑用于实现在单选模式下,当用户选择了两个树节点时,自动将第一个选中的节点取消选择,并将第二个选中的节点设置为选中状态。——>在这里结构过于复杂,代码不适用也不启用
      //list 是一个对象,它包含了当前树节点的选择状态和相关信息,其中包括一个 checkedKeys 数组,表示当前被选中的树节点的 key 值列表。
			//node 也是一个对象,表示当前被选中的树节点对象。
      if (list.checkedKeys.length == 2 && this.isSingleChoice) {
        //单选实现
        this.$refs.agentClassTree.setCheckedKeys([node.PHY_ID]);
      }
    }
  }
};

queryAgentTree方法得到的数据结构:可以直接赋值给树控件作为源数据

image-20230612124856816

注意:这个结构就是el-tree所要求的数据结构和属性,一般 title、value 都是必有的,children 可以没有,但是属性的命名可不一定是 title、value、key,因为 props 属性会把 data 的每一项进行格式化,真正展示的内容是 props 的定义规则的内容,为以下四项:

image-20230612184850053

一般指定node-key="属性"这个配置项,指定的属性都是 data 每一项的那个 value(id)

也只有配置了 node-key=“value”这个配置项,才可以使用以下两个重点的方法

js
let selectArr = this.$refs.agentClassTree.getCheckedKeys();//得到选中的 key 列表,即选中的 data 的每一项的 value 属性
let selectNodeArr = this.$refs.agentClassTree.getCheckedNodes();//得到选中的 node 节点列表,即选中的 data 的每一项那整个对象(可以再从中获取 title 等内容)

注意:如果没有为 props 定义 children 属性的话,那么就变成了一层的树!即OnlyAgentTree 组件

14.js 中为假的内容

注意:空字符串“”在 js 中也是假的,但是空数组([])和空对象({})是真的

在 JavaScript 中,以下值被认为是假的(即逻辑上为 false):

  1. false:布尔值 false。
  2. null:表示空或无值。
  3. undefined:表示未定义的值。
  4. 0:数字 0。
  5. NaN:非数字值。
  6. "":空字符串。

这些值在条件语句(例如 if 语句)中被视为假,而其他值都被视为真。请注意,空数组([])和空对象({})被视为真值,因为它们都是对象,即使它们没有任何元素或属性。

NaN 表示的含义

NaN(非数字)不是表示空值的概念。NaN 表示一个特殊的数值,大多数情况它是由计算产生的(表示一个错误的结果,这个结果叫做不是一个数字),表示计算结果不是数字。NaN 是一个特殊的数值,代表 "Not a Number"(不是一个数字)。

NaN 可以通过一些操作产生,例如将非数字字符串解析为数字、进行无效的数学运算或使用无效的数值。

以下是一些产生 NaN 的示例:

js
let result1 = parseInt("Hello");  // 将非数字字符串解析为数字
console.log(result1);  // 输出 NaN

let result2 = 0 / 0;  // 0 除以 0
console.log(result2);  // 输出 NaN

let result3 = Math.sqrt(-1);  // 对负数求平方根
console.log(result3);  // 输出 NaN

请注意,NaN 与任何其他值(包括 NaN 本身)进行比较的结果都是 false。这意味着 NaN 不等于任何其他值,包括它自己。要检测一个值是否为 NaN,可以使用全局函数 isNaN()。

js
let value = NaN;
console.log(isNaN(value));  // 输出 true

15.分页组件的使用模版

后端返回一个 count 字段,count 字段表示总数据量,只有 count>=pageSize时才显示分页组件,所以这个是用于标志是否显示分页按钮组件的!

image-20230609201435734

分页组件的回调函数控制第几页,v-model绑定控制页面数据量

html
<!--分页组件,总数据量大于10条才会出来-->
<div class="gm-pagination" v-if="dataCount > searchInfo.pagesize">
  <el-pagination
                 background
                 layout="prev, pager, next"
                 :page-size="searchInfo.pagesize"
                 :total="dataCount"
                 @current-change="handleCurrentChange"
                 :current-page.sync="searchInfo.page"
                 >
  </el-pagination>
</div>
<!--/.分页-->

data 中的数据:

js
dataCount: 0,
searchInfo: {
  pagesize: 10,
  page: 1
},

method 方法:

js
// 控制页数
handleCurrentChange(currentPage) {
  this.searchInfo.page = currentPage;
  this.queryAgentHeightRankPage();
},

在构造请求参数的时候,在 params 对象中写上两个分页属性

image-20230612101502516

16.el-table-column的高级用法

el-table-column怎么在 html 中获取当前行的索引?scope.$index

注意:slot-scope和v-slot两种写法都可以

html
<template>
  <el-table :data="tableData">
    <el-table-column label="姓名">
      <template slot-scope="scope">
        <span>{{ scope.$index }}</span>
      </template>
    </el-table-column>
    <!-- 其他列 -->
  </el-table>
</template>

怎么在html获取当前列的值,并动态渲染呢?scope.row.变量

html
<el-table-column prop="area" align="center" label="区域">
  <template v-slot="scope">
    <template v-if="scope.$index != moreIndex"
              >{{ scope.row.area }}
      <span class="read-more" @click="lookMore(scope.$index)">
        更多
      </span></template
      >
    <template v-else
              >{{ scope.row.areaFull }}
      <span class="read-more" @click="noLookMore">
        收起
      </span></template
      >
  </template>
</el-table-column>

注意:还可以把v-slot="scope"直接写在el-table-column上面

html
<template>
  <el-table :data="tableData">
    <el-table-column label="姓名" v-slot="scope">
      <span>{{ scope.row.name }}</span>
    </el-table-column>
    <!-- 其他列 -->
  </el-table>
</template>

17.开发中比较常遇到的注意点

1.对于表单或者选择框等组件,不是用v-model绑定值的组件,那么在清空值的时候就要在回调函数中手动把 对应的 data 中的内容进行清除置空,而 v-model绑定了的话,页面空了 js 也就空了,所以不用手动置空

2.后端返回的数据,如果是需要拼接展示的,或者需要在空的时候展示特定的字符串的,用在 html 模版中的时候就需要用三元运算符等判断是否为空,否则可以不判断,但是一般都需要判断空值

image-20230609210554046

3.JavaScript 中的 getMonth() 方法返回的月份是从 0 开始的,即 0 表示一月,1 表示二月,以此类推,11 表示十二月,所以要获得正确的月份的时候需要+1