前端问题汇总

1.常用数组方法

.filter()
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
是否改变原数组:否
是否对空数组进行检测:否
语法:
const arr= [32, 33, 16, 40];
const arr1 = arr.filter(item => item >= 18)
console.log(arr) // [32, 33, 16, 40]
console.log(arr1) // [32, 33, 40]
 
.map()
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
map() 方法按照原始数组元素顺序依次处理元素。
是否改变原数组:否
是否对空数组进行检测:否
语法:
const arr= [4, 9, 16, 25];
const arr1 = arr.map(item => item+2)
console.log(arr) // [4, 9, 16, 25]
console.log(arr1) // [6, 11, 18, 27]

.forEach()
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
注意: forEach() 对于空数组是不会执行回调函数的。
tips: forEach()中不支持使用break(报错)和return(不能结束循环),有需要时可使用常规的for循环。
语法:
const arr= [4, 9, 16, 25];
const arr1 = [];
arr.forEach(item => arr1.push(item))
console.log(arr) // [4, 9, 16, 25]
console.log(arr1) // [4, 9, 16, 25]
 
.find()
find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。
find() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 undefined
注意: find() 对于空数组,函数是不会执行的。
注意: find() 并没有改变数组的原始值。
语法:
const arr= [4, 9, 16, 25];
const b = arr.find(item => item>10)
const c = arr.find(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // 16
console.log(c) // undefined

.findIndex()
findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
findIndex() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
注意: findIndex() 对于空数组,函数是不会执行的。
注意: findIndex() 并没有改变数组的原始值。
语法:
const arr= [4, 9, 16, 25];
const b = arr.findIndex(item => item>10)
const c = arr.findIndex(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // 2
console.log(c) // -1
 
.some()
some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。
some() 方法会依次执行数组的每个元素:
如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
如果没有满足条件的元素,则返回false。
注意: some() 不会对空数组进行检测。
注意: some() 不会改变原始数组。
语法:
const arr= [4, 9, 16, 25];
const b = arr.some(item => item>10)
const c = arr.some(item => item<1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // true
console.log(c) // false
 
.every()
every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。
every() 方法使用指定函数检测数组中的所有元素:
如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
如果所有元素都满足条件,则返回 true。
注意: every() 不会对空数组进行检测。
注意: every() 不会改变原始数组。
语法:
const arr= [4, 9, 16, 25];
const b = arr.every(item => item>10)
const c = arr.every(item => item>1)
console.log(arr) // [4, 9, 16, 25]
console.log(b) // false
console.log(c) // true

2.父子组件传值

父组件:
<span @click="handleRun()">调用子组件</span>
<operatingCity v-if="showCity" :showCity="showCity" @setCityValue="getCityValue"/>
 
 data() {
   return {
     showCity: false,
   };
 },
 
 handleRun(){
   this.showCity = true;
 },
 getCityValue(val){
   this.showCity = val;
 }

子组件:
<template>
  <div>
    <el-dialog title="提示" :visible.sync="dialogCity" width="30%">
      <div>
        <span>子组件内容</span>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  props: {
    showCity: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      dialogCity: false,
      param: {},
    };
  },
  created() {},
  mounted() {
    this.dialogCity = this.showCity;
  },
  methods: {
    handleSubmit() {
      this.dialogCity = false;
      this.$emit("setCityValue", false);
    },
    handleClose() {
      this.dialogCity = false;
      this.$emit("setCityValue", false);
    },
  },
};
</script>
父组件
<BmjDialog :bmjShow="bmjShow" @close="bmjClose" :bmjTitle="'即将测试小程序'"></BmjDialog>
import BmjDialog from "@/components/dialog/bmjDialog.vue";
components: {BmjDialog }
// 定义变量
bmjShow: false
// 方法
bmjClose(){
    this.bmjShow = false;
},
    

子组件
<template>
  <!-- 弹窗 -->
  <view class="contact">
    <u-popup v-model="show" mode="center" width="90%" @close="onClose" borderRadius="40">
      <view class="content">
        <view class="title">
          <text>
            提示
          </text>
          <view class="color"></view>
        </view>
        <!-- 标题 -->
        <view class="phone">{{ bmjTitle }}</view>
        <view class="btn-box">
          <view class="cancel" @click="onClose">取消</view>
          <view class="confirm" @click="confirm">确认</view>
        </view>
      </view>
    </u-popup>
  </view>
</template>
<script>

export default {
  name: "contact",
  props: {
    bmjShow: {
      type: Boolean,
      default: false
    },
    bmjTitle: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      show: false,
    }
  },
  watch: {
    bmjShow(val) {
      this.show = val;
    }
  },
  methods: {
    onClose() {
      this.show = false;
      this.$emit('close', this.show);
    },
    confirm() {
      console.log('跳转小程序');
      uni.navigateToMiniProgram({
        appId: "wx91d27dbf599dff74", // 其他小程序APPID
        path: "pages/gold/item/pages/detail/index?referer=http%3A%2F%2Fwq.jd.com%2Fwxapp%2Fpages%2Fsearch%2Flist%2Flist&sourceType=wx-search&originUrl=%2Fpages%2Fgold%2Fitem%2Fpages%2Fdetail%2Findex&gift_type=1&sku=10070450034072&pageId=W_jdgwxcx_searchresult&bbtf=1&ptag=138926.7.5", //其他小程序地址
        success: (res) => {
          // 打开成功
          console.log("打开成功", res);
          this.show = false;
          this.$emit('close', this.show);
        },
        fail: (err) => {
          console.log(err);
        },
      });
    },
  },
}

</script>
<style lang="scss" scoped>
.content {
  margin: auto;
  height: 304rpx;
  background: linear-gradient(
    175.11deg,
    rgb(126, 215, 217) -29.264%,
    rgb(255, 255, 255) 22.345%
  );
  border-radius: 40rpx;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  font-family: PingFang SC;
  text-align: center;

  .title {
    position: relative;
    margin-bottom: 30rpx;
    text {
      position: absolute;
      left: 0;
      right: 0;
      // margin: 0 auto;
      color: #000;
      font-size: 32rpx;
      font-weight: 600;
      z-index: 5;
    }
    .color {
      position: absolute;
      width: 170rpx;
      height: 16rpx;
      left: 252rpx;
      top: 32rpx;
      bottom: 0;
      z-index: 4;
      background: linear-gradient(
        90deg,
        rgb(243, 233, 129) 1.835%,
        rgba(243, 233, 129, 0) 100%
      );
      border-radius: 32rpx;
    }
  }

  .phone {
    color: rgba(0, 0, 0, 0.85);
    font-size: 28rpx;
    font-weight: 400;
  }

  .btn-box {
    display: flex;
    padding: 0 32rpx;
    justify-content: space-between;
    .cancel,
    .confirm {
      width: 265rpx;
      height: 64rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: PingFang SC;
      font-size: 28rpx;
      font-weight: 500;
      border-radius: 32rpx;
      border: 0px;
    }
    .cancel {
      background: rgba(93, 138, 163, 0.1);
      color: rgba(0, 0, 0, 0.3);
      margin-right: 16rpx;
    }
    .confirm {
      color: #fff;
      background: #32afb2;
    }
  }
}
</style>

3.Computed的使用

<template>
  <div class="hello">
    <!-- 写法1 -->
    <div>
      <label v-if="count < 0">111</label>
      <label v-else-if="count === 0">222</label>
      <label v-else-if="count <= 3">333</label>
      <label v-else-if="count < 15">444</label>
      <label v-else>555</label>
    </div>

    <!-- 写法2 -->
    <div>
      <label>{{countMsg}}</label>
    </div>

  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      count: -21,
    };
  },
  methods: {},

  computed: {
    // 写法2
    countMsg() {
      if (this.count < 0) {
        return "111";
      } else if (this.count === 0) {
        return "222";
      } else if (this.count <= 3) {
        return "333";
      } else if (this.count < 15) {
        return "444";
      } else {
        return "555";
      }
    },
  },
};
</script>
<view class="expired" v-if="isExpired(item)">已过期</view>

computed: {
	// 计算过期的状态
	isExpired() {
		return function(item) {
			return new Date(item.expireTime) <= new Date();
		}
	}
	// 使用ES6的解构赋值,优化代码
    // isExpired: () => ({ expireTime }) => new Date(expireTime) < new Date()
},
优化前:
<el-table-column prop="productStatus" label="状态" min-width="120">
  <template slot-scope="scope">
    <span v-if="scope.row.productStatus === 'on_shelves' && scope.row.isSell == 0">停售</span>
    <span v-else-if="scope.row.productStatus === 'on_shelves' && scope.row.isSell == 1">在售</span>
    <span v-else-if="scope.row.productStatus === 'for_sale'">待发售</span>
    <span v-else-if="scope.row.productStatus === 'off_shelves'">已下架</span>
  </template>
</el-table-column>


优化后:
<el-table-column prop="productStatus" label="状态" min-width="120">
  <template slot-scope="scope">
    <span>{{ getStatusMsg(scope.row) }}</span>
  </template>
</el-table-column>

methods: {
  getStatusMsg(row) {
    if (row.productStatus === 'on_shelves' && row.isSell == 0) {
      return '停售';
    } else if (row.productStatus === 'on_shelves' && row.isSell == 1) {
      return '在售';
    } else if (row.productStatus === 'for_sale') {
      return '待发售';
    } else if (row.productStatus === 'off_shelves') {
      return '已下架';
    }
    return '';
  },
}

4.Watch的使用

//1.监听简单数据类型
<template>
  <div>
    <el-input v-model="mergeText"></el-input>
  </div>
</template>

<script>
export default {
  data() {
    return {
      mergeText:'',
    };
  },
  watch:{
    // mergeText值变化即触发
    mergeText(newval,oldVal){
      console.log(this.mergeText,newval,oldVal);
    }
  },
};
</script>

//2.监听复杂数据(深度监听 deep)
<template>
  <div>
    <el-input v-model="obj.text"></el-input>
  </div>
</template>

<script>
export default {
  data() {
    return {
      obj:{
        text:'hello'
      }
    };
  },
  watch:{
    // 监听对象obj的变化
    obj:{
      handler (newVal,oldval) {
        console.log(newVal,oldval)
      },
      deep: true,
      immediate: true
    }
  },
};
</script>

5.NextTick的使用

<template>
  <div class="box">{{msg}}</div>
</template>

<script>
export default {
  name: "index",
  data() {
    return {
      msg: "hello",
    };
  },
  mounted() {
    // console.log(box.innerHTML) // hello
    // 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
    this.$nextTick(() => {
      console.log(box.innerHTML); // world
    });
    this.msg = "world";
    let box = document.getElementsByClassName("box")[0];
  },
};
</script>

6.Vue中实现函数的防抖和节流

// 防抖
function _debounce(fn, delay = 500) {
  var timer = null;
  return function () {
    var _this = this;
    var args = arguments;
    if (timer) clearTimeout(timer); 
    timer = setTimeout(function () {
      fn.apply(_this, args);
    }, delay);
  };
}

// 节流
function _throttle(fn,delay = 1000){
  var lastTime, timer, delay;
  return function(){
    var _this = this;
    var args = arguments;
    var nowTime = Date.now(); 
    if(lastTime && nowTime - lastTime < delay){
      if (timer) clearTimeout(timer); 
      timer = setTimeout(function(){
        lastTime = nowTime;
        fn.apply(_this,args);
      },delay)
    }else{
      lastTime = nowTime;
      fn.apply(_this,args);
    } 
  }
}  

export {
  _debounce,
  _throttle,
}
<template>
  <div class="about">
    <el-input v-model="inputVal" placeholder="请输入内容" @input="inputChange"></el-input>
  </div>
</template>

<script>
import {_debounce, _throttle} from '@/utils/index.js'
export default {
  data() {
    return {
      inputVal:'',
      count:0,
    };
  },
  methods: {
    // input值改变时触发
    inputChange:_debounce(function(){
      console.log(this.inputVal)
    },1000),
    // 滚动条滚动时触发
    scroll(){
      this.count += 1;
      console.log('scroll触发了'+ this.count +'次')
    }
  },
  mounted() {
    window.addEventListener('scroll', _throttle(this.scroll,5000));
  },
};
</script>

<style lang="stylus" scoped>
.about{
  width:100%;
  height:800px;
}
</style>

7.Vue中获取当前时间并实时刷新

<template>
  <div>
    {{nowDate}}{{nowWeek}}{{nowTime}}
  </div>
</template>

<script>
export default {
  data() {
    return {
      nowDate: "", // 当前日期
      nowTime: "", // 当前时间
      nowWeek: "", // 当前星期
    };
  },
  methods: {
    dealWithTime(data) {
      // 获取当前时间
      let Y = data.getFullYear();
      let M = data.getMonth() + 1;
      let D = data.getDate();
      let H = data.getHours();
      let Min = data.getMinutes();
      let S = data.getSeconds();
      let W = data.getDay();
      H = H < 10 ? "0" + H : H;
      Min = Min < 10 ? "0" + Min : Min;
      S = S < 10 ? "0" + S : S;
      switch (W) {
        case 0:
          W = "日";
          break;
        case 1:
          W = "一";
          break;
        case 2:
          W = "二";
          break;
        case 3:
          W = "三";
          break;
        case 4:
          W = "四";
          break;
        case 5:
          W = "五";
          break;
        case 6:
          W = "六";
          break;
        default:
          break;
      }
      this.nowDate = Y + "年" + M + "月" + D + "日 ";
      this.nowWeek = "周" + W;
      this.nowTime = H + ":" + Min + ":" + S;
    },
  },
  mounted() {
    // 页面加载完后显示当前时间
    this.dealWithTime(new Date());
    // 定时刷新时间
    this.timer = setInterval(() => {
      this.dealWithTime(new Date()); // 修改数据date
    }, 500);
  },
  destroyed() {
    if (this.timer) {
      // 注意在vue实例销毁前,清除我们的定时器
      clearInterval(this.timer);
    }
  },
};
</script>

