网页公众号开发

背景是要在一个h5的页面,可以发送语音可以听语音,因为苹果手机对Audio的支持有限,所以后来准备采用微信公众号开发,因为这个h5也是要签入在项目的公众号中。

先扒文档

微信官网文档

1.文档指引截图

第一步:绑定域名

先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。虽然微信公众平台提供了测试号,但是在微信开发者工具中还是是用不了微信能力,还是需要申请一个个人的公众号或者是公司的。

2.设置安全域名

第二步:引入js文件

3.引入js
可以直接引入js标签,也可以是添加模块的方式,这里我用了yarn add weixin-js-sdk

第三步:通过config接口注入权限验证配置

1
2
3
4
5
6
7
8
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});

除了debug,jsApiList是需要前端自己需要设置,其他都可以依赖后端的返回,其中签名算法,在文档的文末的附录1。附录1

ivew表单清空表单的一些技巧

PC项目通常使用iView组件,我通常会把表单Form单独写一个vue文件,然后在父组件里边用iView的Modal将表单弹出,代码如下:
1.Modal引用表单组件

那么有一个问题就是关闭弹框的时候要将表单重置。
重置有两个用处:
其一:将表单的字段清空
其二:表单难免有校验,避免重新打开表单时,错误提示还在

在引用子组件的时候,给子组件添加ref属性,方便使用子组件的方法。
iView的Modal弹框右上角有一个关闭的按钮,在关闭的按钮,我们要将弹框关闭的是就执行清空表单的操作,Modal组件抛出一个事件on-visible-change,modal框显示状态发生变化时触发,返回一个参数,若参数为false为modal框关闭,那这样我们就可以来清空表单了

1
2
3
4
5
close(state){				
if(!state){
this.$refs.child.$refs['formModel'].resetFields()
}
}

resetFields()方法是form表单的方法,直接解决了我们需要重置表单的两个问题,在表单组件中也要给Form组件添加ref属性,因为验证表单项和重置表单使用的都是表单组件自带的方法,如下:
2.表单组件添加ref属性

为什么要写两层refs来获取呢,打印如下:

3.打印refs

并结合chrome的vue插件可以清晰的看到:

4.vue组件关系
所有需要refs两次才能找到表单实例并调用方法。

***那么还有一个问题,若是表单中有上传视频的功能,上传视频的时间比较长,在关闭弹框的时候将停止上传视频,这个问题确实还挺困扰,不能终止请求上传,但是又不能再打开弹框的时候,文件上传成功了,并显示了文件上传列表,所以关闭弹框的时候要清掉已经上传的文件。iview虽然没有提供upload提供终止请求,但是提供了一个清空文件的方法。

既然是调用upload组件中的方法,所以在upload组件上写上ref

1
2
3
4
5
6
7
8
9
10
11
// add-resource.vue中

<Upload ref="uploadViedo" v-if="formModel.rpicture !== ''" :action='actionVideo' name='multipartFile' :showList='true' type='select' :maxSize='51200' :disabled="formModel.rpicture == ''" :on-success="uploadVideo" :on-exceeded-size="sizeErroeViedo" :format="['mp4']" :on-format-error="formatErrorViedo">
</Upload>

methods:{
clearFiles(){
this.$refs.uploadViedo.clearFiles()
}

}

那么在父组件中的close方法添加:

1
2
3
4
5
6
close(state){				
if(!state){
this.$refs.child.$refs['formModel'].resetFields()
this.$refs.child.clearFiles()
}
}

整理一些正则和replace的使用

replace

1.用replace去掉html标签以及空格

str.replace(/<img[^>]*>|<\/?.+?\/?>| |\s/gi, ‘’)

2.replace的替换文本函数使用

replace(regexp/substr, replacement), replce有两个参数,regexp/substr必需。规定子字符串或要替换的模式的 RegExp 对象。

请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象;replacement 必需。一个字符串值。规定了替换文本或生成替换文本的函数。

在项目中会很少用到替换文本的函数,没想到用起来还真的挺强大的,比如在给上传的图片添加缩略的时候,图片地址url_200x300.png这样的格式
我们可能需要将图片的后置去掉,然后拼接上图片缩略图的参数,然后在拼上图片的后缀,无疑需要indexOf一下,substring一下等等,用replace的替换文本函数就很容易了

