概述
vue开发微信公众号可视化配置菜单
其实一开始我也是懵逼的,我搞不懂微信公众号后台其实已经很好用了,为什么还需要开发一个模仿微信公众号后台的系统呢?而且微信公众号可以绑定多个运营者,都可以和管理员一样可以通过扫码登录来修改微信菜单配置,唯一想明白的就是如果有什么特殊功能需要开启第三方服务(开发者配置)的时候,才需要开发自定义菜单和自动回复。既然有如此需求,那么我们就来看看vue是如何实现微信公众号的可视化配置菜单的。菜单数据使用mock.js模拟,安装命令是,前端框架采用vue + element
npm install mockjs --save-dev
一、效果图:
二、采用的是vue-cli 2.0版本,菜单目录结构如图所示:
三、对应文件分别写入
1、views目录下menu的index.vue,我这边使用的是scss,css根据个人需要调整,图片路径也自己更改,所使用的图片地址是:
http://pri3p9w51.bkt.clouddn.com/iphone_backImg.png
<template>
<div class="public-account-management clearfix">
<!--左边配置菜单-->
<div class="left">
<div class="menu clearfix">
<div v-for="(item, i) of menu.button" :key="i" class="menu_bottom">
<!-- 一级菜单 -->
<div :class="{'active': isActive == i}" class="menu_item" @click="menuFun(i,item)">{{ item.name }}</div>
<!--以下为二级菜单-->
<div v-if="isSubMenuFlag == i" class="submenu">
<div v-for="(subItem, k) in item.sub_button" :key="k" class="subtitle">
<div :class="{'active': isSubMenuActive == i + '' + k}" class="menu_subItem" @click="subMenuFun(item, subItem, i, k)">{{ subItem.name }}</div>
</div>
<!--二级菜单加号, 当长度 小于 5 才显示二级菜单的加号 -->
<div v-if="item.sub_button.length < 5" class="menu_bottom menu_addicon" @click="addSubMenu(item)"><i class="el-icon-plus"></i></div>
</div>
</div>
<!-- 一级菜单加号 -->
<div v-if="menuKeyLength < 3" class="menu_bottom menu_addicon" @click="addMenu"><i class="el-icon-plus"></i></div>
</div>
<el-button class="save_btn" type="success" @click="saveFun">保存并发布至菜单</el-button>
</div>
<!--右边配置-->
<div v-if="!showRightFlag" class="right">
<div class="configure_page">
<div class="delete_btn">
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteMenu(tempObj)">删除当前菜单</el-button>
</div>
<div>
<span>菜单名称:</span>
<el-input v-model="tempObj.name" class="input_width" placeholder="请输入菜单名称" clearable style="margin-top: 12px;"></el-input>
</div>
<div>
<div class="menu_content">
<span>菜单内容:</span>
<el-radio-group v-model="tempObj.type">
<el-radio :label="'media_id'">发送素材</el-radio>
<el-radio :label="'view'">跳转链接</el-radio>
<el-radio :label="'click'">发送关键词</el-radio>
<el-radio :label="'miniprogram'">小程序</el-radio>
</el-radio-group>
</div>
<div class="configur_content">
<div v-if="tempObj.type == 'media_id'" class="material">
<span>素材内容:</span>
<el-input v-model="tempObj.media_id" :disabled="true" class="input_width" placeholder="素材名称"></el-input>
<!--下面点击“选择素材”按钮,弹框框-->
<el-popover
v-model="visible2"
placement="top">
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
label="文件名"
width="600">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>文件名: {{ scope.row.name }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.name }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<el-button slot="reference" type="success">选择素材</el-button>
</el-popover>
<p class="blue">tips:需要调后台获取到内容,弹框出来,然后选择,把名字赋值上去!</p>
</div>
<div v-if="tempObj.type == 'view'">
<span>跳转链接:</span>
<el-input v-model="tempObj.url" class="input_width" placeholder="请输入链接" clearable></el-input>
</div>
<div v-if="tempObj.type == 'click'">
<div>
<span>关键词:</span>
<el-input v-model="tempObj.key" class="input_width" placeholder="请输入关键词" clearable></el-input>
</div>
<p class="blue">tips:这里需要配合关键词推送消息一起使用</p>
</div>
<div v-if="tempObj.type == 'miniprogram'">
<div class="applet">
<span>小程序的appid:</span>
<el-input v-model="tempObj.appid" class="input_width" placeholder="请输入小程序的appid" clearable></el-input>
</div>
<div>
<span>小程序的页面路径:</span>
<el-input v-model="tempObj.pagepath" class="input_width" placeholder="请输入小程序的页面路径,如:pages/index" clearable></el-input>
</div>
<p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p>
</div>
</div>
</div>
</div>
<div>menu对象值:{{ menu }}</div>
</div>
<!--一进页面就显示的默认页面,当点击左边按钮的时候,就不显示了-->
<div v-if="showRightFlag" class="right">
<p>请选择菜单配置</p>
</div>
</div>
</template>
<script>
/**
*时间:2018/08/26
*作者: spirtis
*功能:
* 公众号管理页面(基于vue-cli + elementUI + mockjs)
* 如果想迁移到其他UI框架,里面更改的地方,就是:
* 1、+ 加号 按钮
* 2、el- 开头的组件: 输入框,按钮,弹出框 基本就这三种类型
* @参数:
*
* 1、自定义菜单配置文档,需要根据微信公众号要求配置,下面的链接
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013
*
* 2、tempObj.type:以下这4个类型的type值一定是这样,不能改变。
* media_id:发送素材; view:跳转链接; click:发送关键词 ; miniprogram:小程序
*
* 3、素材内容这些都得调后台接口,读取微信配置,我本地没有,就用mock模拟
*
* 4、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
* 一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
* (PS:输入框这些没有做限制,也没有做验证)
*
* 5、menu 对象必须严格按照微信公众号文档说明的一样
*
* 6、提交的时候,整个menu提交,type值是什么,后台获取的也是对应的内容,如果同个菜单
* 的其他内容也填写了,也不妨碍。
*/
export default {
components: {
},
data() {
return {
showRightFlag: true, // 右边配置显示默认详情还是配置详情
menu: {
// 一级菜单
button: [
// {
// type: 'click',
// name: '菜单1',
// // key: 'menu1',关键词
// url: '', // 跳转链接
// media_id: '', // 素材名称--图文消息
// sub_button: [
// {
// type: '', // media_id:素材内容; view:跳转链接
// name: '子菜单1',
// url: '', // 跳转链接
// media_id: '' // 素材名称--图文消息
// }
// ]
// }
]
}, // 横向菜单
isActive: -1, // 一级菜单点中样式
isSubMenuActive: -1, // 一级菜单点中样式
isSubMenuFlag: -1, // 二级菜单显示标志
tempObj: {
// type: "view",
// media_id: 素材内容
// view: 跳转链接
// name: "",//菜单名称
// material: "",//素材名称
// link: "",//跳转链接
}, // 右边临时变量,作为中间值牵引关系
tempSelfObj: {
// 一些临时值放在这里进行判断,如果放在tempObj,由于引用关系,menu也会多了多余的参数
// grand:"" 1:表示一级菜单; 2:表示二级菜单
// index:"" 表示一级菜单索引
// secondIndex:"" 表示二级菜单索引
},
visible2: false, // 素材内容 "选择素材"按钮弹框显示隐藏
tableData: [] // 素材内容弹框数据
}
},
created() {
this.mockMediaFun() // 模拟获取素材
this.mockMenuFun() // 模拟调取菜单数据
// this.getWxMenu()
},
/* eslint-disable */
methods: {
// 模拟调取菜单数据
getWxMenu() {
this.$http.get('/CustomMenu/get').then(response => {
if(response.data.errcode == null) {
this.menu.button = response.data.menu.button
} else if (response.data.errcode == 46003) {
this.menu.button = [
{
type: 'click',
name: '菜单1',
// key: 'menu1',关键词
url: '', // 跳转链接
media_id: '', // 素材名称--图文消息
sub_button: [
{
type: '', // media_id:素材内容; view:跳转链接
name: '子菜单1',
url: '', // 跳转链接
media_id: '' // 素材名称--图文消息
}
]
}]
}
}).catch(error => {
this.$message({
showClose: true,
message: '获取微信公众号菜单异常{' + error + '}',
type: 'error'
})
})
},
mockMenuFun(){
this.$http(
{
url:"/api/menu",
method:'get'
}
)
.then((res)=>{
this.menu.button = res.data.button
})
},
// 调用素材内容 “选择素材”的弹框数据,模拟从微信返回的数据
mockMediaFun(){
this.$http(
{
url:"/api/data",
method:'get'
}
)
.then((res)=>{
this.tableData = res.data;
})
},
// 素材内容弹框的选择按钮函数
handleEdit(index, row) {
this.visible2 = false;
this.tempObj.media_id = row.name;
},
saveFun(){
this.$http.post('/CustomMenu/add', this.menu, {headers: {'content-type': 'application/json'}}).then(response => {
if(response.data.errcode == 0) {
this.$message({
showClose: true,
message: '保存并发布成功!',
type: 'success'
})
} else {
this.$message({
showClose: true,
message: '' + response.data.errmsg + '',
type: 'warning'
})
}
}).catch(error => {
this.$message({
showClose: true,
message: '保存并发布菜单异常{' + error + '}',
type: 'error'
})
})
},
// 一级菜单点击事件
menuFun(i, item){
this.showRightFlag = false //右边菜单隐藏
// console.log(i)
this.tempObj = item //这个如果放在顶部,flag会没有。因为重新赋值了。
this.tempSelfObj.grand = "1" //表示一级菜单
this.tempSelfObj.index = i //表示一级菜单索引
this.isActive = i //一级菜单选中样式
this.isSubMenuFlag = i //二级菜单显示标志
this.isSubMenuActive = -1 //二级菜单去除选中样式
},
// 二级菜单点击事件
subMenuFun(item, subItem, index, k){
this.showRightFlag = false;//右边菜单隐藏
this.tempObj = subItem;//将点击的数据放到临时变量,对象有引用作用
this.tempSelfObj.grand = "2";//表示二级菜单
this.tempSelfObj.index = index;//表示一级菜单索引
this.tempSelfObj.secondIndex = k;//表示二级菜单索引
this.isSubMenuActive = index + "" + k; //二级菜单选中样式
this.isActive = -1;//一级菜单去除样式
},
// 添加横向一级菜单
addMenu(){
// 先判断1,再判断2,对象增加,会进行往下计算,所以必须先判断2,再判断1
if(this.menuKeyLength == 2){
this.$set(this.menu.button,"2",
{
// type: "",
name: "菜单3",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
sub_button: []
}
)
}
if(this.menuKeyLength == 1){
this.$set(this.menu.button, "1",
{
// type: "",
name: "菜单2",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
sub_button: []
}
)
}
},
// 添加横向二级菜单
addSubMenu(item){
let subMenuKeyLength = item.sub_button.length;//获取二级菜单key长度
if(subMenuKeyLength == 4){
this.$set(item.sub_button,"4",
{
// type: "",
name: "子菜单5",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
}
);
}
if(subMenuKeyLength == 3){
this.$set(item.sub_button,"3",
{
// type: "",
name: "子菜单4",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
}
);
}
if(subMenuKeyLength == 2){
this.$set(item.sub_button,"2",
{
// type: "",
name: "子菜单3",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
}
)
}
if(subMenuKeyLength == 1){
this.$set(item.sub_button,"1",
{
// type: "",
name: "子菜单2",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
}
)
}
if(subMenuKeyLength == 0){
this.$set(item.sub_button,"0",
{
// type: "",
name: "子菜单1",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
}
)
}
},
//删除当前菜单
deleteMenu(obj){
var _this = this
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
_this.deleteData() // 删除菜单数据
}).catch(() => {
})
},
// 删除菜单数据
deleteData(){
// 一级菜单的删除方法
if (this.tempSelfObj.grand == "1") {
this.menu.button.splice(this.tempSelfObj.index,1)
}
// 二级菜单的删除方法
if (this.tempSelfObj.grand == "2") {
this.menu.button[this.tempSelfObj.index].sub_button.splice(this.tempSelfObj.secondIndex, 1)
}
this.$message({
type: 'success',
message: '删除成功!'
})
}
},
computed:{
// menuObj的长度,当长度 小于 3 才显示一级菜单的加号
menuKeyLength:function(){
return this.menu.button.length;
}
}
/* eslint-disable */
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import 'src/styles/variables.scss';
/* 公共颜色变量 */
.clearfix{*zoom:1;}
.clearfix::after{content: "";display: table; clear: both;}
div{
text-align: left;
}
.public-account-management{
padding: 20px;
min-width: 1200px;
width: 1200px;
margin: 0 auto;
.left{
font-family: 'Times New Roman', Times, serif;
float: left;
display: inline-block;
width: 301px;
background: url("../../../assets/img/wxmenu/iphone_backImg.png") no-repeat;
background-size: 100% auto;
padding: 490px 25px 88px;
position: relative;
box-sizing: border-box;
/*第一级菜单*/
.menu{
.menu_bottom{
position: relative;
float: left;
display: inline-block;
box-sizing: border-box;
width: 83px;
/*height: 44px;*/
/*line-height: 44px;*/
text-align: center;
background-color: #e2e2e2;
border: 1px solid #ebedee;
cursor: pointer;
&.menu_addicon{
height: 46px;
line-height: 46px;
}
.menu_item{
height: 44px;
line-height: 44px;
/*padding: 14px 0;*/
text-align: center;
box-sizing: border-box;
&.active{
border: 1px solid #2bb673;
}
}
.menu_subItem{
height: 44px;
line-height: 44px;
text-align: center;
box-sizing: border-box;
/*padding: 14px 0;*/
&.active{
border: 1px solid #2bb673;
}
}
}
i{
color:#2bb673;
}
/*第二级菜单*/
.submenu{
position: absolute;
width: 83px;
bottom: 45px;
.subtitle{
background-color: #e8e7e7;
box-sizing: border-box;
margin-bottom: 2px;
}
}
}
.save_btn{
position: relative;
margin-top: 100px;
left: 45px;
}
}
/*右边菜单内容*/
.right{
font-family: 'Times New Roman', Times, serif;
float: left;
width: 70%;
background-color: #e8e7e7;
padding: 25px 10px 0px 20px;
min-height: 610px;
margin-left: 20px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
p {
font-size: 16px;
margin-top: -10px;
font-weight: bold;
}
.configure_page{
.delete_btn{
text-align: right;
margin-bottom: 15px;
}
.menu_content{
margin-top: 20px;
}
.configur_content{
margin-top: 20px;
}
.blue{
color:#29b6f6;
margin-top: 10px;
}
.applet{
margin-bottom: 20px;
span{
margin-right: 18px;
}
}
.material{
.input_width{
width: 30%;
}
.el-textarea{
width: 80%
}
}
}
}
}
</style>
2、wxmenu.js放置与根目录mock下
import Mock from 'mockjs';
// 素材内容
export var tableData = Mock.mock('/api/data',
[{
name: 'Vue',
}, {
name: 'angular',
},{
name: 'react',
},
]
);
// 配置菜单
export var menu = Mock.mock('/api/menu',
{
// 一级菜单
button:[
{
type: "click",
name: "菜单1",
key: "menu1", // 关键词
url: "", // 跳转链接
media_id:"", // 素材名称--图文消息
sub_button: [
{
//type: "",//media_id:素材内容; view:跳转链接
name: "子菜单1",
// url: "",//跳转链接
// media_id:"",//素材名称--图文消息
},
]
}
]
}
);
3、main.js中写入
require('../mock/wxmenu')
至此,恭喜您,可视化菜单就ok了
最后
以上就是温柔小鸽子为你收集整理的vue开发微信公众号可视化配置菜单的全部内容,希望文章能够帮你解决vue开发微信公众号可视化配置菜单所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复