8.Vue中iframe的内容加载慢,实现加载(Loading)效果

<template>
  <div style="height:1000px;" v-loading="loading">
    <iframe ref="Iframe" src="https://www.taobao.com/" width="100%" height="100%" frameborder="0">
    </iframe>
  </div>
</template>

<script>
export default {
  data() {
    return {
      loading: false,
    };
  },
  methods: {
    iframeLoad() {
      this.loading = true;
      const iframe = this.$refs.Iframe;
      if (iframe.attachEvent) {
        // IE
        iframe.attachEvent("onload", () => {
          this.loading = false;
        });
      } else {
        // 非IE
        iframe.onload = () => {
          this.loading = false;
        };
      }
    },
  },
  mounted() {
    this.iframeLoad();
  },
};
</script>

9.Vue中动态添加class

//添加单个
:class="name1 == '名称1' ? 'class1' : 'class2'"
//添加多个
:class="[name1 == '名称1' ? 'class1' : 'class2', name2 == '名称2' ? 'new1' : 'new2']"

10.Vue中时间格式转化

/*
1.转换带T的时间格式
举例:formDateT('2019-01-01T08:01:01') 
结果:2019-01-01 08:01:01
*/ 
export const formDateT = (data) => {
  if (!data) return;
  let dates = new Date(data).toJSON();
  return new Date(+new Date(dates) + 8 * 3600 * 1000)
    .toISOString()
    .replace(/T/g, " ")
    .replace(/\.[\d]{3}Z/, "");
};

/*
2.计算两个时间相差的天、小时、分、秒
举例:timediff("2022-01-06 05:18:34", "2022-01-06 07:10:34")
结果:0天1小时52分0秒
*/
export const timediff = (startDate, endDate) => {
  if (!startDate || !endDate) return;
  //时间差的毫秒数
  let date3 = new Date(endDate).getTime() - new Date(startDate).getTime();
  //计算出相差天数
  let days = Math.floor(date3 / (24 * 3600 * 1000));
  //计算出小时数
  let leave1 = date3 % (24 * 3600 * 1000); //计算天数后剩余的毫秒数
  let hours = Math.floor(leave1 / (3600 * 1000));
  //计算相差分钟数
  let leave2 = leave1 % (3600 * 1000); //计算小时数后剩余的毫秒数
  let minutes = Math.floor(leave2 / (60 * 1000));
  //计算相差秒数
  let leave3 = leave2 % (60 * 1000); //计算分钟数后剩余的毫秒数
  let seconds = Math.round(leave3 / 1000);

  return days + "天" + hours + "小时" + minutes + "分" + seconds + "秒";
};

/*
3.时间戳转换成,yyyy-mm-dd hh:ii:ss格式
举例:formatDate(1641417514502)
结果:2022-01-06 05:18:34
*/
export const formatDate = (date) => {
  if (!date) return;
  let time = new Date(date);
  return (
    time.getFullYear() +
    "-" +
    (time.getMonth() + 1).toString().padStart(2, "0") +
    "-" +
    time.getDate().toString().padStart(2, "0") +
    "\xa0" +
    time.getHours().toString().padStart(2, "0") +
    ":" +
    time.getMinutes().toString().padStart(2, "0") +
    ":" +
    time.getSeconds().toString().padStart(2, "0")
  );
};

11.Element-ui中表格(Table)实现跨页多选数据——功能实现

解决方案
1.在table一定要定义以下事件和列表属性:
row-key //写在table标签上
reserve-selection // 写在有选择框的那一列上

<el-table @selection-change="handleSelectionChange" :row-key="getRowKeys">
  ......
 </el-table>  //写在table标签上
 
 <el-table-column type="selection" :reserve-selection="true" width="40" align="center">
</el-table-column>   // 写在有选择框的那一列上 type 必须为 selection

2、在data上定义row-key绑定的
getRowKeys(row) {
    return row.id;
},

3、把勾选的数据传到后台所以在methods定义
handleSelectionChange(val) {
    this.multipleSelection = val;//勾选放在multipleSelection数组中
},

注:此时问题已解决,但是怎样进行清空已经选择的数据呢,代码如下。
使用后怎样清空数据
1、在表格上操作dom元素,设置ref属性
 <el-table @selection-change="handleSelectionChange" ref="multiTable" :row-key="getRowKeys">
  ......
 </el-table>

2、清空数据
在你点完确定后,调用此方法。比如是弹框,有个确定按钮,绑定的click事件为confirm,在methods里写confirm方法。
confirm(){
this.dialogvisible=false //关闭弹框
this.$refs.multiTable.clearSelection() //清除选中的数据
}
注:也可以在弹框刚打开的时候进行清空,在这里加个判断,判断是否有选中的数据,如果有执行this.$refs.multiTable.clearSelection() ,如果没有就不用清空。

12.Element-ui中表格(Table)实现表头、表格内容,任意格都可编辑实现

<template>
  <div class="app-container">
    <el-table :data="tableData" @cell-click="handleCellClick" @header-click="handleHeaderClick" :cell-class-name="cellClassName" style="width: 90%;align: center;cursor: pointer;" :header-cell-style="{ height: '50px' }" :row-style="{ height: '50px' }">
      <el-table-column :label="item.propName" :property="item.prop" v-for="item in tableColumnList" :key="item.prop" align="center">
        <template slot-scope="scope">
          <span>{{scope.row[scope.column.property]}}</span>
        </template>
      </el-table-column>
      <el-table-column label="添加项目流程" width="120" prop="addTableHeaderName" align="center" />
    </el-table>
    <el-dialog :visible.sync="dialogForHeader" title="修改项目流程名称" width="800">
      <el-form ref="form" :model="tableHeader" label-width="80px">
        <el-form-item label="表头名称">
          <el-input v-model="tableHeader.tableHeaderName" placeholder="请输入表头名称" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="handleHeaderSubmit">确 定</el-button>
        <el-button @click="handleHeaderCancel">取 消</el-button>
      </div>
    </el-dialog>

    <el-dialog :visible.sync="dialogForTable" title="修改项目流程内容" width="800">
      <el-form ref="form" :model="tableCell" label-width="120px">
        <el-form-item label="流程内容名称">
          <el-input v-model="tableCell.tableCellData" placeholder="流程内容名称" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="handleCellSubmit">确 定</el-button>
        <el-button @click="handleCellCancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>
  
<script>
export default {
  data() {
    return {
      tableCellIndex: 0,
      TableColumnIndex: 0,
      tableCell: { tableCellData: "" },
      dialogForTable: false,
      num: 6,
      tableHeader: { tableHeaderName: "", property: "" },
      dialogForHeader: false,
      // 扩展:将tableColumnList和tableData当作字符串传给后端
      // 1.数组转化成字符串:JSON.stringify(tableData)
      // 2.字符串转化成数组:JSON.parse(tableData);
      tableColumnList: [
        { prop: "0", propName: "编号" },
        { prop: "1", propName: "名字" },
        { prop: "2", propName: "保质期" },
        { prop: "3", propName: "特点1" },
        { prop: "4", propName: "特点2" },
        { prop: "5", propName: "特点3" },
      ],
      tableData: [
        {
          0: "2016-05-01",
          1: "王小虎1",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-02",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
        {
          0: "2016-05-02",
          1: "王小虎2",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-02",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
        {
          0: "2016-05-03",
          1: "王小虎3",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-02",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
        {
          0: "2016-05-04",
          1: "王小虎4",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-02",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
        {
          0: "2016-05-05",
          1: "王小虎5",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-06",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
        {
          0: "2016-05-07",
          1: "王小虎6",
          2: "上海市普陀区金沙江路 1518 弄",
          3: "2016-05-02",
          4: "王小虎",
          5: "上海市普陀区金沙江路 1518 弄",
        },
      ],
    };
  },
  methods: {
    // 给每一行数据添加index,用来定位修改单元格
    cellClassName({ row, column, rowIndex, columnIndex }) {
      row.index = rowIndex;
      column.index = columnIndex;
    },
    // 修改表头
    handleHeaderClick(val) {
      if (val.property == "addTableHeaderName") {
        this.tableColumnList.push({
          prop: this.num.toString(),
          propName: "点击编辑项目流程名称",
        });
        for (let i = 0; i < this.tableData.length; i++) {
          this.$set(this.tableData[i], [parseInt(this.num)], "请添加内容");
        }
        this.num = this.num + 1;
      } else {
        this.tableHeader.tableHeaderName = val.label;
        this.tableHeader.property = val.property;
        this.dialogForHeader = true;
      }
    },
    // 修改单元格
    handleCellClick(row, column) {
      this.tableCellIndex = row.index;
      this.TableColumnIndex = column.index;
      this.tableCell.tableCellData = row[this.TableColumnIndex];
      this.dialogForTable = true;
    },
    handleHeaderSubmit() {
      this.tableColumnList.map((item, index) => {
        if (item.prop === this.tableHeader.property) {
          item.propName = this.tableHeader.tableHeaderName;
        }
      });
      this.dialogForHeader = false;
    },
    handleCellSubmit() {
      this.tableData[Number(this.tableCellIndex)][
        Number(this.TableColumnIndex)
      ] = this.tableCell.tableCellData;
      this.rush();
      this.dialogForTable = false;
    },
    //强制刷新数据
    rush() {
      this.$set(this.tableData);
    },
    handleHeaderCancel() {
      this.dialogForHeader = false;
    },
    handleCellCancel() {
      this.dialogForTable = false;
    },
  },
};
</script>

13.Element-ui中表格(Table)单元格内添加换行转义符 

<template>
  <div class="home">
    <el-table :data="tableData" border style="width: 80%">
      <el-table-column align="center" prop="number" label="编号" width="180"></el-table-column>
      <el-table-column align="center" prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column align="center" prop="address" label="地址"></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableData: [
        {
          number: "20160503",
          name: "王小虎",
          address: "2016-05-03" + "\n" + "武汉市江夏区文化大道",
        },
        {
          number: "20160504",
          name: "张小虎",
          address: "2019-05-04" + "\n" + "武汉市洪山区洪山侧路",
        },
        {
          number: "20160501",
          name: "李小虎",
          address: "2020-05-01" + "\n" + "南京市建邺区白龙江东街",
        },
        {
          number: "20160502",
          name: "宋小虎",
          address: "2021-05-02" + "\n" + "南京市江宁区水阁路",
        },
      ],
    };
  },
};
</script>

<style lang="scss" scoped>
.home {
  ::v-deep .el-table {
    .cell {
      white-space: pre-line;
    }
  }
}
</style>

14.Element-ui中表格(Table)组件中滚动条样式修改

//1.修改单个滚动条样式
<style lang="scss" scoped>
.el-table {
	/deep/ .el-table__body-wrapper::-webkit-scrollbar {
	    width: 10px; /*滚动条宽度*/
	    height: 10px; /*滚动条高度*/
	}
	/*定义滚动条轨道 内阴影+圆角*/
	/deep/ .el-table__body-wrapper::-webkit-scrollbar-track {
	    box-shadow: 0px 1px 3px #071e4a inset; /*滚动条的背景区域的内阴影*/
	    border-radius: 10px; /*滚动条的背景区域的圆角*/
	    background-color: #071e4a; /*滚动条的背景颜色*/
	}
	/*定义滑块 内阴影+圆角*/
	/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb {
	    box-shadow: 0px 1px 3px #00a0e9 inset; /*滚动条的内阴影*/
	    border-radius: 10px; /*滚动条的圆角*/
	    background-color: #00a0e9; /*滚动条的背景颜色*/
	}
}
</style>

//2.修改全局滚动条样式(推荐)
<style lang="scss">
::-webkit-scrollbar {
  width: 6px;
  height: 8px;
  background-color: #ebeef5;
}
::-webkit-scrollbar-thumb {
  box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
  background-color: #ccc;
}
::-webkit-scrollbar-track{
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  border-radius: 3px;
  background: rgba(255, 255, 255, 1);
}
</style>

15.Element-ui中表格 (Table) 组件中动态合并单元格

<template>
  <div class>
    <el-table
      :data="listData"
      :span-method="objectSpanMethod"
      border
      class="tableArea"
      style="width: 40%;"
    >
      <el-table-column label="商品类别" prop="productType" align="center" width="200"></el-table-column>
      <el-table-column label="商品数量" prop="amount" align="center"></el-table-column>
      <el-table-column label="商品价格" prop="price" align="center"></el-table-column>
      <el-table-column label="商品名称" prop="productName" width="200px" align="center"></el-table-column>
      <el-table-column label="更新时间" prop="updateTime" align="center"></el-table-column>
    </el-table>
  </div>
</template>
<script>
export default {
  data() {
    return {
      listData: [],
      testArr1: [],
      testArr2: [],
      testPosition1: 0,
      testPosition2: 0,
    };
  },
  methods: {
    // 获取数据
    queryData() {
      this.listData = [
        {
          id: "201808300001",
          productType: "纺织品",
          amount: 20,
          productName: "上衣",
          price: "80",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300002",
          productType: "纺织品",
          amount: 20,
          productName: "裤子",
          price: "76",
          updateTime: "2018-08-31",
        },
        {
          id: "201808300003",
          productType: "皮制品",
          amount: 100,
          productName: "挎包",
          price: "150",
          updateTime: "2018-08-31",
        },

        {
          id: "201808300004",
          productType: "皮制品",
          amount: 180,
          productName: "鞋子",
          price: "76",
          updateTime: "2018-08-29",
        },
        {
          id: "201808300005",
          productType: "绸缎",
          amount: 80,
          productName: "旗袍",
          price: "106",
          updateTime: "2018-08-31",
        },
        {
          id: "201808300006",
          productType: "纺织品",
          amount: 20,
          productName: "短裙",
          price: "36",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300007",
          productType: "纺织品",
          amount: 80,
          productName: "短袖",
          price: "36",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300008",
          productType: "纺织品",
          amount: 20,
          productName: "短袖",
          price: "36",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300009",
          productType: "皮制品",
          amount: 20,
          productName: "钱包",
          price: "60",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300011",
          productType: "纺织品",
          amount: 90,
          productName: "手套",
          price: "60",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300012",
          productType: "纺织品",
          amount: 90,
          productName: "袜子",
          price: "36",
          updateTime: "2018-08-30",
        },
        {
          id: "201808300013",
          productType: "饮料",
          amount: 100,
          productName: "雪碧",
          price: "5",
          updateTime: "2018-08-31",
        },
        {
          id: "201808300013",
          productType: "纺织品",
          amount: 100,
          productName: "风衣",
          price: "50",
          updateTime: "2018-08-31",
        },
      ];

      this.rowspan(this.testArr1, this.testPosition1, "productType");
      this.rowspan(this.testArr2, this.testPosition2, "amount");
    },
    rowspan(spanArr, position, spanName) {
      this.listData.forEach((item, index) => {
        if (index === 0) {
          spanArr.push(1);
          position = 0;
        } else {
          if (
            this.listData[index][spanName] ===
            this.listData[index - 1][spanName]
          ) {
            spanArr[position] += 1;
            spanArr.push(0);
          } else {
            spanArr.push(1);
            position = index;
          }
        }
      });
    },
    // 表格合并行
    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0) {
        const _row = this.testArr1[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col,
        };
      }
      if (columnIndex === 1) {
        const _row = this.testArr2[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col,
        };
      }
    },
  },
  mounted() {
    this.queryData();
  },
};
</script>