1
2
3
4
let regx = /\.(JPG|JPEG|PNG|BMP)$/gi
let content = url.replace(regx, (m)=>{
return '_200x300' + m
})

效果如图:
1.replace

3取出富文本框中的全部图片

在富文本中无论是上传的图片还是复制粘贴过来,其实无非就是img标签,利用正则将图片全部取出,利用正则匹配图片的img标签

1
2
let reg = /<img[^>]*>/gi
let arr = data.match(reg)

利用match会返回一个数组

4.用正则表达式匹配身份证

1
2
let rep = /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/; 
rep.test(val)

符合将返回true否则返回false

用reduce对比两个数组的差异

假如有两个数组pre、cur,比较两个数组之间的差异,比如在pre中但不在cur数组中,在cur数组中不在pre数组中,这个时候会想到用filter过滤返回一个新的数组,filter的条件为pre数组是否cur数组的某一项,可以写成:

1
2
3
4
5
6
var pre = [1, 2, 3, 4, 5]
var current = [3, 4, 5, 6, 7]
var diff = pre.filter(item => !current.includes(item))
console.log(diff)

=> [1,2]

大佬说这样的时间负责度太高了,于是乎亮出大招:

1
2
3
4
5
6
7
8
9
var bMap = current.reduce((map, i) => {
map[i] = 1
return map
}, {})

var b = pre.filter(item => !bMap[item])
console.log(b)

=> [1,2]

生成哈希表,哈希表的寻址是直接性的,不需要for循环遍历,includes需要每次遍历成哈希表以后节省了遍历,所以速度比较快

arr.reduce(function(prev,cur,index,arr){

}, init);
有四个参数prev 表示上一次调用回调时的返回值,或者初始值 init;cur 表示当前正在处理的数组元素;index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;init 表示初始值。通常情况会用到前两个参数。

那么在秀刀操作中,使用了前两个参数,初始值为一个空对象,其实就会将数组变成的值变成一个键值对的形式,1就是给对象的属性复制,这里也可以写成true比较好理解,最后在通过对象的方式去寻找。

如果是一个对象数组呢,我们就需要绑定对象中唯一值:
比如:

1
2
3
4
5
var bMap = arr1.reduce((map, i) => {
map[i.userId] = true;
return map;
}, {});
var result = arr2.filter(item => !bMap[item.userId]);

如果有两个对象数组,分别数组a与b中的差异,就可以将上边的代码封装为一个方法:

1
2
3
4
5
6
7
8
splitCaptainList(arr1, arr2) {
var bMap = arr1.reduce((map, i) => {
map[i.userId] = true;
return map;
}, {});
var result = arr2.filter(item => !bMap[item.userId]);
return result
}

如果你有什么更好的方法或者文章有不正确的内容,很开心能跟你交流,QQ:39004003 [笑脸]

利用map解构返回数据格式

有的时候后端返回的字段并不是我们需要的,比如
1.渲染一个select,那么option接受的属性名称是固定的,
2.或者有一个可复用的列表页,可复用的列表将不改变模板中的变量,那么就需要在调用接口的地方将返回的数据名称进行改变

很简单,我们利用map对返回结果进行操作,map不会改变原数组会生成一个新的数组

1
let newArr = arr.map(it => ({value:it.subjectType, label:it.subjectName}))

***注意:箭头函数若返回结果是一个对象时一定要添加括号
结果如下:
1.map解构对象

***注意:箭头函数若只有一个参数的时候可以不用添加括号

1
let newArr = arr.map(({subjectType,subjectName}) => ({value:subjectType,label:subjectName}))

2.map解构对象

动态改变组件的keepAlive的值

keepAlive的作用还是很大的,虽然在之前的总结中有说避开使用keepAlive,但是在app端的表单页面难免会有跳转到其他页面进行选择然后再返回到表单页面,虽然通过主子路由的方式,但是页面返回后表单并没有保存填过的信息。

首先在app.vue根组件中,

