mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 10:18:43 +08:00 
			
		
		
		
	feat: 微信自定义菜单
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/iphone_backImg.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/iphone_backImg.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 34 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/menu_foot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/menu_foot.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/menu_head.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/views/mp/menu/assets/menu_head.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 12 KiB | 
| @@ -1,3 +1,793 @@ | |||||||
| <template> | <template> | ||||||
|   <span>开发中</span> |   <div class="app-container"> | ||||||
|  |     <doc-alert title="公众号菜单" url="https://doc.iocoder.cn/mp/menu/" /> | ||||||
|  |  | ||||||
|  |     <!-- 搜索工作栏 --> | ||||||
|  |     <el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> | ||||||
|  |       <el-form-item label="公众号" prop="accountId"> | ||||||
|  |         <el-select v-model="accountId" placeholder="请选择公众号"> | ||||||
|  |           <el-option | ||||||
|  |             v-for="item in accountList" | ||||||
|  |             :key="parseInt(item.id)" | ||||||
|  |             :label="item.name" | ||||||
|  |             :value="parseInt(item.id)" | ||||||
|  |           /> | ||||||
|  |         </el-select> | ||||||
|  |       </el-form-item> | ||||||
|  |       <el-form-item> | ||||||
|  |         <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> | ||||||
|  |         <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button> | ||||||
|  |       </el-form-item> | ||||||
|  |     </el-form> | ||||||
|  |  | ||||||
|  |     <div class="public-account-management clearfix" v-loading="loading"> | ||||||
|  |       <!--左边配置菜单--> | ||||||
|  |       <div class="left"> | ||||||
|  |         <div class="weixin-hd"> | ||||||
|  |           <div class="weixin-title">{{ name }}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="weixin-menu menu_main clearfix"> | ||||||
|  |           <div class="menu_bottom" v-for="(item, i) of menuList" :key="i"> | ||||||
|  |             <!-- 一级菜单 --> | ||||||
|  |             <div @click="menuClick(i, item)" class="menu_item" :class="{ active: isActive === i }" | ||||||
|  |               ><Icon icon="ep:fold" color="black" />{{ item.name }} | ||||||
|  |             </div> | ||||||
|  |             <!-- 以下为二级菜单--> | ||||||
|  |             <div class="submenu" v-if="isSubMenuFlag === i"> | ||||||
|  |               <div class="subtitle menu_bottom" v-for="(subItem, k) in item.children" :key="k"> | ||||||
|  |                 <div | ||||||
|  |                   class="menu_subItem" | ||||||
|  |                   v-if="item.children" | ||||||
|  |                   :class="{ active: isSubMenuActive === i + '' + k }" | ||||||
|  |                   @click="subMenuClick(subItem, i, k)" | ||||||
|  |                 > | ||||||
|  |                   {{ subItem.name }} | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |               <!-- 二级菜单加号, 当长度 小于 5 才显示二级菜单的加号  --> | ||||||
|  |               <div | ||||||
|  |                 class="menu_bottom menu_addicon" | ||||||
|  |                 v-if="!item.children || item.children.length < 5" | ||||||
|  |                 @click="addSubMenu(i, item)" | ||||||
|  |               > | ||||||
|  |                 <Icon icon="ep:plus" /> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |           <!-- 一级菜单加号 --> | ||||||
|  |           <div class="menu_bottom menu_addicon" v-if="menuList.length < 3" @click="addMenu"> | ||||||
|  |             <Icon icon="ep:plus" /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="save_div"> | ||||||
|  |           <el-button | ||||||
|  |             class="save_btn" | ||||||
|  |             type="success" | ||||||
|  |             size="small" | ||||||
|  |             @click="handleSave" | ||||||
|  |             v-hasPermi="['mp:menu:save']" | ||||||
|  |             >保存并发布菜单</el-button | ||||||
|  |           > | ||||||
|  |           <el-button | ||||||
|  |             class="save_btn" | ||||||
|  |             type="danger" | ||||||
|  |             size="small" | ||||||
|  |             @click="handleDelete" | ||||||
|  |             v-hasPermi="['mp:menu:delete']" | ||||||
|  |             >清空菜单</el-button | ||||||
|  |           > | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <!--右边配置--> | ||||||
|  |       <div v-if="showRightFlag" class="right"> | ||||||
|  |         <div class="configure_page"> | ||||||
|  |           <div class="delete_btn"> | ||||||
|  |             <el-button size="small" type="danger" @click="handleDeleteMenu(tempObj)" | ||||||
|  |               >删除当前菜单<Icon icon="ep:delete" | ||||||
|  |             /></el-button> | ||||||
|  |           </div> | ||||||
|  |           <div> | ||||||
|  |             <span>菜单名称:</span> | ||||||
|  |             <el-input | ||||||
|  |               class="input_width" | ||||||
|  |               v-model="tempObj.name" | ||||||
|  |               placeholder="请输入菜单名称" | ||||||
|  |               :maxlength="nameMaxLength" | ||||||
|  |               clearable | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |           <div v-if="showConfigureContent"> | ||||||
|  |             <div class="menu_content"> | ||||||
|  |               <span>菜单标识:</span> | ||||||
|  |               <el-input | ||||||
|  |                 class="input_width" | ||||||
|  |                 v-model="tempObj.menuKey" | ||||||
|  |                 placeholder="请输入菜单 KEY" | ||||||
|  |                 clearable | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  |             <div class="menu_content"> | ||||||
|  |               <span>菜单内容:</span> | ||||||
|  |               <el-select v-model="tempObj.type" clearable placeholder="请选择" class="menu_option"> | ||||||
|  |                 <el-option | ||||||
|  |                   v-for="item in menuOptions" | ||||||
|  |                   :label="item.label" | ||||||
|  |                   :value="item.value" | ||||||
|  |                   :key="item.value" | ||||||
|  |                 /> | ||||||
|  |               </el-select> | ||||||
|  |             </div> | ||||||
|  |             <div class="configur_content" v-if="tempObj.type === 'view'"> | ||||||
|  |               <span>跳转链接:</span> | ||||||
|  |               <el-input | ||||||
|  |                 class="input_width" | ||||||
|  |                 v-model="tempObj.url" | ||||||
|  |                 placeholder="请输入链接" | ||||||
|  |                 clearable | ||||||
|  |               /> | ||||||
|  |             </div> | ||||||
|  |             <div class="configur_content" v-if="tempObj.type === 'miniprogram'"> | ||||||
|  |               <div class="applet"> | ||||||
|  |                 <span>小程序的 appid :</span> | ||||||
|  |                 <el-input | ||||||
|  |                   class="input_width" | ||||||
|  |                   v-model="tempObj.miniProgramAppId" | ||||||
|  |                   placeholder="请输入小程序的appid" | ||||||
|  |                   clearable | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |               <div class="applet"> | ||||||
|  |                 <span>小程序的页面路径:</span> | ||||||
|  |                 <el-input | ||||||
|  |                   class="input_width" | ||||||
|  |                   v-model="tempObj.miniProgramPagePath" | ||||||
|  |                   placeholder="请输入小程序的页面路径,如:pages/index" | ||||||
|  |                   clearable | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |               <div class="applet"> | ||||||
|  |                 <span>小程序的备用网页:</span> | ||||||
|  |                 <el-input | ||||||
|  |                   class="input_width" | ||||||
|  |                   v-model="tempObj.url" | ||||||
|  |                   placeholder="不支持小程序的老版本客户端将打开本网页" | ||||||
|  |                   clearable | ||||||
|  |                 /> | ||||||
|  |               </div> | ||||||
|  |               <p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p> | ||||||
|  |             </div> | ||||||
|  |             <div class="configur_content" v-if="tempObj.type === 'article_view_limited'"> | ||||||
|  |               <el-row> | ||||||
|  |                 <div class="select-item" v-if="tempObj && tempObj.replyArticles"> | ||||||
|  |                   <WxNews :articles="tempObj.replyArticles" /> | ||||||
|  |                   <el-row class="ope-row" justify="center" align="middle"> | ||||||
|  |                     <el-button type="danger" circle @click="deleteMaterial" | ||||||
|  |                       ><icon icon="ep:delete" | ||||||
|  |                     /></el-button> | ||||||
|  |                   </el-row> | ||||||
|  |                 </div> | ||||||
|  |                 <div v-else> | ||||||
|  |                   <el-row justify="center"> | ||||||
|  |                     <el-col :span="24" style="text-align: center"> | ||||||
|  |                       <el-button type="success" @click="openMaterial"> | ||||||
|  |                         素材库选择<Icon icon="ep:circle-check" /> | ||||||
|  |                       </el-button> | ||||||
|  |                     </el-col> | ||||||
|  |                   </el-row> | ||||||
|  |                 </div> | ||||||
|  |                 <el-dialog title="选择图文" v-model="dialogNewsVisible" width="90%"> | ||||||
|  |                   <WxMaterialSelect | ||||||
|  |                     :objData="{ type: 'news', accountId: accountId }" | ||||||
|  |                     @select-material="selectMaterial" | ||||||
|  |                   /> | ||||||
|  |                 </el-dialog> | ||||||
|  |               </el-row> | ||||||
|  |             </div> | ||||||
|  |             <div | ||||||
|  |               class="configur_content" | ||||||
|  |               v-if="tempObj.type === 'click' || tempObj.type === 'scancode_waitmsg'" | ||||||
|  |             > | ||||||
|  |               <WxReplySelect :objData="tempObj.reply" v-if="hackResetWxReplySelect" /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <!-- 一进页面就显示的默认页面,当点击左边按钮的时候,就不显示了--> | ||||||
|  |       <div v-else class="right"> | ||||||
|  |         <p>请选择菜单配置</p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { ref, nextTick } from 'vue' | ||||||
|  | import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' | ||||||
|  | import WxNews from '@/views/mp/components/wx-news/main.vue' | ||||||
|  | import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' | ||||||
|  | import { deleteMenu, getMenuList, saveMenu } from '@/api/mp/menu' | ||||||
|  | import { getSimpleAccountList } from '@/api/mp/account' | ||||||
|  | import { handleTree } from '@/utils/tree' | ||||||
|  | import menuOptions from './menuOptions' | ||||||
|  |  | ||||||
|  | const message = useMessage() | ||||||
|  |  | ||||||
|  | // ======================== 列表查询 ======================== | ||||||
|  | // 遮罩层 | ||||||
|  | const loading = ref(true) | ||||||
|  | // 显示搜索条件 | ||||||
|  | const showSearch = ref(true) | ||||||
|  | // 公众号Id | ||||||
|  | const accountId = ref(undefined) | ||||||
|  | // 公众号名 | ||||||
|  | const name = ref('') | ||||||
|  | const menuList = ref({ children: [] }) | ||||||
|  |  | ||||||
|  | // const menuList = ref(menuListData) | ||||||
|  | // ======================== 菜单操作 ======================== | ||||||
|  | const isActive = ref(-1) // 一级菜单点中样式 | ||||||
|  | const isSubMenuActive = ref(-1) // 一级菜单点中样式 | ||||||
|  | const isSubMenuFlag = ref(-1) // 二级菜单显示标志 | ||||||
|  |  | ||||||
|  | // ======================== 菜单编辑 ======================== | ||||||
|  | const showRightFlag = ref(false) // 右边配置显示默认详情还是配置详情 | ||||||
|  | const nameMaxLength = ref(0) // 菜单名称最大长度;1 级是 4 字符;2 级是 7 字符; | ||||||
|  | const showConfigureContent = ref(true) // 是否展示配置内容;如果有子菜单,就不显示配置内容 | ||||||
|  | const hackResetWxReplySelect = ref(false) // 重置 WxReplySelect 组件 | ||||||
|  | const tempObj = ref({}) // 右边临时变量,作为中间值牵引关系 | ||||||
|  |  | ||||||
|  | const tempSelfObj = ref({ | ||||||
|  |   // 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数 | ||||||
|  | }) | ||||||
|  | const dialogNewsVisible = ref(false) // 跳转图文时的素材选择弹窗 | ||||||
|  |  | ||||||
|  | // 公众号账号列表 | ||||||
|  | const accountList = ref([]) | ||||||
|  |  | ||||||
|  | onMounted(async () => { | ||||||
|  |   accountList.value = await getSimpleAccountList() | ||||||
|  |   // 选中第一个 | ||||||
|  |   if (accountList.value.length > 0) { | ||||||
|  |     // @ts-ignore | ||||||
|  |     setAccountId(accountList.value[0].id) | ||||||
|  |   } | ||||||
|  |   await getList() | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | // ======================== 列表查询 ======================== | ||||||
|  | /** 设置账号编号 */ | ||||||
|  | const setAccountId = (id) => { | ||||||
|  |   accountId.value = id | ||||||
|  |   name.value = accountList.value.find((item) => item.id === accountId.value)?.name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const getList = async () => { | ||||||
|  |   loading.value = false | ||||||
|  |   getMenuList(accountId.value) | ||||||
|  |     .then((response) => { | ||||||
|  |       const menuData = convertMenuList(response) | ||||||
|  |       menuList.value = handleTree(menuData, 'id') | ||||||
|  |     }) | ||||||
|  |     .finally(() => { | ||||||
|  |       loading.value = false | ||||||
|  |     }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** 搜索按钮操作 */ | ||||||
|  | const handleQuery = () => { | ||||||
|  |   resetForm() | ||||||
|  |   // 默认选中第一个 | ||||||
|  |   if (accountId.value) { | ||||||
|  |     setAccountId(accountId.value) | ||||||
|  |   } | ||||||
|  |   getList() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** 重置按钮操作 */ | ||||||
|  | const resetQuery = () => { | ||||||
|  |   resetForm() | ||||||
|  |   // 默认选中第一个 | ||||||
|  |   if (accountList.value.length > 0) { | ||||||
|  |     setAccountId(accountList.value[0].id) | ||||||
|  |   } | ||||||
|  |   handleQuery() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 将后端返回的 menuList,转换成前端的 menuList | ||||||
|  | const convertMenuList = (list) => { | ||||||
|  |   if (!list) return [] | ||||||
|  |  | ||||||
|  |   const menuList = [] | ||||||
|  |   list.forEach((item) => { | ||||||
|  |     const menu = { | ||||||
|  |       ...item | ||||||
|  |     } | ||||||
|  |     if (item.type === 'click' || item.type === 'scancode_waitmsg') { | ||||||
|  |       delete menu.replyMessageType | ||||||
|  |       delete menu.replyContent | ||||||
|  |       delete menu.replyMediaId | ||||||
|  |       delete menu.replyMediaUrl | ||||||
|  |       delete menu.replyDescription | ||||||
|  |       delete menu.replyArticles | ||||||
|  |       menu.reply = { | ||||||
|  |         type: item.replyMessageType, | ||||||
|  |         accountId: item.accountId, | ||||||
|  |         content: item.replyContent, | ||||||
|  |         mediaId: item.replyMediaId, | ||||||
|  |         url: item.replyMediaUrl, | ||||||
|  |         title: item.replyTitle, | ||||||
|  |         description: item.replyDescription, | ||||||
|  |         thumbMediaId: item.replyThumbMediaId, | ||||||
|  |         thumbMediaUrl: item.replyThumbMediaUrl, | ||||||
|  |         articles: item.replyArticles, | ||||||
|  |         musicUrl: item.replyMusicUrl, | ||||||
|  |         hqMusicUrl: item.replyHqMusicUrl | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     menuList.push(menu) | ||||||
|  |   }) | ||||||
|  |   return menuList | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 重置表单,清空表单数据 | ||||||
|  | const resetForm = () => { | ||||||
|  |   // 菜单操作 | ||||||
|  |   isActive.value = -1 | ||||||
|  |   isSubMenuActive.value = -1 | ||||||
|  |   isSubMenuFlag.value = -1 | ||||||
|  |  | ||||||
|  |   // 菜单编辑 | ||||||
|  |   showRightFlag.value = false | ||||||
|  |   nameMaxLength.value = 0 | ||||||
|  |   showConfigureContent.value = 0 | ||||||
|  |   hackResetWxReplySelect.value = false | ||||||
|  |   tempObj.value = {} | ||||||
|  |   tempSelfObj.value = {} | ||||||
|  |   dialogNewsVisible.value = false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ======================== 菜单操作 ======================== | ||||||
|  | // 一级菜单点击事件 | ||||||
|  | const menuClick = (i, item) => { | ||||||
|  |   // 右侧的表单相关 | ||||||
|  |   resetEditor() | ||||||
|  |   showRightFlag.value = true // 右边菜单 | ||||||
|  |   tempObj.value = item // 这个如果放在顶部,flag 会没有。因为重新赋值了。 | ||||||
|  |   tempSelfObj.value.grand = '1' // 表示一级菜单 | ||||||
|  |   tempSelfObj.value.index = i // 表示一级菜单索引 | ||||||
|  |   nameMaxLength.value = 4 | ||||||
|  |   showConfigureContent.value = !(item.children && item.children.length > 0) // 有子菜单,就不显示配置内容 | ||||||
|  |  | ||||||
|  |   // 左侧的选中 | ||||||
|  |   isActive.value = i // 一级菜单选中样式 | ||||||
|  |   isSubMenuFlag.value = i // 二级菜单显示标志 | ||||||
|  |   isSubMenuActive.value = -1 // 二级菜单去除选中样式 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 二级菜单点击事件 | ||||||
|  | const subMenuClick = (subItem, index, k) => { | ||||||
|  |   // 右侧的表单相关 | ||||||
|  |   resetEditor() | ||||||
|  |   showRightFlag.value = true // 右边菜单 | ||||||
|  |   console.log(subItem) | ||||||
|  |   tempObj.value = subItem // 将点击的数据放到临时变量,对象有引用作用 | ||||||
|  |   tempSelfObj.value.grand = '2' // 表示二级菜单 | ||||||
|  |   tempSelfObj.value.index = index // 表示一级菜单索引 | ||||||
|  |   tempSelfObj.value.secondIndex = k // 表示二级菜单索引 | ||||||
|  |   nameMaxLength.value = 7 | ||||||
|  |   showConfigureContent.value = true | ||||||
|  |  | ||||||
|  |   // 左侧的选中 | ||||||
|  |   isActive.value = -1 // 一级菜单去除样式 | ||||||
|  |   isSubMenuActive.value = index + '' + k // 二级菜单选中样式 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 添加横向一级菜单 | ||||||
|  | const addMenu = () => { | ||||||
|  |   const menuKeyLength = menuList.value.length | ||||||
|  |   const addButton = { | ||||||
|  |     name: '菜单名称', | ||||||
|  |     children: [], | ||||||
|  |     reply: { | ||||||
|  |       // 用于存储回复内容 | ||||||
|  |       type: 'text', | ||||||
|  |       accountId: accountId.value // 保证组件里,可以使用到对应的公众号 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   menuList.value[menuKeyLength] = addButton | ||||||
|  |   menuClick(menuKeyLength.value - 1, addButton) | ||||||
|  | } | ||||||
|  | // 添加横向二级菜单;item 表示要操作的父菜单 | ||||||
|  | const addSubMenu = (i, item) => { | ||||||
|  |   // 清空父菜单的属性,因为它只需要 name 属性即可 | ||||||
|  |   if (!item.children || item.children.length <= 0) { | ||||||
|  |     item.children = [] | ||||||
|  |     delete item['type'] | ||||||
|  |     delete item['menuKey'] | ||||||
|  |     delete item['miniProgramAppId'] | ||||||
|  |     delete item['miniProgramPagePath'] | ||||||
|  |     delete item['url'] | ||||||
|  |     delete item['reply'] | ||||||
|  |     delete item['articleId'] | ||||||
|  |     delete item['replyArticles'] | ||||||
|  |     // 关闭配置面板 | ||||||
|  |     showConfigureContent.value = false | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   let subMenuKeyLength = item.children.length // 获取二级菜单key长度 | ||||||
|  |   let addButton = { | ||||||
|  |     name: '子菜单名称', | ||||||
|  |     reply: { | ||||||
|  |       // 用于存储回复内容 | ||||||
|  |       type: 'text', | ||||||
|  |       accountId: accountId.value // 保证组件里,可以使用到对应的公众号 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   item.children[subMenuKeyLength] = addButton | ||||||
|  |   subMenuClick(item.children[subMenuKeyLength], i, subMenuKeyLength) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 删除当前菜单 | ||||||
|  | const handleDeleteMenu = async () => { | ||||||
|  |   try { | ||||||
|  |     await message.confirm('确定要删除吗?') | ||||||
|  |     if (tempSelfObj.value.grand === '1') { | ||||||
|  |       // 一级菜单的删除方法 | ||||||
|  |       menuList.value.splice(tempSelfObj.value.index, 1) | ||||||
|  |     } else if (tempSelfObj.value.grand === '2') { | ||||||
|  |       // 二级菜单的删除方法 | ||||||
|  |       menuList.value[tempSelfObj.value.index].children.splice(tempSelfObj.value.secondIndex, 1) | ||||||
|  |     } | ||||||
|  |     // 提示 | ||||||
|  |     message.notifySuccess('删除成功') | ||||||
|  |  | ||||||
|  |     // 处理菜单的选中 | ||||||
|  |     tempObj.value = {} | ||||||
|  |     showRightFlag.value = false | ||||||
|  |     isActive.value = -1 | ||||||
|  |     isSubMenuActive.value = -1 | ||||||
|  |   } catch {} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ======================== 菜单编辑 ======================== | ||||||
|  | const handleSave = async () => { | ||||||
|  |   try { | ||||||
|  |     await message.confirm('确定要删除吗?') | ||||||
|  |     loading.value = true | ||||||
|  |     await saveMenu(accountId.value, convertMenuFormList()) | ||||||
|  |     getList() | ||||||
|  |     message.notifySuccess('发布成功') | ||||||
|  |   } finally { | ||||||
|  |     loading.value = false | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 表单 Editor 重置 | ||||||
|  | const resetEditor = () => { | ||||||
|  |   hackResetWxReplySelect.value = false // 销毁组件 | ||||||
|  |   nextTick(() => { | ||||||
|  |     console.log('nextTick') | ||||||
|  |     hackResetWxReplySelect.value = true // 重建组件 | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const handleDelete = async () => { | ||||||
|  |   try { | ||||||
|  |     await message.confirm('确定要删除吗?') | ||||||
|  |     loading.value = true | ||||||
|  |     await deleteMenu(accountId.value) | ||||||
|  |     handleQuery() | ||||||
|  |     message.notifySuccess('清空成功') | ||||||
|  |   } finally { | ||||||
|  |     loading.value = false | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 将前端的 menuList,转换成后端接收的 menuList | ||||||
|  | const convertMenuFormList = () => { | ||||||
|  |   const result = [] | ||||||
|  |   menuList.value.forEach((item) => { | ||||||
|  |     let menu = convertMenuForm(item) | ||||||
|  |     result.push(menu) | ||||||
|  |  | ||||||
|  |     // 处理子菜单 | ||||||
|  |     if (!item.children || item.children.length <= 0) { | ||||||
|  |       return | ||||||
|  |     } | ||||||
|  |     menu.children = [] | ||||||
|  |     item.children.forEach((subItem) => { | ||||||
|  |       menu.children.push(convertMenuForm(subItem)) | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  |   return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 将前端的 menu,转换成后端接收的 menu | ||||||
|  | const convertMenuForm = (menu) => { | ||||||
|  |   let result = { | ||||||
|  |     ...menu, | ||||||
|  |     children: undefined, // 不处理子节点 | ||||||
|  |     reply: undefined // 稍后复制 | ||||||
|  |   } | ||||||
|  |   if (menu.type === 'click' || menu.type === 'scancode_waitmsg') { | ||||||
|  |     result.replyMessageType = menu.reply.type | ||||||
|  |     result.replyContent = menu.reply.content | ||||||
|  |     result.replyMediaId = menu.reply.mediaId | ||||||
|  |     result.replyMediaUrl = menu.reply.url | ||||||
|  |     result.replyTitle = menu.reply.title | ||||||
|  |     result.replyDescription = menu.reply.description | ||||||
|  |     result.replyThumbMediaId = menu.reply.thumbMediaId | ||||||
|  |     result.replyThumbMediaUrl = menu.reply.thumbMediaUrl | ||||||
|  |     result.replyArticles = menu.reply.articles | ||||||
|  |     result.replyMusicUrl = menu.reply.musicUrl | ||||||
|  |     result.replyHqMusicUrl = menu.reply.hqMusicUrl | ||||||
|  |   } | ||||||
|  |   return result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ======================== 菜单编辑(素材选择) ======================== | ||||||
|  | const openMaterial = () => { | ||||||
|  |   dialogNewsVisible.value = true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const selectMaterial = (item) => { | ||||||
|  |   const articleId = item.articleId | ||||||
|  |   const articles = item.content.newsItem | ||||||
|  |   // 提示,针对多图文 | ||||||
|  |   if (articles.length > 1) { | ||||||
|  |     message.alertWarning('您选择的是多图文,将默认跳转第一篇') | ||||||
|  |   } | ||||||
|  |   dialogNewsVisible.value = false | ||||||
|  |  | ||||||
|  |   // 设置菜单的回复 | ||||||
|  |   tempObj.value.articleId = articleId | ||||||
|  |   tempObj.value.replyArticles = [] | ||||||
|  |   articles.forEach((article) => { | ||||||
|  |     tempObj.value.replyArticles.push({ | ||||||
|  |       title: article.title, | ||||||
|  |       description: article.digest, | ||||||
|  |       picUrl: article.picUrl, | ||||||
|  |       url: article.url | ||||||
|  |     }) | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const deleteMaterial = () => { | ||||||
|  |   delete tempObj.value['articleId'] | ||||||
|  |   delete tempObj.value['replyArticles'] | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <!--本组件样式--> | ||||||
|  | <style lang="scss" scoped="scoped"> | ||||||
|  | /* 公共颜色变量 */ | ||||||
|  | .clearfix { | ||||||
|  |   *zoom: 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .clearfix::after { | ||||||
|  |   content: ''; | ||||||
|  |   display: table; | ||||||
|  |   clear: both; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | div { | ||||||
|  |   text-align: left; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .weixin-hd { | ||||||
|  |   color: #fff; | ||||||
|  |   text-align: center; | ||||||
|  |   position: relative; | ||||||
|  |   bottom: 426px; | ||||||
|  |   left: 0px; | ||||||
|  |   width: 300px; | ||||||
|  |   height: 64px; | ||||||
|  |   background: transparent url('./assets/menu_head.png') no-repeat 0 0; | ||||||
|  |   background-position: 0 0; | ||||||
|  |   background-size: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .weixin-title { | ||||||
|  |   color: #fff; | ||||||
|  |   font-size: 14px; | ||||||
|  |   width: 100%; | ||||||
|  |   text-align: center; | ||||||
|  |   position: absolute; | ||||||
|  |   top: 33px; | ||||||
|  |   left: 0px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .weixin-menu { | ||||||
|  |   background: transparent url('./assets/menu_foot.png') no-repeat 0 0; | ||||||
|  |   padding-left: 43px; | ||||||
|  |   font-size: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .menu_option { | ||||||
|  |   width: 40% !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .public-account-management { | ||||||
|  |   min-width: 1200px; | ||||||
|  |   width: 1200px; | ||||||
|  |   margin: 0 auto; | ||||||
|  |  | ||||||
|  |   .left { | ||||||
|  |     float: left; | ||||||
|  |     display: inline-block; | ||||||
|  |     width: 350px; | ||||||
|  |     height: 715px; | ||||||
|  |     background: url('./assets/iphone_backImg.png') no-repeat; | ||||||
|  |     background-size: 100% auto; | ||||||
|  |     padding: 518px 25px 88px; | ||||||
|  |     position: relative; | ||||||
|  |     box-sizing: border-box; | ||||||
|  |  | ||||||
|  |     /*第一级菜单*/ | ||||||
|  |     .menu_main { | ||||||
|  |       .menu_bottom { | ||||||
|  |         position: relative; | ||||||
|  |         float: left; | ||||||
|  |         display: inline-block; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         width: 85.5px; | ||||||
|  |         text-align: center; | ||||||
|  |         border: 1px solid #ebedee; | ||||||
|  |         background-color: #fff; | ||||||
|  |         cursor: pointer; | ||||||
|  |  | ||||||
|  |         &.menu_addicon { | ||||||
|  |           height: 46px; | ||||||
|  |           line-height: 46px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         .menu_item { | ||||||
|  |           height: 44px; | ||||||
|  |           line-height: 44px; | ||||||
|  |           // text-align: center; | ||||||
|  |           box-sizing: border-box; | ||||||
|  |           width: 100%; | ||||||
|  |           display: flex; | ||||||
|  |           align-items: center; | ||||||
|  |           justify-content: center; | ||||||
|  |  | ||||||
|  |           &.active { | ||||||
|  |             border: 1px solid #2bb673; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         .menu_subItem { | ||||||
|  |           height: 44px; | ||||||
|  |           line-height: 44px; | ||||||
|  |           text-align: center; | ||||||
|  |           box-sizing: border-box; | ||||||
|  |  | ||||||
|  |           &.active { | ||||||
|  |             border: 1px solid #2bb673; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       i { | ||||||
|  |         color: #2bb673; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       /*第二级菜单*/ | ||||||
|  |       .submenu { | ||||||
|  |         position: absolute; | ||||||
|  |         width: 85.5px; | ||||||
|  |         bottom: 45px; | ||||||
|  |  | ||||||
|  |         .subtitle { | ||||||
|  |           background-color: #fff; | ||||||
|  |           box-sizing: border-box; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     .save_div { | ||||||
|  |       margin-top: 15px; | ||||||
|  |       text-align: center; | ||||||
|  |  | ||||||
|  |       .save_btn { | ||||||
|  |         bottom: 20px; | ||||||
|  |         left: 100px; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /*右边菜单内容*/ | ||||||
|  |   .right { | ||||||
|  |     float: left; | ||||||
|  |     width: 63%; | ||||||
|  |     background-color: #e8e7e7; | ||||||
|  |     padding: 20px; | ||||||
|  |     margin-left: 20px; | ||||||
|  |     -webkit-box-sizing: border-box; | ||||||
|  |     box-sizing: border-box; | ||||||
|  |  | ||||||
|  |     .configure_page { | ||||||
|  |       .delete_btn { | ||||||
|  |         text-align: right; | ||||||
|  |         margin-bottom: 15px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .menu_content { | ||||||
|  |         margin-top: 20px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .configur_content { | ||||||
|  |         margin-top: 20px; | ||||||
|  |         background-color: #fff; | ||||||
|  |         padding: 20px 10px; | ||||||
|  |         border-radius: 5px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .blue { | ||||||
|  |         color: #29b6f6; | ||||||
|  |         margin-top: 10px; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .applet { | ||||||
|  |         margin-bottom: 20px; | ||||||
|  |  | ||||||
|  |         span { | ||||||
|  |           width: 20%; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .input_width { | ||||||
|  |         width: 40%; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       .material { | ||||||
|  |         .input_width { | ||||||
|  |           width: 30%; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         .el-textarea { | ||||||
|  |           width: 80%; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .el-input { | ||||||
|  |     width: 70%; | ||||||
|  |     margin-right: 2%; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | <!--素材样式--> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .pagination { | ||||||
|  |   text-align: right; | ||||||
|  |   margin-right: 25px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .select-item { | ||||||
|  |   width: 280px; | ||||||
|  |   padding: 10px; | ||||||
|  |   margin: 0 auto 10px auto; | ||||||
|  |   border: 1px solid #eaeaea; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .select-item2 { | ||||||
|  |   padding: 10px; | ||||||
|  |   margin: 0 auto 10px auto; | ||||||
|  |   border: 1px solid #eaeaea; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .ope-row { | ||||||
|  |   padding-top: 10px; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .item-name { | ||||||
|  |   font-size: 12px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   text-overflow: ellipsis; | ||||||
|  |   white-space: nowrap; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								src/views/mp/menu/menuOptions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/views/mp/menu/menuOptions.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | export default [ | ||||||
|  |   { | ||||||
|  |     value: 'view', | ||||||
|  |     label: '跳转网页' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'miniprogram', | ||||||
|  |     label: '跳转小程序' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'click', | ||||||
|  |     label: '点击回复' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'article_view_limited', | ||||||
|  |     label: '跳转图文消息' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'scancode_push', | ||||||
|  |     label: '扫码直接返回结果' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'scancode_waitmsg', | ||||||
|  |     label: '扫码回复' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'pic_sysphoto', | ||||||
|  |     label: '系统拍照发图' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'pic_photo_or_album', | ||||||
|  |     label: '拍照或者相册' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'pic_weixin', | ||||||
|  |     label: '微信相册' | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     value: 'location_select', | ||||||
|  |     label: '选择地理位置' | ||||||
|  |   } | ||||||
|  | ] | ||||||
		Reference in New Issue
	
	Block a user
	 dhb52
					dhb52