16.Element-ui中选择器(Select)解决数据量大导致渲染慢、页面卡顿的问题

// utils.js
function _debounce(fn, delay = 300) {
  var timer = null;
  return function () {
    var _this = this;
    var args = arguments;
    if (timer) clearTimeout(timer); 
    timer = setTimeout(function () {
      fn.apply(_this, args);
    }, delay);
  };
}
export {
  _debounce
}
<template>
  <div class="content">
    <el-select 
      v-model="chooseValue" clearable filterable :filter-method="filterMethod" 
      v-el-select-loadmore="loadMore(rangeNumber)"
      @visible-change="visibleChange">
      <el-option
        v-for="(item, index) in options.slice(0, rangeNumber)"
        :key="index"
        :label="item.label"
        :value="item.value">
      </el-option>
    </el-select>
  </div>
</template>
 
<script>
import {_debounce} from '@/utils/index.js'
export default {
  data() {
    return {
      chooseValue: "",
      options: [],
      rangeNumber: 10,
      resOptions:[],
    };
  },
  methods: { 
    // 模拟获取大量数据
    getList() {
      // 测试数据15000条数据, 这里数据多少条无所谓,options.slice(0, rangeNumber)方法只会默认加载初始的10条数据
      for (let i = 0; i < 25000; i++) {
        this.resOptions.push({label: "选择"+i,value:"选择"+i});
      } 
    },
    loadMore(n) {
      // n是默认初始展示的条数会在渲染的时候就可以获取,具体可以打log查看
      // elementui下拉超过7条才会出滚动条,如果初始不出滚动条无法触发loadMore方法
      return () => (this.rangeNumber += 5); // 每次滚动到底部可以新增条数  可自定义
    },
    // 筛选方法
    filterMethod:_debounce(function(filterVal){
      if(filterVal){
        let filterArr = this.resOptions.filter((item)=>{
          return item.label.toLowerCase().includes(filterVal.toLowerCase())
        })
        this.options = filterArr;
      }else{
        this.options = this.resOptions;
      }
    },500),
    // 下拉框出现时,调用过滤方法
    visibleChange(flag){
      if(flag){
        this.filterMethod()
      }
    },
  },
  beforeMount() {
    this.getList();
  },
  directives:{
    'el-select-loadmore':(el, binding) => {
      // 获取element-ui定义好的scroll盒子
      const SELECTWRAP_DOM = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
      if(SELECTWRAP_DOM){
        SELECTWRAP_DOM.addEventListener("scroll", function () {
          /**
           * scrollHeight 获取元素内容高度(只读)
           * scrollTop 获取或者设置元素的偏移值,
           *  常用于:计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
           * clientHeight 读取元素的可见高度(只读)
           * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
           * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
           */
          const condition = this.scrollHeight - this.scrollTop <= this.clientHeight;
          if (condition) binding.value();
        });
      }
    },
  }
};
</script>

17.Element-ui中树形控件(Tree)实现只显示某一层级复选框且单选

<template>
  <div class="wrap">
    <el-tree :data="treeData" ref="tree" show-checkbox :check-strictly="true" node-key="id" :props="defaultProps" @check-change="treeCheckedChange">
    </el-tree>
  </div>
</template>

<script>
export default {
  data() {
    return {
      defaultProps: {
        children: "children",
        label: "label",
      },
      treeData: [],
      testdata: [
        {
          id: 1,
          label: "一级 1",
          children: [
            {
              id: 3,
              label: "二级 1-1",
              children: [
                {
                  id: 7,
                  label: "二级 1-1-1",
                },
              ],
            },
          ],
        },
        {
          id: 2,
          label: "一级 2",
          children: [
            {
              id: 4,
              label: "二级 2-1",
              children: [
                {
                  id: 5,
                  label: "三级 2-1-1",
                },
                {
                  id: 6,
                  label: "三级 2-1-2",
                },
              ],
            },
          ],
        },
      ],
    };
  },
  methods: {
    formatData(params) {
      let data = params;
      data.map((item) => {
        if (item.hasOwnProperty("children")) {
          item.disabled = true;
          this.formatData(item.children);
        }
      });
      return data;
    },
    treeCheckedChange(data, isChecked) {
      if (isChecked) {
        const checked = [data.id]; // id为tree的node-key属性
        this.$refs.tree.setCheckedKeys(checked);
      }
    },
  },
  mounted() {
    this.treeData = this.formatData(this.testdata);
  },
};
</script>

<style lang="scss" scoped>
.wrap {
  /deep/.el-checkbox__input.is-disabled {
    display: none;
  }
}
</style>

18.Element-ui中树形控件(Tree)搜索目标子节点展示

<template>
    <div>
        <el-input
                placeholder="输入关键字进行过滤"
                v-model="filterText">
        </el-input>

        <div style="display: flex;justify-content: space-around">
            <div>
                <p style="color: red">修改后的查询</p>
                <el-tree :data="data1"
                         default-expand-all
                         ref="tree">
                </el-tree>
            </div>
            <div>
                <p style="color: red">element-ui 提供的查询</p>
                <el-tree :data="data1"
                         default-expand-all
                         :filter-node-method="filterNode"
                         ref="tree1">
                </el-tree>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                filterText: '',
                data1: [{
                    id: 100,
                    label: '1263',
                    children: [
                        {
                            id: 100,
                            label: '852',
                        }, {
                            id: 111,
                            label: '369',
                        }, {
                            id: 852,
                            label: 'ki',
                            children: [
                                {
                                    id: 96,
                                    label: 'pp',
                                }
                            ]
                        },
                    ]
                }, {
                    id: 1,
                    label: '一级 1',
                    children: [{
                        id: 4,
                        label: 'gf',
                        children: [{
                            id: 23,
                            label: 'lk',
                            children: [
                                {
                                    id: 9,
                                    label: 'abnc'
                                }, {
                                    id: 11,
                                    label: 'abnc43'
                                }, {
                                    id: 10,
                                    label: '三级 1-1-2'
                                }
                            ]
                        }]
                    }]
                }, {
                    id: 2,
                    label: '一级 2',
                    children: [{
                        id: 5,
                        label: '二级 2-1',
                    }, {
                        id: 6,
                        label: '二级 2-2',
                    }]
                }, {
                    id: 3,
                    label: '一级 3',
                    children: [{
                        id: 56,
                        label: 'test',
                        children: [
                            {
                                id: 7,
                                label: '二级 3-1'
                            }, {
                                id: 8,
                                label: '二级 3-2'
                            }
                        ]
                    }]
                }],
            }
        },
        watch: {
            filterText(val) {
                this.filter(val)
                this.$refs.tree1.filter(val);
            }
        },
        methods: {
            /**
             * 遍历节点
             **/
            filterNode(value, data, node) {
                if (data.label.indexOf(value) !== -1) {
                    return true
                }
            },

            /**
             * 递归遍历设置那些节点展示,那些节点不展示
             **/
            filter(value) {
                const traverse = (node) => {
                    const childNodes = node.root ? node.root.childNodes : node.childNodes;

                    childNodes.forEach((child) => {
                        child.visible = this.filterNode(value, child.data, child);

                        traverse(child);
                    });

                    // node.visible为真,子节点也为真,就进行递归遍历设置子节点内容为展示状态
                    if (node.visible && node.childNodes.length) {
                        this.findChildNodes(node.childNodes);
                    }

                    if (!node.visible && childNodes.length) {
                        let allHidden = true;
                        allHidden = !childNodes.some(child => child.visible);

                        if (node.root) {
                            node.root.visible = allHidden === false;
                        } else {
                            node.visible = allHidden === false;
                        }
                    }
                    if (!value) return;

                    if (node.visible && !node.isLeaf) node.expand();
                };

                traverse(this.$refs.tree.store);
            },

            /**
             * 递归循环子节点
             * 设置过滤目标节点下面的子节点为展示状态
             **/
            findChildNodes(data){
                for (let item of data) {
                    item.visible = true;
                    if (item.childNodes.length) {
                        return this.findChildNodes(item.childNodes);
                    }
                }
            }
        },
    }
</script>

19.Element-ui中级联选择器(Cascader)组装数据

<template>
  <div>
    <span>单选选择任意一级选项</span>
    <el-cascader v-model="areaId" :options="options" :props="areaProps" clearable></el-cascader>
    <span>{{areaId}}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      areaId: "yizhi",
      areaProps: {
        label: "areaName",
        value: "areaId",
        children: "child",
        checkStrictly: true,
        emitPath: false,
      },
      options: [
        {
          areaId: "zhinan",
          areaName: "指南",
          child: [
            {
              areaId: "shejiyuanze",
              areaName: "设计原则",
              child: [
                {
                  areaId: "yizhi",
                  areaName: "一致",
                },
                {
                  areaId: "fankui",
                  areaName: "反馈",
                },
                {
                  areaId: "xiaolv",
                  areaName: "效率",
                },
                {
                  areaId: "kekong",
                  areaName: "可控",
                },
              ],
            },
            {
              areaId: "daohang",
              areaName: "导航",
              child: [
                {
                  areaId: "cexiangdaohang",
                  areaName: "侧向导航",
                },
                {
                  areaId: "dingbudaohang",
                  areaName: "顶部导航",
                },
              ],
            },
          ],
        },
      ],
    };
  },
};
</script>

20.Element-ui中表格(Table)多选改为单选功能 

<template>
  <el-table :data="tableData" :row-key="row => row.id" :highlight-current-row="highlightCurrentRow" @row-click="handleRowClick">
    <!-- 将 type 属性设为 'selection' 的列移除,并在第一列中使用自定义的 radio 组件来实现单选功能 -->
    <el-table-column width="55" align="center">
      <template slot-scope="scope">
        <el-radio v-model="selectedRowId" :label="scope.row.id">
          <span class="radio-no-label"></span>
        </el-radio>
      </template>
    </el-table-column>
    <el-table-column prop="name" label="姓名"></el-table-column>
    <el-table-column prop="age" label="年龄"></el-table-column>
    <el-table-column prop="sex" label="性别"></el-table-column>
  </el-table>
</template>

<script>
export default {
  data() {
    return {
      tableData: [
        { id: 1, name: "张三", age: 18, sex: "男" },
        { id: 2, name: "李四", age: 20, sex: "女" },
        { id: 3, name: "王五", age: 22, sex: "男" },
      ],
      selectedRowId: null,
      highlightCurrentRow: true,
    };
  },
  methods: {
    // 处理行点击事件,将点击的行数据更新为选中的行数据
    handleRowClick(row) {
      this.selectedRowId = row.id;
    },
  },
};
</script>

<style scoped>
.radio-no-label {
  display: none;
}
</style>

21.vue实现日历备忘录