1
2
3
4
5
6
7
8
<template v-if="$route.meta.keepAlive">
<keep-alive>
<router-view></router-view>
</keep-alive>
</template>
<template v-else>
<router-view></router-view>
</template>

是渲染组件(可以理解为此处是你的a.vue, b.vue等等),所以不难理解,若是组件在路由中定义时加上了keepAlive并且为true的情况下,那么该组件有将引入的keep-alive组件。

在路由文件中这么写:

1
2
3
4
5
6
{
name: 'create',
path: '/create',
meta: {title: '会议详情', keepAlive: true},
component: resolve => {require.ensure(['../pages/create.vue'], () => {resolve(require('../pages/create.vue'))})}
},

在meta对象中添加keepAlive属性为true。

通常情况下查看详情页和创建页面为一个,某些操作隐藏掉而已,所以会遇到一个问题,从列表页查看详情的时候,因为该页面添加了keep-alive组件,所以详情页并不会重新渲染,无论点击哪条进来详情都是第一页的内容。

那么既然是路由中设置了keepAlive属性的问题,那就看看能不能动态改变。那么在进入创建页面的时候也就是离开上一个页面的时候能不能改变create页面的keepAlive属性呢。

所以就在创建的页面之前的页面使用路由钩子函数这样处理:

1
2
3
4
5
6
beforeRouteLeave (to, from, next) {
if(this.isKeep) {
to.meta.keepAlive = true
} else to.meta.keepAlive = false
next()
}

那到底是什么原因添加了keep-alive组件之后,组件将不会渲染,也只能用路由钩子函数来改变吗?所以又尝试了一下,那么每个组件都有生命周期函数,created、mounted、activated、distroyed,那么页面第一次进来的时候,created、mounted、activated都将打印,但是当第二次在进到创建页面的时候created、mounted钩子函数并没有执行,但是执行了activated钩子函数,所以说除了上边的处理方式以外,我们还可以将页面初始化的函数放在activated一份。


如果你有什么更好的方法或者文章有不正确的内容,很开心能跟你交流,QQ:39004003 [笑脸]

vue自定义指令

vue中不止有v-if、v-else、v-model这样的指令,可自定义指令。项目中有一个搜索页面,进去之后获取焦点,使用input的autofocus属性,很奇怪的是第一次焦点在,但是第二次焦点就获取不上了。这个问题也一直没有找到原因,还在找原因,项目急就用了自定义指令的方法。
自己定义一下focus。有两种方式注册指令,一种是全局的一种是在组件内的(在组件内注册就类似于watch、filter的用法)。

注册全局组件

1
2
3
4
5
6
7
8
9
import Vue from 'vue'

Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})

注册局部(组件内)

1
2
3
4
5
6
7
8
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}

在绑定函数的地方,传入一个参数el,el便是添加指令的地方获取的dom元素,比如说将该指令添加在input输入框中,那么el便是获取该DOM元素,所以若是想要改变元素的样式或者内容等等,可以写成el.style.color或el.innerHtml,等等一系列的DOM操作。

然后在模板中使用该指令:

1
<input type="text" v-model="inputValue" placeholder="搜索" v-focus>

这样就无论如何input输入框一直都是在focus的状态了。

如何设置Mint UI中Picker中的默认值

第一步:引入组件

js部分引入依赖

1
2
3
import { Popup, Picker} from "mint-ui";
Vue.component(Switch.name, Switch);
Vue.component(Popup.name, Popup);

在模板中引入

1
2
3
4
<mt-popup v-model="remindVisible" position="bottom" class="remind-popup">
<mt-picker :slots="Minuteslots" @change="onRemindChange" :show-toolbar="true" v-model="remindMin">
</mt-picker>
</mt-popup>

能够从上边的代码可以看出picker结合了popup使用。在使用picker的时候,有一个参数为slots,这个参数就是picker的备选值都有哪些,同时可以设置picker默认选中项,那么slots的参数在data中是这样设置的:

1
2
3
4
5
6
7
8
9
10
export default {
data () {
return {
Minuteslots: [{
values: ['未设置', '15分钟', '60分钟', '一天'],
defaultIndex: 1
}]
}
}
}

其中defaultIndex就是默认值的在values中的index值,言外之意就是picker默认选中的就是’15分钟’。
有一个问题就是如果是一个提交信息的页面,并且还可以提交后进行编辑,那么将返回的数据为picker的默认值,这个时候就用到双向绑定,v-model了,设置了一个remindMin变量,因为后端返回的数据和前端picker显示的不同,所以使用computed进行转换,并改变defaultIndex来改变picker中的默认值(picker中高亮的部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
computed: {
remindMin () {
switch(this.formObj.remindMinute) {
case 0:
this.Minuteslots[0].defaultIndex = 0
return '未设置'
break;
case 15:
this.Minuteslots[0].defaultIndex = 1
return '15分钟'
break;
case 60:
this.Minuteslots[0].defaultIndex = 2
return '60分钟'
break;
case 1440:
this.Minuteslots[0].defaultIndex = 3
return '一天'
break;
default:
return this.formObj.remindMinute
break;
}
}
}

那么就做到了,当有后端返回的时候,我们通过计算属性来改变当前的默认值以及显示。


如果你有什么更好的方法,很开心能跟你交流,QQ:39004003 [笑脸]

iView表单循环表单验证

PC端现在用的最多的组件库就是iView,通常我们都是一个表单验证后就提交了,如果是动态新添加的表单,如何做验证并提交呢。首先先看一下要实现的样子:

1.循环表单验证

你会发现,两个表单在分别点击完成的时候都分别作了验证。

iView的Form表单,需要两个属性,rule是表单验证规则,比如该字段是否需要必填等,即使是循环动态生成的表单,若是相同的表单项,那么验证规则一定是一样的,所以rule所关联的就是一个变量;还有一个属性model是表单数据对象,那么我们既然是循环生成的表单,并且每一次点击完成的时候是提交当前的表格的数据,所以我们想到model是循环生成表单的list的某一项,那么我们就可以定义变量了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 表单验证规则
uploadFormRule: {
mechanismAddress: [
{ required: true, message: '请输入服务机构地址', trigger: 'blur' }],
mechanismName: [
{ required: true, message: '请输入服务机构名称', trigger: 'blur' }],
mechanismPhone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^(\d{11}|\d{3}-\d{8})$/, message: '请输入正确的联系方式', trigger: 'blur' }
],
mechanismUrl: [
{ type: 'url', message: '请输入正确的服务机构网址', trigger: 'blur' }
]
},
list: [
{
mechanismAddress: "",
mechanismName: "",
mechanismPhone: "",
mechanismUrl: ""
}
]

那么循环表单的时候就可以写成(忽略一些样式所需要的元素):

1
<Form v-for="(li, index) in list" :key="index"  :rules="uploadFormRule"  :model="li"></Form>

那么现在就是最重要的问题了,我们知道如果是当个表单验证的时候就是

1
2
this.$refs[name].validate((valid) => {})
// name指的是model所对应的对象名字

那么this.$ref是获取注册过的组件,所以需要使用ref来注册组件,通常单表单注册的时候就是model所关联的对象,但是是循环生成的表单并且都需要验证,那ref就需要换一个写法,ref是一个对象,也就是说点击完成的时候我们验证的是当前的这个表单,所以ref需要关联当前的index,所以表单写成:

1
2
3
4
5
<Form v-for="(li, index) in list" :key="index"  :rules="uploadFormRule"  :model="li" :ref="'list' + index"></Form>
// 当然ref里边list可以替换成想要的字符串,比如li
// 验证表单的时候:
let form = 'list' + index
this.$refs[form][0].validate((valid) => {})

之所以这样写,把refs打印出来是这样的
2.打印ref

页面有几个表单,就注册了几个表单,refs是一个对象,注册时使用ref=”‘list’ + index”来命名每一个表单,自然取出时也需要拼接这样的一个字符串,从上图中可以看出,每一个ref对象(每一个表单项都一个数组),打印一下我们可以看出,我们每个表单所对应的model绑定数据对象以及rule
3.张开refs中某一项

总结:循环表单验证的时候注册组件时主要加上该表单的唯一标识,index或者key