<template>
  <div class="vue-calendar">
    <div class="calendar-title">
      <div>
        <p>
          <i class="icon-arrow-left-year calendar-icon" @click="lastYear"><<</i>
          <i class="icon-arrow-left calendar-icon" @click="lastMonth"><</i>
        </p>
        <p>
          <span>{{ year }} 年 </span>
          <span> {{ month + 1 }} 月</span>
        </p>
        <p>
          <i class="icon-arrow-right calendar-icon" @click="nextMonth">></i>
          <i class="icon-arrow-right-year calendar-icon" @click="nextYear">>></i>
        </p>
      </div>
    </div>
    <table id="table">
      <thead>
      <tr>
        <th v-for="item in weekArray" :key="item">{{ item }}</th>
      </tr>
      </thead>
      <tbody>
      <tr v-for="(item, index) in dateArr" :key="index">
        <td v-for="(val, num) in item" :key="num" @click="handle(val)"
            :class="{'today':setClassToday(val)}">

          <span :class="`${val.class}`">{{ val.day }}</span>

          <div v-for="(textItem, index) in infoArr" :key="index">
            <template v-if="textItem.day === val.day && textItem.month === val.month && textItem.year === val.year">
              <span v-show="textItem.count">({{ textItem.count }} 条)</span>
              <div>
                <p v-for="(value, num) in textItem.taskCalendarList" :key="num">
                  {{ value }}
                </p>
              </div>
            </template>
          </div>
        </td>
      </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  name: 'calendar',
  data() {
    return {
      activeIndex: null,
      dateArr: [],
      weekArray: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
      month: new Date().getMonth(),
      year: new Date().getFullYear(),
      day: new Date().getDate(),
      currentDate: new Date(),
      infoArr: [
        {
          year: new Date().getFullYear(),
          month: new Date().getMonth() + 1,
          day: 14,
          count: 5,
          taskCalendarList: ['07:00 嗷嗷嗷', '08:00 酷酷酷', '13:35 水水水水水', '18:20 斤斤计较', '20:00 噢噢噢噢']
        }
      ]
    }
  },
  mounted() {
    this.createDate()
  },
  methods: {
    /**
     * 日期计算逻辑
     **/
    createDate() {
      this.dateArr = [];
      let arr = [];
      let count = 0;
      let date = new Date(this.year, this.month);

      // setMonth(4) === 5月份,4 + 1 = 6月份
      // setDate(0) 为 setMonth(4) 设置月份的上个月的最后一天
      // 如:当前为5月份,setMonth(4 + 1)为6月份,setDate(0)设置的就是5月份的最后一天
      date.setMonth(this.month + 1);
      date.setDate(0);

      let lastDay = date.getDate(); // 获取最后一天

      // 按当前月份共有多少天循环创建数组
      for (let i = 0; i < lastDay; i++) {
        date.setDate(i + 1); // 设置date,用于获取星期

        // 每7条数据生成一个数组
        if (count < 6) {
          count = date.getDay() === 0 ? 6 : date.getDay() - 1; // 一周中的某一天作为数组的下标,因为每月1号的周数不一样。0 是星期天

          arr[count] = {day: i + 1, week: date.getDay(), month: this.month + 1, year: this.year};
        }

        if (arr.length === 7 || i === lastDay - 1) {
          this.dateArr.push(arr); // 生成二维数组
          count = 0; // 置0,从新开始
          arr = [];
        }
      }

      /**
       *  表格第一行,计算上个月日期
       **/
      let firstWeek = null;
      let firstArr = this.dateArr[0];
      date.setDate(0);

      // 计算第一行数组还需要循环几次填充满
      for (let item of firstArr) {
        if (item) {
          firstWeek = item.week === 0 ? 6 : item.week - 1; // 计算还差几列没有数据
          break;
        }
      }

      let day = date.getDate();
      // 循环填充满第一列数组
      for (let i = firstWeek; i > 0; i--) {
        date.setDate(day--);
        firstArr[date.getDay() - 1] = {
          day: date.getDate(),
          week: date.getDay(),
          month: this.month,
          year: this.month === 0 ? this.year - 1 : this.year,
          class: 'not-current-month',
        };
      }

      /**
       *  表格最后一行,计算下个月日期
       **/
      let lastDate = new Date(this.year, this.month + 1);
      let lastWeek = null; // 获取最后一个周数
      let lastArr = this.dateArr[this.dateArr.length - 1];
      let lastDateArray = []; // 用于新增一行数组

      // 计算最后一行数组还需要循环几次填充满
      for (let i = 0; i < 7; i++) {
        if (typeof lastArr[i] === "undefined") {
          lastWeek = 7 - lastArr[i - 1].week; // 计算还差几列没有数据
          break;
        }
      }

      if (lastWeek > 0) {
        // 循环填充满最后一行数组
        for (let i = 0; i < lastWeek; i++) {
          lastDate.setDate(i + 1);
          lastArr[lastDate.getDay() === 0 ? 6 : lastDate.getDay() - 1] = {
            day: lastDate.getDate(),
            week: lastDate.getDay(),
            month: this.month + 2,
            year: this.month + 2 === 12 ? this.year + 1 : this.year,
            class: 'not-current-month',
          };
        }
      }

      // dateArr新增一行数组
      if (this.dateArr.length < 6) {
        for (let i = 0; i < 7; i++) {
          lastDate.setDate(lastWeek + i + 1);
          lastDateArray.push({
            day: lastDate.getDate(),
            week: lastDate.getDay(),
            class: 'not-current-month',
            month: this.month + 2,
            year: this.month + 2 === 12 ? this.year + 1 : this.year
          });
        }
      }
      if (lastDateArray.length > 0) {
        this.dateArr.push(lastDateArray);
      }
    },

    /**
     * 当天日期设置高亮
     **/
    setClassToday(val) {
      return val.month === (this.currentDate.getMonth() + 1) && val.day === this.day && val.year === this.currentDate.getFullYear();
    },

    /**
     * 日期点击事件
     **/
    handle(val) {
      this.activeIndex = val.day;
      // 点击灰色的日期,跳转月份
      if (val.class === 'not-current-month') {
        if (val.month > this.month) {
          this.nextMonth()
        } else {
          this.lastMonth()
        }
      }
    },

    /**
     * 上个月
     **/
    lastMonth() {
      this.month--;
      if (this.month === -1) {
        this.month = 11;
        this.year--;
      }
      this.$nextTick(() => {
        this.createDate()
      })
    },

    /**
     * 下个月
     **/
    nextMonth() {
      this.month++;
      if (this.month === 12) {
        this.month = 0;
        this.year++
      }
      this.$nextTick(() => {
        this.createDate()
      })
    },

    /**
     * 下一年
     **/
    nextYear() {
      this.year += 1;
      this.$nextTick(() => {
        this.createDate()
      })
    },

    /**
     * 上一年
     **/
    lastYear() {
      this.year -= 1;
      this.$nextTick(() => {
        this.createDate()
      })
    }
  }
}
</script>

<style lang="scss">
.vue-calendar {
  height: 800px;

  .calendar-icon {
    cursor: pointer;
  }

  .icon-arrow-right-year {
    margin-left: 20px;
  }

  .icon-arrow-left-year {
    margin-right: 20px;
  }

  .calendar-title {
    font-size: 20px;
    text-align: center;
    margin-bottom: 10px;

    & > div {
      padding: 10px;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
  }

  #table {
    height: 100%;
    width: 100%;
    border-collapse: collapse;

    thead {
      text-align: center;

      tr {
        border: 1px #e2e2e2 solid;
        height: 50px;
      }
    }

    tbody {
      text-align: center;

      .today {
        background: #fb0;
        color: #ffffff;
      }

      td {
        cursor: pointer;
        width: 210px;
        border: 1px #e2e2e2 solid;
        padding: 0;
        font-size: 20px;
        position: relative;

        &:not(.today):hover {
          background: #e2e2e2;
        }

        & > span {
          position: absolute;
          top: 0;
          left: 10px;
        }

        .not-current-month {
          color: #c0c4cc;
        }

        div {
          height: 75%;
          position: absolute;
          width: 100%;
          bottom: 0;

          span {
            font-size: 20px;
            position: absolute;
            left: 30px;
            top: -32px;
          }

          div {
            position: relative;
            /*left: 23px;*/
            width: 100%;
            height: 100%;
            overflow: auto;

            p {
              white-space: nowrap;
              overflow: hidden;
              text-overflow: ellipsis;
              margin-bottom: 10px;
              font-size: 13px;
            }
          }
        }
      }
    }
  }
}
</style>

22.vue实现点击全屏效果,可具体让某个容器全屏

<template>
  <div>
    <el-button type="primary" @click="screen">全屏</el-button>
    <div id="con_lf_top_div" style="background: #ffffff;">
      <el-button type="primary">111</el-button>
      <el-button type="primary">222</el-button>
    </div>
  </div>
</template>

<script>
export default {
  name: "indexAss",

  data() {
    return {
      fullscreen: false,
    };
  },

  methods: {
    screen() {
      // let element = document.documentElement; //设置后就是我们平时的整个页面全屏效果
      let element = document.getElementById("con_lf_top_div"); //设置后就是   id==con_lf_top_div 的容器全屏
      if (this.fullscreen) {
        if (document.exitFullscreen) {
          document.exitFullscreen();
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen();
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen();
        }
      } else {
        if (element.requestFullscreen) {
          element.requestFullscreen();
        } else if (element.webkitRequestFullScreen) {
          element.webkitRequestFullScreen();
        } else if (element.mozRequestFullScreen) {
          element.mozRequestFullScreen();
        } else if (element.msRequestFullscreen) {
          // IE11
          element.msRequestFullscreen();
        }
      }
      this.fullscreen = !this.fullscreen;
    },
  },
};
</script>

23.vue中实现滚动时钟效果

<template>
  <div class="wraper">
    <div class="column" :style="{transform: `translateY(${-lineHeight*index6}px)`}">
      <div class="num" v-for="(item, index) in arr6" :key="index">{{ item }}</div>
    </div>
 
    <div class="column" :style="{transform: `translateY(${-lineHeight*index5}px)`}">
      <div class="num" v-for="(item, index) in arr5" :key="index">{{ item }}</div>
    </div>
 
    <div>:</div>
 
    <div class="column" :style="{transform: `translateY(${-lineHeight*index4}px)`}">
      <div class="num" v-for="(item, index) in arr4" :key="index">{{ item }}</div>
    </div>
 
    <div class="column" :style="{transform: `translateY(${-lineHeight*index3}px)`}">
      <div class="num" v-for="(item, index) in arr3" :key="index">{{ item }}</div>
    </div>
 
    <div>:</div>
 
    <div class="column" :style="{transform: `translateY(${-lineHeight*index2}px)`}">
      <div class="num" v-for="(item, index) in arr2" :key="index">{{ item }}</div>
    </div>
 
    <div class="column" :style="{transform: `translateY(${-lineHeight*index1}px)`}">
      <div class="num" v-for="(item, index) in arr1" :key="index">{{ item }}</div>
    </div>
  </div>
</template>
 
<script>
  export default {
    data() {
      return {
        lineHeight: 64, //跟字体大小和wraper的高度相关!
        // 秒
        arr1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        index1: 0, //就是获取真实时间后的起始数字
        arr2: [0, 1, 2, 3, 4, 5],
        index2: 0,
        // 分
        arr3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        index3: 0,
        arr4: [0, 1, 2, 3, 4, 5],
        index4: 0,
        // 时
        arr5: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
        index5: 0,
        arr6: [0, 1, 2],
        index6: 0,
      }
    },
    created() {
      this.getTime()
    },
    watch: {
      index5(newVal) {
        // 超过24小时
        if (this.index6 === 2 && newVal===4) {
          console.log('out')
          for (let i=1; i<7; i++) {
            this[`index${i}`] = 0
          }
        }
      }
    },
    methods: {
      getTime() {
        const date = new Date()
        let hour = this.bu0(date.getHours())
        let minute = this.bu0(date.getMinutes())
        let second = this.bu0(date.getSeconds())
 
        // 测试用
        // let hour = ['1', '9']
        // let minute = ['5', '9']
        // let second = ['5', '5']
 
        // 秒
        this.index1 = +second[1]
        this.index2 = +second[0]
        // 分
        this.index3 = +minute[1]
        this.index4 = +minute[0]
        // 时
        this.index5 = +hour[1]
        this.index6 = +hour[0]
 
        this.turnSecond(this.arr1.length)
      },
      bu0(num) {
        let str
        if (num < 10) str = `0${num}`
        else str = `${num}`
        return str.split('')
      },
      turnSecond (length) {
        setInterval(()=> {
          if (this.index1 === length-1) {
            // console.log(1)
            // 通知前一位移动
            this.turnOther( 2, this.arr2.length)
            this.index1 = -1
          }
          this.index1++
        }, 1000)
      },
      turnOther(type, length) {
        // type代表第几组数列,例如2,就是从右往左第二列
        if (this[`index${type}`] === length-1) {
          // console.log(type)
          // 通知前一位移动
          let next = type+1
          this.turnOther( next, this[`arr${next}`].length)
 
          this[`index${type}`] = -1
        }
        this[`index${type}`]++
      }
    }
  }
</script>
 
<style scoped>
  .wraper {
    text-align: center;
    background: #ffffff;
    height: 64px;
    font-size: 48px;
    font-weight: bolder;
    letter-spacing: 7px;
    margin-top: 7px;
    display: flex;
    justify-content: center;
    overflow:hidden;
 
  }
  .column {
    transition: transform 300ms;
  }
  .num {
    height: 64px;
  }
</style>

24.vue表单校验

<template>
  <el-form ref="submitForm" :model="form" :rules="rules">
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username"></el-input>
    </el-form-item>
    <el-form-item label="密码" prop="password">
      <el-input type="password" v-model="form.password"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="validateUser">校验-全部字段</el-button>
      <el-button type="primary" @click="validateUsername">校验-用户名字段</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  data() {
    return {
      form: {
        username: "",
        password: "",
      },
      rules: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
        ],
        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
      },
    };
  },
  methods: {
    validateUser() {
      // 校验用户名和密码字段
      this.$refs.submitForm.validate((valid) => {
        if (valid) {
          this.$message.success("校验密码登录成功");
        }
      });
    },
    validateUsername() {
      // 校验用户名字段,不检验密码字段
      this.$refs.submitForm.validateField("username", (valid) => {
        if (!valid) {
          this.$message.success("不校验密码登录");
        }
      });
    },
  },
};
</script>

25.vue弹框和分页查询(高频使用)

<template>
  <div>
    <el-button type="primary" @click="handleOpenDialog">弹框-表格</el-button>
    <el-dialog title="提示" :visible.sync="dialogVisible" width="60%">
      <el-form :inline="true" class="demo-form-inline" size="medium">
        <el-form-item label="名称">
          <el-input v-model="searchInput" placeholder="请输入" @keyup.enter.native="handleQueryForm"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button size="small" type="primary" @click="handleQueryForm">查询</el-button>
        </el-form-item>
      </el-form>
      <!-- 普通表格 -->
      <el-table :data="dataTable" :row-key="getRowKey" border height="400px" style="width: 100%">
        <el-table-column label="序号" width="60" align="center">
          <template v-slot="scope">{{ ( pageParam.pageNum - 1) * pageParam.pageSize + (scope.$index + 1) }}</template>
        </el-table-column>
        <el-table-column label="编号" prop="merchantCode" align="center"></el-table-column>
        <el-table-column label="名称" prop="merchantName" align="center"></el-table-column>
      </el-table>
      <!-- 单选表格 -->
      <!-- <el-table ref="multiTable" :data="dataTable" @row-click="handleRowClick" :row-key="getRowKey" :highlight-current-row="true" border height="400px" style="width: 100%">
        <el-table-column width="60" align="center">
          <template slot-scope="scope">
            <el-radio v-model="radio" :label="scope.row.merchantId">
              <span></span>
            </el-radio>
          </template>
        </el-table-column>
        <el-table-column label="商家编号" prop="merchantCode" align="center"></el-table-column>
        <el-table-column label="商家名称" prop="merchantName" align="center"></el-table-column>
      </el-table> -->
      <!-- 多选表格 -->
      <!-- <el-table ref="multiTable" :data="dataTable" @selection-change="handleSelectionChange" :row-key="getRowKey" border height="400px" style="width: 100%">
        <el-table-column type="selection" width="60" :reserve-selection="true" align="center"></el-table-column>
        <el-table-column label="编号" prop="merchantCode" align="center"></el-table-column>
        <el-table-column label="名称" prop="merchantName" align="center"></el-table-column>
      </el-table> -->
      <div class="pagination">
        <el-pagination :current-page="pageParam.pageNum" :page-size="pageParam.pageSize" :page-sizes="[10, 20, 50, 100, 200, 1000]" :total="pageParam.total" background layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange"></el-pagination>
      </div>
      <span slot="footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    return {
      dialogVisible: false, // 弹框
      pageParam: {
        pageNum: 1,
        pageSize: 10,
        total: 0,
      },
      dataTable: [], // 表格的数据
      selectedRows: [], // 选中的行
      searchInput: "", // 输入框查询条件
      radio: null, // 单选按钮
    };
  },
  created() {},
  methods: {
    // 点击按钮弹框
    handleOpenDialog() {
      this.initQuery();
      this.dialogVisible = true;
    },
    initQuery() {
      // 打开弹窗时重置radio状态
      // this.radio = null;
      // 打开弹窗时重置勾选状态
      // this.$refs.multiTable && this.$refs.multiTable.clearSelection();
      this.pageParam.pageNum = 1;
      this.handleQueryForm();
    },
    // 查询表格
    handleQueryForm() {
      let requestData = {
        pageNum: this.pageParam.pageNum,
        pageSize: this.pageParam.pageSize,
        param: {
          merchantName: this.searchInput,
        },
      };
      this.$http
        .requestPost({
          url: "/customer/merchant/page",
          param: requestData,
        })
        .then((res) => {
          this.dataTable = res.data.list;
          this.pageParam.total = res.data.total;
        })
        .catch((err) => {
          console.log(err);
        });
    },
    // pageSize改变时会触发
    handleSizeChange(size) {
      this.pageParam.pageNum = 1;
      this.pageParam.pageSize = size;
      this.handleQueryForm();
    },
    // currentPage改变时会触发
    handleCurrentChange(page) {
      this.pageParam.pageNum = page;
      this.handleQueryForm();
    },
    // 行数据的Key,用来优化Table的渲染
    getRowKey(row) {
      return row.merchantId;
    },
    // 当某一行被点击时会触发该事件
    handleRowClick(row) {
      // 更新选中状态
      this.radio = row.merchantId;
      this.selectedRows = row;
      console.log("单选======", this.selectedRows);
    },
    // 当选择项发生变化时会触发该事件
    handleSelectionChange(row) {
      this.selectedRows = row;
      console.log("多选======", this.selectedRows);
    },
  },
};
</script>

<style lang="scss" scoped>
</style>

26.vue中导出Execl

async handleReport(){
  var res;
  let param = {
    year: this.inquire.year,
    month: this.inquire.month
  }
  // res将文件转成Blob二进制流
  res = await zyExportDetails(param, "post")
  try{
    this.$public.downloadFile(res);
    this.$message.success("导出成功!");
    }catch(error){
      console.log(error)
      this.$message.error("导出失败!");
    }
},
// 流文件下载
function downloadFile(res) {
    var blob = res.data;
    console.log(res)
    // FileReader主要用于将文件内容读入内存
    var reader = new FileReader();
    console.log(reader)
    reader.readAsDataURL(blob);
    // onload当读取操作成功完成时调用
    reader.onload = function(e) {
        var a = document.createElement('a');
        // 获取文件名fileName
        var fileName = res.headers.filename;
        a.download = fileName;
        a.href = e.target.result;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }
}

export default {
    downloadFile,
}

27.用axios跨域调用的接口

<template>
  <div class="container">
    <el-form :inline="true" size="medium">
      <el-form-item label="名称">
        <el-input v-model="searchInput" placeholder="请输入"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button size="small" type="primary" @click="handleQueryForm">查询</el-button>
      </el-form-item>
    </el-form>
    <!-- 普通表格 -->
    <el-table :data="dataTable" border height="400px" style="width: 100%">
      <el-table-column label="序号" width="60" align="center" type="index"></el-table-column>
      <el-table-column label="编号" prop="merchantCode" align="center"></el-table-column>
      <el-table-column label="名称" prop="merchantName" align="center"></el-table-column>
    </el-table>
    <div class="pagination">
      <el-pagination :current-page="pageParam.pageNum" :page-size="pageParam.pageSize" :page-sizes="[10, 20, 50, 100, 200, 1000]" :total="pageParam.total" background layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange"></el-pagination>
    </div>
  </div>
</template>
 
<script>
import axios from "axios";
export default {
  data() {
    return {
      pageParam: {
        pageNum: 1,
        pageSize: 10,
        total: 0,
      },
      dataTable: [], // 表格的数据
      searchInput: "", // 输入框查询条件
    };
  },
  created() {
    this.initQuery();
  },
  methods: {
    initQuery() {
      this.pageParam.pageNum = 1;
      this.handleQueryForm();
    },
    // 查询表格
    handleQueryForm() {
      let requestData = {
        pageNum: this.pageParam.pageNum,
        pageSize: this.pageParam.pageSize,
        param: {
          merchantName: this.searchInput,
        },
      };
      axios({
        method: "post",
        url: "http://dev-manager-api.638yipin.uyaoku.com/management/customer/merchant/page",
        data: requestData,
        responseType: "application/json;charset=utf-8",
        headers: { Authorization: "d817b9e319fe46a189b642c137ea196b" },
      }).then((res) => {
        this.dataTable = res.data.data.list;
        this.pageParam.total = res.data.data.total;
      });
    },
    // pageSize改变时会触发
    handleSizeChange(size) {
      this.pageParam.pageNum = 1;
      this.pageParam.pageSize = size;
      this.handleQueryForm();
    },
    // currentPage改变时会触发
    handleCurrentChange(page) {
      this.pageParam.pageNum = page;
      this.handleQueryForm();
    },
  },
};
</script>
 
<style lang="scss" scoped>
.container {
  padding: 20px;
}
.pagination {
  margin-top: 10px;
  text-align: end;
}
</style>

28.vue封装普通表格+分页+操作栏(高频)

<template>
  <div class="container">
    <el-form :inline="true" size="medium">
      <el-form-item label="名称">
        <el-input v-model="searchInput" placeholder="请输入"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button size="small" type="primary" @click="fetchData">查询</el-button>
      </el-form-item>
    </el-form>
    <!-- 普通表格 -->
    <TableList :tableData="dataTable" :tableHeader="tableHeader" :pageNum="pageParam.pageNum" :total="pageParam.total" :isShowPage="true" :isShowIndex="true">
      <!-- #operation 插槽对应的名称(列配置项声明的插槽名称) data插槽返回的行数据  -->
      <template #operation='{data}'>
        <el-button size="mini" icon="el-icon-edit" @click="handleEdit(data)" type="success">编辑</el-button>
      </template>
    </TableList>
  </div>
</template>
 
<script>
import axios from "axios";
import TableList from "@/components/TableList";
export default {
  components: {
    TableList,
  },
  data() {
    return {
      pageParam: {
        pageNum: 1,
        pageSize: 10,
        total: 0,
      },
      tableHeader: [
        {
          label: "编号",
          prop: "merchantCode",
        },
        {
          label: "名称",
          prop: "merchantName",
        },
        {
          label: "操作",
          type: "slot", //slot插槽
          slotName: "operation", //插槽名称(html表格组件内声明此插槽)
        },
      ],
      dataTable: [], // 表格的数据
      searchInput: "", // 输入框查询条件
    };
  },
  created() {
    this.initQuery();
  },
  methods: {
    initQuery() {
      this.pageParam.pageNum = 1;
      this.fetchData();
    },
    // 查询表格
    fetchData() {
      let requestData = {
        pageNum: this.pageParam.pageNum,
        pageSize: this.pageParam.pageSize,
        param: {
          merchantName: this.searchInput,
        },
      };
      axios({
        method: "post",
        url: "http://dev-manager-api.638yipin.uyaoku.com/management/customer/merchant/page",
        data: requestData,
        responseType: "application/json;charset=utf-8",
        headers: { Authorization: "53ea34b9a74c4660b4a982f24699f243" },
      }).then((res) => {
        this.dataTable = res.data.data.list;
        this.pageParam.total = res.data.data.total;
      });
    },
    //表格每页数量改变触发
    setSize(size) {
      this.pageParam.pageNum = 1;
      this.pageParam.pageSize = size;
      this.fetchData();
    },
    //表格当前页数改变触发
    setPage(page) {
      this.pageParam.pageNum = page;
      this.fetchData();
    },
    // 编辑按钮
    handleEdit(row) {
      console.log("row======", row);
    },
  },
};
</script>
 
<style lang="scss" scoped>
.container {
  padding: 20px;
}
</style>
<template>
  <div id="tables">
    <!-- table 表格 -->
    <el-table id="ou" size="mini" :cell-style="{ textAlign: 'center' }" :header-cell-style="{ background: '#4e9aef', color: '#fff', textAlign: 'center' }" :border="true" :data="tableData" ref="multipleTable" style="width: 100%" max-height="400px">
      <el-table-column v-if="isShowIndex" type="index" label="序号" width="50"></el-table-column>
      <el-table-column v-for="(item, index) in tableHeader" :key="index" :prop="item.prop" :label="item.label" :min-width="item.width">
        <template v-if="item.type === 'slot'" #default="{ row }">
          <slot :name="item.slotName" :data="row"></slot>
        </template>
      </el-table-column>
    </el-table>
    <!-- table end -->
    <!-- 分页器 -->
    <div class="pagination" v-if="isShowPage">
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="10" layout="total, sizes, prev, pager, next, jumper" :total="total">
      </el-pagination>
    </div>
  </div>
</template>
 
<script>
export default {
  props: {
    // 表格数据
    tableData: {
      type: Array,
      default: () => [],
    },
    // 表格头部
    tableHeader: {
      type: Array,
      default: () => [],
    },
    // 分页器页数
    pageNum: {
      type: Number,
      default: 0,
    },
    // 总条数
    total: {
      type: Number,
      default: 0,
    },
    // 是否显示分页器
    isShowPage: {
      type: Boolean,
      default: true,
    },
    // 是否显示表格序号
    isShowIndex: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {};
  },
  methods: {
    //每页条数
    handleSizeChange(val) {
      this.$parent.setSize(val);
    },
    //当前页数/页数切换
    handleCurrentChange(val) {
      this.$parent.setPage(val);
    },
  },
};
</script>
 
<style scoped lang="scss">
.pagination {
  margin-top: 10px;
  text-align: end;
}
</style>

29.uniapp刷新前一个页面的数据

setTimeout(() => {
	let pages = getCurrentPages(); //获取所有页面栈实例列表
	let prevPage = pages[pages.length - 2]; //上一页页面实例
	prevPage.$vm.initData(); //$vm后面是需要调用的上一个页面的方法和对象
	uni.navigateBack({
		delta:1
	})
}, 1500)

30.uniapp在小程序和app使用视频播放组件

// 小程序
<!-- enable-play-gesture:双击暂停或播放   show-mute-btn:静音按钮 -->
<!-- #ifdef MP-WEIXIN  -->
<video style="width:100%;height:100%;" :src="videoAddress" :show-mute-btn="true" :enable-play-gesture="true" title="名医科普"></video>
<!-- #endif -->

// app
<!-- #ifdef APP-PLUS -->
<view v-if="videoAddress">
  <!-- 处理video在APP中层级过高问题 -->
  <view style="width:100%;height:100%;" v-html="videoContent"></view>
</view>
<!-- #endif -->

data(){
    return{
        videoAddress:'',  // 小程序:字段是.map4格式
        videoContent: '',  // app:video标签转html  
    }
}

methods:{
    getVideoDetail(){
        this.videoAddress = res.videoAddress;
	    this.videoContent = res.videoAddress ? `<video src="${res.videoAddress}" controls style="width:100%;height:400px;z-index: 1;" mode="aspectFill"></video>` : '';
    }
}    

31.uniapp自定义弹框

<u-popup v-model="modelShow" :maskCloseAble="false" mode="center" width="540rpx" border-radius="20">
	<view style="padding: 20rpx;">
    <view style="font-size: 32rpx;text-align: center;font-weight: bold;">服务协议和隐私政策</view>
    <view style="font-size: 28rpx;margin-top: 20rpx;">
    请你务必审慎阅读,充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了向你提供即时通讯、内容分享等服务,我们需要收集你的设备信息、操作日志等个人信息。你可以在“设置”中查看、变更、删除个人信息并管理你的授权。<br/>你可阅读
    <text style="color:#2979ff;">《服务协议》</text>
    和
    <text style="color:#2979ff;">《隐私政策》</text>
    了解详细信息。如你同意,请点击“同意”开始接收我们的服务。
    </view>
    <view style="display: flex;justify-content: space-between;padding: 20rpx;font-size: 28rpx;margin: 10rpx 0 10rpx 0;">
      <view style="width: 200rpx;height: 60rpx;border-radius: 32rpx;text-align: center;color: #6d9fe8;background: #fff;border: 1px solid #6d9fe8;line-height: 60rpx;">暂不使用</view>
      <view style="width: 200rpx;height: 60rpx;border-radius: 32rpx;text-align: center;color: #fff;background: #6d9fe8;line-height: 60rpx;">同意</view>
    </view>
    </view>
</u-popup>

32.响应式布局-媒体查询

// 1.设置 meta 标签
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

// 2.使用 @media 设置样式
// 屏幕大于 1024px 或小于 1440px 时应用该样式
@media screen and (min-width: 1024px) and (max-width: 1440px) { 
  ...
}

// 3.依据要求并结合屏幕尺寸设置布局分界点
// 屏幕大于 1440px 时应用该样式
@media screen and (min-width: 1441px) { 
  ...
}
// 屏幕大于 1024px 或小于 1440px 时应用该样式
@media screen and (min-width: 1024px) and (max-width: 1440px) { 
  ...
}

// https://www.strerr.com/screen.html  手机屏幕尺寸大全
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <!-- 浏览器需要下面一行才能正确响应响应式布局 -->
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />

    <style>
      /* 公共样式 */
      body {
        margin: 0;
        padding: 0;
      }

      /* PC 端样式 */
      @media (min-width: 768px) {
        .box-pc {
          .img {
            width: 100%;
            height: 100%;
          }
        }

        .box-mobile {
          display: none;
        }
      }

      /* 移动端样式 */
      @media (max-width: 767px) {
        .box-pc {
          display: none;
        }

        .box-mobile {
          .img {
            width: 100%;
            height: 100%;
          }
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <!-- pc端页面 -->
      <div class="box-pc">
        <div class="box1">
          <img class="img" src="../images/banner1.jpg" alt="..." />
        </div>
      </div>
      <!-- 移动端页面 -->
      <div class="box-mobile">
        <div class="box1">
          <img class="img" src="../images/bannerx1.png" alt="..." />
        </div>
      </div>
    </div>
  </body>
</html>

33.微信小程序添加隐私协议弹框

<template>
	<view class="content">
		<view>主页面</view>
		<!-- 隐私协议 -->
		<!-- #ifdef MP-WEIXIN -->
		<PrivacyPopup ref="privacyComponent"></PrivacyPopup>
		<!-- #endif -->
	</view>
</template>

<script>
// #ifdef MP-WEIXIN
import PrivacyPopup from '@/components/privacy-popup/privacy-popup.vue';
// #endif
export default {
	components: {
		// #ifdef MP-WEIXIN
		PrivacyPopup
		// #endif
	},
	data() {
		
	},
};
</script>
<template>
	<view v-if="showPrivacy" class="privacy">
		<view class="content">
			<view class="title">隐私保护指引</view>
			<view class="des">
				在使用当前小程序服务之前,请仔细阅读
				<text class="link" @tap="openPrivacyContract">{{privacyContractName}}</text>
				。如你同意{{privacyContractName}},请点击“同意”开始使用。
			</view>
			<view class="btns">
				<button class="item reject" @tap="exitMiniProgram">拒绝</button>
				<button id="agree-btn" class="item agree" open-type="agreePrivacyAuthorization"
					@agreeprivacyauthorization="handleAgreePrivacyAuthorization">同意</button>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'PrivacyPopup',
		data() {
			return {
				privacyContractName: '',
				showPrivacy: false,
				// isRead: false,
				resolvePrivacyAuthorization: null,
			};
		},
		mounted() {
			if (wx.onNeedPrivacyAuthorization) {
				wx.onNeedPrivacyAuthorization((resolve) => {
					this.resolvePrivacyAuthorization = resolve;
				});
			}

			if (wx.getPrivacySetting) {
				wx.getPrivacySetting({
					success: (res) => {
						console.log(res, 'getPrivacySetting');
						if (res.needAuthorization) {
							this.privacyContractName = res.privacyContractName;
							this.showPrivacy = true;
						}
					},
				});
			}
		},

		methods: {
			openPrivacyContract() {
				wx.openPrivacyContract({
					success: () => {
						// this.isRead = true;
					},
					fail: () => {
						uni.showToast({
							title: '遇到错误',
							icon: 'error',
						});
					},
				});
			},
			exitMiniProgram() {

				wx.exitMiniProgram();

			},
			handleAgreePrivacyAuthorization() {
				// if (this.isRead) {
					this.showPrivacy = false;
					if (typeof this.resolvePrivacyAuthorization === 'function') {
						this.resolvePrivacyAuthorization({
							buttonId: 'agree-btn',
							event: 'agree',
						});
					}
				// } else {
				// 	uni.showToast({
				// 		title: '请先阅读隐私授权协议',
				// 		icon: 'error',
				// 	});
				// }
			},
		},
	};
</script>

<style>
	.privacy {
		position: fixed;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		background: rgba(0, 0, 0, .5);
		z-index: 9999999;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.content {
		width: 632rpx;
		padding: 48rpx;
		box-sizing: border-box;
		background: #fff;
		border-radius: 16rpx;
	}

	.content .title {
		text-align: center;
		color: #333;
		font-weight: bold;
		font-size: 32rpx;
	}

	.content .des {
		font-size: 26rpx;
		color: #666;
		margin-top: 40rpx;
		text-align: justify;
		line-height: 1.6;
	}

	.content .des .link {
		color: #32AFB2;
		text-decoration: underline;
	}

	.btns {
		margin-top: 48rpx;
		display: flex;
	}

	.btns .item {
		width: 244rpx;
		height: 80rpx;
		overflow: visible;
		display: flex;
		align-items: center;
		margin: 0 12px;
		justify-content: center;
		/* border-radius: 16rpx; */
		box-sizing: border-box;
		border: none !important;
	}

	.btns .reject {
		background: #f4f4f5;
		color: #909399;
	}

	.btns .agree {
		background: #32AFB2;
		color: #fff;
	}
</style>
manifest.json文件添加如下

"mp-weixin" : {
    "__usePrivacyCheck__" : true
},

34.uniapp添加隐私协议弹框

// 打开项目的manifest.json文件,切换到“App启动界面配置”,在“Android启动界面样式”中勾选“使用原生隐私政策提示框”
// 勾选后会在项目中自动添加androidPrivacy.json文件

{
    "version" : "1",
    "prompt" : "template",
    "title" : "用户协议和隐私政策",
    "message" : "  请你务必审慎阅读、充分理解“用户协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"static\\htmls\\userAgreement.html?type=1\" >《用户协议》</a>和<a href=\"static\\htmls\\userAgreement.html?type=2\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
    "buttonAccept" : "同意",
    "buttonRefuse" : "暂不使用",
    "hrefLoader" : "system|default",
    "second" : {
        "title" : "确认提示",
        "message" : "  进入应用前,你需先同意<a href=\"static\\htmls\\userAgreement.html?type=1\">《用户协议》</a>和<a href=\"static\\htmls\\userAgreement.html?type=2\">《隐私政策》</a>,否则将退出应用。",
        "buttonAccept" : "同意并继续",
        "buttonRefuse" : "退出应用"
    },
    "styles" : {
        "backgroundColor" : "#fff",
        "borderRadius" : "5px",
        "title" : {
            "color" : "#000"
        },
        "buttonAccept" : {
            "color" : "#fff"
        },
        "buttonRefuse" : {
            "color" : "#cccccc"
        }
    }
}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>用户协议&隐私政策</title>
        <meta
            content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"
            name="viewport" />
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            #title {
                width: 100vw;
                height: 50px;
                /* background-color: #FFFFFF; */
                display: flex;
                align-items: center;
                justify-content: center;
            }

            #text {
                border: none;
                width: 100vw;
                height: 100vh;
                box-sizing: border-box;
                padding: 20px;
                background-color: #ffffff;
                color: #333333;
                font-size: 14px;
                line-height: 20px;
            }
        </style>
    </head>
    <body>
        <div id="title"></div>
        <div disabled="true" id="text"></div>
    </body>
</html>
<script type="text/javascript">
    const url = "https://user-api.638yipin.com/mine/setting/protocol/";

    let type = 1;

    function parseQueryString(url) {
		const urlKey = url.split('?')[1]
		const objKeyValue = {}
		if (!urlKey) return objKeyValue
		const urlObj = urlKey.split('&')
		for (let i = 0; i < urlObj.length; i++) {
			objKeyValue[urlObj[i].split('=')[0]] = decodeURI(urlObj[i].split('=')[1])
		}
		return objKeyValue
	}

	const urlParams = parseQueryString(window.location.href)
    type = urlParams.type;
    const httpRequest = new XMLHttpRequest(); //第一步:创建需要的对象
    httpRequest.open("GET", url + type, true); //第二步:打开连接/***发送json格式文件必须设置请求头 ;如下 - */
    // httpRequest.setRequestHeader('Content-type',
    // 	'application/json') //设置请求头 注:post方式必须设置请求头(在建立连接后设置请求头
    httpRequest.send(); //发送请求
    // 获取数据后的处理程序
    httpRequest.onreadystatechange = function () {
        //请求后的回调接口,可将请求成功后要执行的程序写在其中
        // console.log(httpRequest);
        if (httpRequest.readyState == 4 && httpRequest.status == 200) {
            //验证请求是否发送成功
            const res = JSON.parse(httpRequest.responseText); //获取到服务端返回的数据
            // console.log(res);
            if (res.message == "OK") {
                document.getElementById("text").innerHTML =
                    res.data.protocolContent;
                document.getElementById("title").innerHTML =
                    res.data.protocolName;
            }
        }
    };
</script>

35.处理video在App中层级过高的问题

<!-- #ifdef MP-WEIXIN -->
<!-- enable-play-gesture:双击暂停或播放   show-mute-btn:静音按钮 -->
<view class="video_wrapper">
	<video style="width:100%;height:400rpx;" :poster="content.messageTopCover" object-fit="cover" :src="content.topVideo" :show-mute-btn="true" :enable-play-gesture="true"></video>
</view>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<view class="video_wrapper">
<!-- 处理video在APP中层级过高问题 -->
	<view class="video-box" v-html="content.topVideoApp"></view>
</view>
<!-- #endif -->

this.content.topVideoApp = `<video src="${topVideo}" poster="${messageTopCover}" object-fit="cover" controls width="100%" height="720rpx" style="width:100%;height:100%; z-index: 1;" mode="aspectFill"></video>`;

36.vue上传图片

<template>
  <div>
    <el-form size="small" :inline="true" label-width="68px">
      <el-form-item label="上传图片" style="width:40%">
        <d-upload upload-type="picture-card" :file-list="bannerImage" :limit="1" accept=".gif,.jpeg,.jpg,.png" @uploadSuccess="uploadSuccess" @removeFile="removeFile">
          <div class="tip-box">
            <i class="el-icon-plus" />
            <span slot="tip" class="tip">上传图片</span>
          </div>
        </d-upload>
        <div style="color: #ccc;font-size: 12px;">建议图片比例1:1</div>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
import dUpload from "@/components/d-upload/index";

export default {
  components: {
    dUpload,
  },
  data() {
    return {
      backgroundUrl: '',
      bannerImage: [],
    }
  },
  created() {
    this.getDetail()
  },
  methods: {
    async getDetail() {
      const { code, data } = await getContactUsDetail()
      if (code === 200) {
        // ...
        this.backgroundUrl = data.backgroundUrl
        if (this.backgroundUrl) {
          this.bannerImage.push({ name: this.backgroundUrl.substring(this.backgroundUrl.lastIndexOf("/") + 1), url: this.backgroundUrl })
        }
      }
    },
    uploadSuccess(res, file, fileList) {
      this.backgroundUrl = res.url
      this.bannerImage.push({ name: res.originalFilename, url: res.url })
    },
    removeFile(file, fileList) {
      this.bannerImage = []
    },
  }
}
</script>
<style lang="scss" scoped>
.tip-box {
  position: relative;
  .tip {
    position: absolute;
    top: 26px;
    left: 34%;
    font-size: 12px;
    color: #ccc;
  }
}
/*去除upload组件过渡效果*/
::v-deep .el-upload-list__item {
  transition: none !important;
}
</style>
<template>
  <div class="d-upload">
    <el-upload
      class="upload-demo"
      :class="{styleA:fileList.length === 0,styleB:fileList.length === 1}"
      :action="action"
      :list-type="uploadType"
      :before-upload="beforeUpload"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :before-remove="beforeRemove"
      :multiple="multiple"
      :limit="limit"
      :on-exceed="handleExceed"
      :drag="drag"
      :accept="accept"
      :file-list="fileList"
      :data="param"
      :on-success="uploadSuccess"
      :headers="headers"
      :show-file-list="showFileList"
      :on-error="uploadError"
      :auto-upload="autoUpload"
      :on-change="handleChange"
    >
      <slot />
      <div v-if="tip" slot="tip" class="el-upload__tip">{{ tip }}</div>
    </el-upload>

    <!-- 弹框 -->
    <el-dialog
      title="图片预览"
      :visible.sync="dialogVisible"
      :modal="false"
      width="800"
      append-to-body
    >
      <div class="Popout_content">
        <div v-if="fileType === 'image'" class="image-box">
          <img :src="preview" alt="">
        </div>
        <div v-else>
          <video id="video" autoplay :src="preview" controls="controls" width="100%"> 视频描述</video>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">关 闭</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { getToken } from "@/utils/auth";

export default {
    name: 'DUpload',
    props: {
        action: {
            type: String,
            default: process.env.VUE_APP_BASE_API + '/file/upload'
        },
        param: {
            type: Object,
            default: () => {
            }
        },
        multiple: {
            // 是否多选
            type: Boolean,
            default: false
        },
        showFileList: {
            // 是否显示文件列表
            type: Boolean,
            default: true
        },
        drag: {
            // 是否拖拽上传
            type: Boolean,
            default: false
        },
        limit: {
            // 允许上传数量
            type: Number,
            default: 1
        },
        accept: {
            // 接受上传文件类型
            type: String,
            default: ''
        },
        fileList: {
            // 上传文件列表
            type: Array,
            default: () => []
        },
        size: {
            // 限制上传文件大小
            type: Number,
            default: 10
        },
        uploadType: {
            type: String,
            default: ''
        },
        tip: {
            type: String,
            default: ''
        },
        headers: {
            type: Object,
            default: () => {
                return {
                    'Authorization': "Bearer " + getToken()
                }
            }
        },
        autoUpload: {
            type: Boolean,
            default: true
        }

    },
    data() {
        return {
            preview: '', // 预览图片路径
            fileType: '', // 文件类型
            dialogVisible: false, // 预览弹窗
            removeB: true
        }
    },
    methods: {
        // action和headers需要同步修改
        handleChange(file,fileList){
            console.log(file)
            this.removeB = true
            const fileSize = file.size / 1024 / 1024 > this.size
            if (fileSize) {
                this.$message({
                    message: '上传文件的大小不能大于' + this.size + 'M',
                    type: 'warning'
                })
                this.removeB = false
                return false
            } else {
                this.$emit('handleChange', file, fileList)
                this.removeB = false
            }
        },
        // 文件上传之前
        beforeUpload(file) {
            this.removeB = true
            const fileSize = file.size / 1024 / 1024 > this.size
            if (fileSize) {
                this.$message({
                    message: '上传文件的大小不能大于' + this.size + 'M',
                    type: 'warning'
                })
                this.removeB = false
                return false
            } else {
                this.$emit('beforeUpload', file)
                this.removeB = false
            }
        },
        // 删除文件
        handleRemove(file, fileList) {
            this.$emit('removeFile', file, fileList)
        },
        // 预览图片
        handlePreview(file) {
            if (file.raw) {
                console.log('file.raw',file.raw)
                const type = file.raw.type.split('/')
                this.fileType = type[0]

                const reader = new FileReader()
                reader.readAsDataURL(file.raw)
                reader.onload = (e) => {
                    console.log(e)
                    // _base64
                    this.preview = reader.result
                    console.log('reader.result',reader.result)
                }

            } else if (file.url) {
                const imgT = ['.gif', '.jpeg', '.jpg', '.png', '.PNG']
                const videoT = ['.wmv', '.asf', '.asx', '.rm', '.rmvb', '.rmvb', '.mpg', '.mpeg', '.mpe', '.3gp', '.mov', '.mp4', '.m4v']
                const ind = file.url.lastIndexOf('.')
                const type = file.url.slice(ind)
                if (imgT.indexOf(type) !== -1) {
                    this.fileType = 'image'
                } else if (videoT.indexOf(type) !== -1) {
                    this.fileType = 'video'
                }
                this.preview = file.url
            }

            this.dialogVisible = true
        },
        handleExceed(files, fileList) {
            this.$message.warning(
                `当前限制选择 ${this.limit} 个文件`
            )
        },
        // 删除文件之前
        beforeRemove(file, fileList) {
            if (this.removeB) {
                return this.$confirm(`确定移除 ${file.name}?`)
            } else {
                return true
            }
        },
        // 上传成功
        uploadSuccess(response, file, fileList) {
            if(response.code == 200) {
                this.$emit('uploadSuccess', response, file, fileList)
            }else{
                this.$emit('uploadError', response, file, fileList)
            }
        },
        uploadError(err, file, fileList) {
            console.log(err)
        }
    }
}
</script>

<style lang="scss">
.image-box {
    img {
        width: 100%;
        height: 100%;
    }
}
// 核心代码添加:class="{styleA:file_list.length === 0,styleB:file_list.length === 1}"(注意style标签不能带有scoped)
.styleA .el-upload--picture-card{
    width:120px;
    height:120px;
    line-height:120px;
}
.styleB .el-upload--picture-card{
    display:none;   
}

</style>

36.vue倒计时(设置为Vue响应式属性)

<!-- 倒计时 -->
<text v-if="item.orderStatus == 'pending_payment'">
{{ item.temp_time }}
<!-- {{ pending_payment_time }} -->
</text>

getLiveTime() {
let that = this;
this.timer2 = null;
that.orderListx.forEach((order) => {
	if (order.orderStatus == 'pending_payment') {
	let time = String(order.orderTime).replace("-", "/").replace("-", "/");
	let endTime = Date.parse(time); // 提交订单的时间
	console.log("endTime00 = ", endTime);
	var now = new Date(); // 当前时间
	var end = new Date(endTime + 15 * 60 * 1000); // 提交订单的时间,变成毫秒
	// 两个时间相减,得到的是毫秒, 变成秒
	let result = parseInt((end - now) / 1000);
	if (result > 0) {
		let second1 = Math.floor(result % 60); // 计算秒,取余
		let minite1 = Math.floor((result / 60) % 60); // 计算分,换算有多少分,取余
		this.$set(order, 'temp_time', '剩余时间' + minite1 + '分' + second1 + '秒'); // 将属性设置为Vue响应式属性
		// that.pending_payment_time = '剩余时间' + minite1 + '分' + second1 + '秒'; // 单个倒计时,直接赋值
		this.timer2 = setInterval(function() {
		result = result - 1;
		let second = Math.floor(result % 60); // 计算秒,取余
		let minite = Math.floor((result / 60) % 60); // 计算分,换算有多少分,取余
		that.$set(order, 'temp_time', '剩余时间' + minite + '分' + second + '秒'); // 将属性设置为Vue响应式属性
		// that.pending_payment_time  = '剩余时间' + minite + '分' + second + '秒';
		that.newResult = result;
		}, 1000)
	} else {
		clearInterval(this.timer2);
		orderCancel(order.orderId).then(res => {});
		this.arr(); // 刷新接口
		this.$set(order, 'temp_time', ''); // 将属性设置为Vue响应式属性
	}
	}
})
},
this.timer2 = setInterval(function() {
	result = result - 1;
	if (result <= 0) { // 如果剩余时间小于等于0,则设置为-1
	result = -1;
	that.$set(order, 'temp_time', '');
	return
	}
	let second = Math.floor(result % 60); // 计算秒,取余
	let minute = Math.floor((result / 60) % 60); // 计算分,换算有多少分,取余
	that.$set(order, 'temp_time', '剩余时间' + minute + '分' + second + '秒'); // 将属性设置为Vue响应式属性
	that.newResult = result;
}, 1000)

37.uniapp实现一个简单的上(固定)中(滚动)下(固定)布局

<template>
	<view class="page">
		<view class='wraper'>
			<view class='header'>header</view>
			<view class='main'>
				<scroll-view class='main-scroll' scroll-y style="height: 100%">
					<view class='main-list'>
						<view class='card' v-for="(item,index) in cardList">
							<view class='card-box'></view>
							<view>{{item.name}}</view>
							<view class='card-content'>{{item.content}}</view>
						</view>
					</view>
				</scroll-view>
			</view>
			<view class='footer'>footer</view>
		</view>
	</view>
</template>
 
<script>
	export default {
		data() {
			return {
				cardList: [{
					name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, {
				 name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, {
					name: 'aa',
					content: 'bb'
				}, ]
			}
		},
		methods: {
 
		}
	}
</script>
 
<style>
	.page {
		width: 100%;
		height: 100%;
	}
	.wraper {
		display: flex;
		flex-direction: column;
		width: 100%;
		height: 100%;
	}
 
	.header {
		background: rgb(8, 117, 94);
		color: #fff;
		line-height: 100rpx;
		flex: 0 0 100rpx;
		/* 不放大不缩小固定100rpx */
	}
 
	.main {
		height: 690rpx;
		position: relative;
	}
 
	.main-scroll {
		position: absolute;
		left: 0;
		right: 0;
		top: 0;
		bottom: 0;
	}
 
	.main-list {
		display: flex;
		flex-wrap: wrap;
		justify-content: space-between;
		margin-left: 2vw;
		margin-right: 2vw;
	}
 
	.card {
		width: 47vw;
		margin-top: 10rpx;
		margin-bottom: 10rpx;
	}
 
	.card-box {
		width: 100%;
		height: 200rpx;
		border-radius: 6rpx;
		background: #ccc;
	}
 
	.card-content {
		color: blue;
	}
 
	.footer {
		background: rgb(8, 117, 94);
		color: #fff;
		line-height: 100rpx;
		flex: 0 0 100rpx;
		
	}
</style>

38.uniapp上传视频或图片(手写原生)

<template>
	<!-- 上传视频或者图片 -->
	<view class="up-page">
		<!--图片-->
		<view class="show-box" v-for="(item,index) in imageList" :key="index">
			<image class="full" :src="item" :data-src="image" @tap="previewImage(item)">
			</image>
			<view class="delect-icon" @tap="delect(index)">
				<image class="full" :src="clearIcon" mode=""></image>
				<!--图片或者视频上传成功了,点击右上角叉号-->
			</view>
		</view>
		<!--视频-->
		<view class="show-box" v-for="(item1, index1) in videoList" :key="index1">
			<video class="full" :src="item1"></video>
			<view  class="delect-icon" @tap="delectVideo(index1)">
				<image class="full" :src="clearIcon" mode=""></image>
			</view>
		</view>
		<view v-if="VideoOfImagesShow" @tap="chooseVideoImage" class="box-mode">
			<image class="full" :src="selectfile" mode=""></image>
			<!-- 上传图标 懒得写,整个的图 -->
		</view>
	</view>
 
</template>
<script>
	var sourceType = [
		['camera'],
		['album'],
		['camera', 'album']
	];
	export default {
		data() {
			return {
				// 图标
				clearIcon: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCAwaDE2YTQgNCAwIDAgMSA0IDR2MTZINGE0IDQgMCAwIDEtNC00VjB6IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXIpIiBmaWxsLW9wYWNpdHk9Ii45OCIgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfYikiLz48cGF0aCBkPSJNMTAuOTQgOS45OTlsMi44NjMtMi44NTdhLjY2OS42NjkgMCAxIDAtLjk0Ni0uOTQ2TDEwIDkuMDYgNy4xNDMgNi4xOTZhLjY2OS42NjkgMCAwIDAtLjk0Ni45NDZsMi44NjQgMi44NTctMi44NjQgMi44NTdhLjY2Ni42NjYgMCAwIDAgLjIxNyAxLjA5Mi42NjQuNjY0IDAgMCAwIC43MjktLjE0NkwxMCAxMC45MzhsMi44NTcgMi44NjRhLjY2Ny42NjcgMCAwIDAgMS4wOTItLjIxNy42NjYuNjY2IDAgMCAwLS4xNDYtLjcyOUwxMC45MzkgMTB6IiBmaWxsPSIjZmZmIi8+PGRlZnM+PGZpbHRlciBpZD0iZmlsdGVyMF9iIiB4PSItNCIgeT0iLTQiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyOCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2UiIHN0ZERldmlhdGlvbj0iMiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1ciIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXIiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXIiIHgxPSIyMCIgeDI9IjE1LjU4NiIgeTI9IjIyLjk0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzBEMUUyOCIgc3RvcC1vcGFjaXR5PSIuOCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzA1MEUxMiIgc3RvcC1vcGFjaXR5PSIuNjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=',
				selectfile: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSIuMjUiIHk9Ii4yNSIgd2lkdGg9IjYzLjUiIGhlaWdodD0iNjMuNSIgcng9IjMuNzUiIGZpbGw9IiNGMkYyRjIiIHN0cm9rZT0iI0YyRjJGMiIgc3Ryb2tlLXdpZHRoPSIuNSIvPjxyZWN0IHg9IjE2IiB5PSIzMSIgd2lkdGg9IjMyIiBoZWlnaHQ9IjIiIHJ4PSIxIiBmaWxsPSIjQkZCRkJGIi8+PHJlY3QgeD0iMzMiIHk9IjE2IiB3aWR0aD0iMzIiIGhlaWdodD0iMiIgcng9IjEiIHRyYW5zZm9ybT0icm90YXRlKDkwIDMzIDE2KSIgZmlsbD0iI0JGQkZCRiIvPjwvc3ZnPg==',
				VideoOfImagesShow: true, // 页面图片或视频数量超出后,拍照按钮隐藏
				imageList: [], //存放图片的地址
				videoList: [], //视频存放的地址
				sourceType: ['拍摄', '相册', '拍摄或相册'],
				sourceTypeIndex: 2,
				cameraList: [{
					value: 'back',
					name: '后置摄像头',
					checked: 'true'
				}, {
					value: 'front',
					name: '前置摄像头'
				}],
				cameraIndex: 0, //上传视频时的数量
				maxCount:9//图片和视频允许上传的总数
			}
		},
		onUnload() {
			(this.imageList = []), (this.sourceTypeIndex = 2), (this.sourceType = ['拍摄', '相册', '拍摄或相册']);
		},
		methods: {
			//点击上传图片或视频
			chooseVideoImage() {
				uni.showActionSheet({
					title: '选择上传类型',
					itemList: ['图片', '视频'],
					success: res => {
						console.log(res);
						if (res.tapIndex == 0) {
							this.chooseImages();
						} else {
							this.chooseVideo();
						}
					}
				});
			},
			//上传图片
			chooseImages() {
				uni.chooseImage({
					count: this.maxCount, //允许选择的数量
					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
					sourceType: ['album', 'camera'], //从相册选择
					success: res => {
						this.imageList = this.imageList.concat(res.tempFilePaths);
						//console.log(this.imageList)
						if (this.imageList.length+this.videoList.length == this.maxCount) {
							this.VideoOfImagesShow = false; //图片上传数量和count一样时,让点击拍照按钮消失
						}
					}
				})
			},
			//上传视频
			chooseVideo(index) {
				uni.chooseVideo({
					maxDuration: 60, //拍摄视频最长拍摄时间,单位秒。最长支持 60 秒
					count: this.maxCount,
					camera: this.cameraList[this.cameraIndex].value, //'front'、'back',默认'back'
					sourceType: sourceType[this.sourceTypeIndex],
					success: res => {
						this.videoList = this.videoList.concat(res.tempFilePath);
						if (this.imageList.length+this.videoList.length == this.maxCount) {
							this.VideoOfImagesShow = false;
						}
						console.log(this.videoList);
					}
				})
			},
			//预览图片
			previewImage: function(e) {
				console.log(e)
				uni.previewImage({
					current: e,
					urls: this.imageList
				});
			},
			// 删除图片
			delect(index) {
				uni.showModal({
					title: '提示',
					content: '是否要删除该图片',
					success: res => {
						if (res.confirm) {
							this.imageList.splice(index, 1);
						}
						if (this.imageList.length+this.videoList.length  == this.maxCount) {
							this.VideoOfImagesShow = false;
						} else {
							this.VideoOfImagesShow = true;
						}
					}
				});
			},
			// 删除视频
			delectVideo(index) {
				uni.showModal({
					title: '提示',
					content: '是否要删除此视频',
					success: res => {
						if (res.confirm) {
							this.videoList.splice(index, 1);
						}
						if (this.imageList.length+this.videoList.length  == this.maxCount) {
							this.VideoOfImagesShow = false;
						} else {
							this.VideoOfImagesShow = true;
						}
					}
				});
			},
		}
	}
</script>
<style lang="scss">
	/* 统一上传后显示的盒子宽高比 */
	.box-mode {
		width: 27vw;
		height: 27vw;
		
		border-radius: 8rpx;
		overflow: hidden;
	}
	
	.full {
		width: 100%;
		height: 100%;
	}
 
	.up-page {
		display: flex;
		flex-wrap: wrap;
		display: flex;
		width: 100%;
		.show-box:nth-child(3n){
			margin-right: 0;
		}
		.show-box {
			position: relative;
			margin-bottom:4vw;
			margin-right: 4vw;
			@extend .box-mode;
 
			.delect-icon {
				height: 40rpx;
				width: 40rpx;
				position: absolute;
				right: 0rpx;
				top: 0rpx;
				z-index: 1000;
			}
		}
	}
 
</style>
// 1.封装上传图片的方法
/**
 * @description: 图片处理-上传图片
 * @param {*} api 请求地址的后缀 url 选择文件后返回的url
 * @return {*} 格式化后的内容
 */
function uploadImage(api, url, callback) {
	uni.showLoading({
		title: '上传中'
	});
	const token = uni.getStorageSync('token');
	let header = {
		'Authorization': token ? token : ''
	};
	return new Promise((resolve, reject) => {
		uni.uploadFile({
			url: BASE_URL + api,
			filePath: url,
			name: 'file',
			header:{
				'Authorization': token ? token : ''
			},
			success: res => {
				res = JSON.parse(res.data);
				console.log('res',res)
				if (res.code === 200) {
					uni.hideLoading()
					uni.showToast({
						title: '上传成功',
						icon: 'none'
					});
					callback(res)
					// resolve(res.data)
				} else {
					uni.hideLoading()
					uni.showModal({
						title: '上传失败',
						content: res.msg
					});
				}
			}
		});
	}).catch(e => {
		reject(e)
	})
}

// 2.页面调用
import { uploadImage } from '@/utils/util';

    //上传图片
    chooseImages() {
      uni.chooseImage({
        count: this.maxCount, //允许选择的数量
        sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
        sourceType: ['album', 'camera'], //从相册选择
        success: res => {
          uploadImage('/common/file/upload', res.tempFilePaths[0], ret => {
            this.imageList = this.imageList.concat(ret.data);
            console.log('图片', this.imageList)
          });
        }
      })
    },
    //上传视频
    chooseVideo(index) {
      uni.chooseVideo({
        maxDuration: 60, //拍摄视频最长拍摄时间,单位秒。最长支持 60 秒
        count: this.maxCount,
        camera: this.cameraList[this.cameraIndex].value, //'front'、'back',默认'back'
        sourceType: sourceType[this.sourceTypeIndex],
        success: res => {
          uploadImage('/common/file/upload', res.tempFilePath, ret => {
            this.videoList = this.videoList.concat(ret.data);
            console.log('视频', this.videoList);
          });
        }
      })
    },

39.element中el-cascader级联获取name和id值

// el-select
<el-select v-model="nValue" value-key="value" placeholder="请选择" @change="handleChange($event)" clearable>
  <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item">
  </el-option>
</el-select>

options: [{
  value: '选项1',
  label: '黄金糕'
}, {
  value: '选项2',
  label: '双皮奶'
}, {
  value: '选项3',
  label: '蚵仔煎'
}, {
  value: '选项4',
  label: '龙须面'
}, {
  value: '选项5',
  label: '北京烤鸭'
}],

handleChange(e){
  this.inquire.newValue = e.value || ''
  this.inquire.newName = e.label || ''
}

// el-cascader
<el-cascader ref="cascaderAddr" v-model="formData.areaId" @change="handleChange" :props="addressProps" :options="addressList" clearable placeholder="请选择地址" />

addressProps: {
    label: 'areaName',
    value: 'areaId',
    children: 'childes',
    checkStrictly: true,
    emitPath: false
},

handleChange(value) {
    this.$nextTick(() => {
        if(this.$refs["cascaderAddr"].presentText && this.$refs["cascaderAddr"].presentText.length > 0){
            // 判断在第几层
            this.formData.addressHierarchy = (this.$refs["cascaderAddr"].presentText.match(/\//g) || []).length + 1;
        }else{
            this.formData.addressHierarchy = null
        }
    });
}

40.uniapp按钮样式调整

<template>
  <view class="page-wrapper">
    <u-navbar title-color="#fff" back-icon-color="#ffffff" :is-fixed="isFixed" :is-back="isBack" :background="background" :back-text-style="{ color: '#fff' }" :title="title" :back-icon-name="backIconName"></u-navbar>
    <view class="page-body">
      <view class="member-list">
        <view class="member-card" v-for="(item, index) in source" :key="index">
          <view class="member-item">{{item.name}}</view>
        </view>
      </view>
      <view class="add-member">
        <view class="addBtn">
          添加成员
        </view>
      </view>
    </view>
  </view>
</template>

<script>

export default {
  data() {
    return {
      title: '成员列表',
      backIconName: 'nav-back',
      background: {
        background: 'url("https://chinaja.oss-cn-shenzhen.aliyuncs.com/2023/7/13/0df31754-03ad-4d88-85a0-5584c54fa127.png") no-repeat top / 100% 100%'
      },
      isBack: true,
      isFixed: true,
      source: [
        {
          id: 1,
          name: '张可乐'
        },
        {
          id: 2,
          name: '五六七'
        },
        {
          id: 3,
          name: '十三'
        },
        {
          id: 4,
          name: '五六七'
        },
        {
          id: 5,
          name: '五六七'
        },
        {
          id: 6,
          name: '五六七'
        },
        {
          id: 7,
          name: '五六七'
        },
        {
          id: 8,
          name: '五六七'
        },
        {
          id: 9,
          name: '五六七'
        },
        {
          id: 10,
          name: '五六七'
        },
        {
          id: 11,
          name: '五六七'
        },
        {
          id: 12,
          name: '五六七'
        },
        {
          id: 13,
          name: '五六七'
        },
        {
          id: 14,
          name: '五六七'
        },
        {
          id: 15,
          name: '五六七'
        },
      ]
    };
  },
  onLoad() {
  },
  onShow() {
  },
  methods: {
  }
};
</script>

<style lang="less" scoped>
.page-body {
  height: 100%;
}
.member-list {
  background: #f5f6f5;
  padding-top: 2%;
  padding-bottom: 160rpx;
  .member-card {
    width: 92%;
    height: 80rpx;
    margin-left: 4%;
    background: #ffffff;
    display: flex;
    align-items: center;
    padding-left: 6%;
    margin-top: 2%;
    border-radius: 12rpx;
  }
}

.add-member {
  position: fixed;
  bottom: 0;
  height: 160rpx;
  width: 100%;
  background: #f5f6f5;
  padding-top: 40rpx;
  .addBtn {
    width: 92%;
    height: 80rpx;
    margin-left: 4%;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #4e9aef;
    color: #ffffff;
    border-radius: 20rpx;
  }
}
</style>

41.js获取网址参数

let str = 'http://www.baidu.com/xxx?name=张三&age=18'
let a1 = new URLSearchParams(new URL(str).search).get('name')
let a2 = new URLSearchParams(new URL(str).search).get('age')
console.log('a1',a1); // 输出:张三
console.log('a2',a2); // 输出:18

42.多选题回显

<view v-for="(item, index) in multipleClone" :key="index" >
                  <view>{{item.content}}</view>
                  <view>
                    <u-checkbox-group wrap>
                      <u-checkbox v-for="(option, optionIndex) in item.optionList" :key="optionIndex" :name="option.content" v-model="option.selected">
                        {{ option.content }}
                      </u-checkbox>
                    </u-checkbox-group>
                  </view>
 </view>

data(){
    return{
       multipleList: [],
       multipleClone: [],
       multipleMap: {},
    }
}


// 多选题
this.multipleList = res.questionDetailVoList.filter(item => item.type === 2);
let arr2 = this.userQuestionVos.filter(item => item.type === 2);
let multipleArr = [];
arr2.map(item => {
   multipleArr.push(item.optionId)
})
this.multipleList.map(item1 => {
    item1.optionList.map(item2 => {
        if(multipleArr.includes(item2.id)){
              item2.selected = true
         }else{
              item2.selected = false
         }
       })
})
    
// 深拷贝
this.multipleClone = JSON.parse(JSON.stringify(this.multipleList));

43.小程序X轴滚动条

<scroll-view class='scrollContainer' scroll-x>
	<view class='scrollitem' v-for="(item, index) in goodsArr" :key="index" @click="getItem(item)">
		<image class="scrollimage" :src="item.contentPic"></image>
		<view class="recommandItemText">{{item.contentTitle}}</view>
	</view>
	<view class="EmptyData" v-if="goodsArr.length==0">暂无数据</view>
</scroll-view>
	// 容器
	.scrollContainer {
		width: 666rpx;
		height: 234rpx;
		white-space: nowrap;
	}

	// 容器项
	.scrollitem {
		display: inline-block;
		margin-left: 30rpx;
		height: 234rpx;
	}

	.scrollimage {
		width: 270rpx;
		height: 154rpx;
	}

	.recommandItemText {
		width: 270rpx;
		text-align: center;
		text-overflow: ellipsis;
		overflow: hidden;
		white-space: nowrap;
	}

	.EmptyData {
		text-align: center;
		margin-top: 50rpx;
	}

  

posted @ 2018-12-04 15:59  这个夏天要冰凉  阅读(12690)  评论(0编辑  收藏  举报