Commit 02ec5d455c20ea4e0e9d6d87334331acedfdccc6

Authored by YunaiV
1 parent f271ef8c

mp:1)引入 less-loader,解决 menu 的 less 样式;2)接入菜单列表接口

yudao-module-mp/yudao-module-mp-api/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java
@@ -51,7 +51,7 @@ public interface ErrorCodeConstants { @@ -51,7 +51,7 @@ public interface ErrorCodeConstants {
51 ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}"); 51 ErrorCode DRAFT_UPDATE_FAIL = new ErrorCode(1006007002, "更新草稿失败,原因:{}");
52 ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}"); 52 ErrorCode DRAFT_DELETE_FAIL = new ErrorCode(1006007002, "删除草稿失败,原因:{}");
53 53
54 - // TODO 要处理下  
55 - ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006001002, "菜单不存在"); 54 + // ========== 公众号菜单 1006008000============
  55 + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1006008000, "菜单不存在");
56 56
57 } 57 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http
@@ -32,3 +32,8 @@ tenant-id: {{adminTenentId}} @@ -32,3 +32,8 @@ tenant-id: {{adminTenentId}}
32 }] 32 }]
33 }] 33 }]
34 } 34 }
  35 +
  36 +### 请求 /mp/menu/list 接口 => 成功
  37 +GET {{baseUrl}}/mp/menu/list?accountId=1
  38 +Authorization: Bearer {{token}}
  39 +tenant-id: {{adminTenentId}}
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuRespVO;
  5 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
3 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert; 6 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert;
4 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 7 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
  8 +import cn.iocoder.yudao.module.mp.service.menu.MpMenuService;
  9 +import io.swagger.annotations.Api;
  10 +import io.swagger.annotations.ApiImplicitParam;
  11 +import io.swagger.annotations.ApiOperation;
  12 +import org.springframework.security.access.prepost.PreAuthorize;
  13 +import org.springframework.validation.annotation.Validated;
5 import org.springframework.web.bind.annotation.*; 14 import org.springframework.web.bind.annotation.*;
6 15
7 import javax.annotation.Resource; 16 import javax.annotation.Resource;
  17 +import javax.validation.Valid;
8 18
9 -import org.springframework.validation.annotation.Validated;  
10 -import org.springframework.security.access.prepost.PreAuthorize;  
11 -import io.swagger.annotations.*;  
12 -  
13 -import javax.validation.*;  
14 -  
15 -import cn.iocoder.yudao.framework.common.pojo.CommonResult; 19 +import java.util.List;
16 20
17 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 21 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
18 22
19 -import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;  
20 -import cn.iocoder.yudao.module.mp.service.menu.MpMenuService;  
21 -  
22 -@Api(tags = "管理后台 - 微信菜单") 23 +@Api(tags = "管理后台 - 公众号菜单")
23 @RestController 24 @RestController
24 @RequestMapping("/mp/menu") 25 @RequestMapping("/mp/menu")
25 @Validated 26 @Validated
@@ -29,14 +30,14 @@ public class MpMenuController { @@ -29,14 +30,14 @@ public class MpMenuController {
29 private MpMenuService mpMenuService; 30 private MpMenuService mpMenuService;
30 31
31 @PostMapping("/save") 32 @PostMapping("/save")
32 - @ApiOperation("保存微信菜单") 33 + @ApiOperation("保存公众号菜单")
33 @PreAuthorize("@ss.hasPermission('mp:menu:save')") 34 @PreAuthorize("@ss.hasPermission('mp:menu:save')")
34 public CommonResult<Long> saveMenu(@Valid @RequestBody MpMenuSaveReqVO createReqVO) { 35 public CommonResult<Long> saveMenu(@Valid @RequestBody MpMenuSaveReqVO createReqVO) {
35 return success(mpMenuService.saveMenu(createReqVO)); 36 return success(mpMenuService.saveMenu(createReqVO));
36 } 37 }
37 38
38 @DeleteMapping("/delete") 39 @DeleteMapping("/delete")
39 - @ApiOperation("删除微信菜单") 40 + @ApiOperation("删除公众号菜单")
40 @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) 41 @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
41 @PreAuthorize("@ss.hasPermission('mp:menu:delete')") 42 @PreAuthorize("@ss.hasPermission('mp:menu:delete')")
42 public CommonResult<Boolean> deleteMenu(@RequestParam("id") Long id) { 43 public CommonResult<Boolean> deleteMenu(@RequestParam("id") Long id) {
@@ -45,7 +46,7 @@ public class MpMenuController { @@ -45,7 +46,7 @@ public class MpMenuController {
45 } 46 }
46 47
47 @GetMapping("/get") 48 @GetMapping("/get")
48 - @ApiOperation("获得微信菜单") 49 + @ApiOperation("获得公众号菜单")
49 @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) 50 @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
50 @PreAuthorize("@ss.hasPermission('mp:menu:query')") 51 @PreAuthorize("@ss.hasPermission('mp:menu:query')")
51 public CommonResult<MpMenuRespVO> getMenu(@RequestParam("id") Long id) { 52 public CommonResult<MpMenuRespVO> getMenu(@RequestParam("id") Long id) {
@@ -53,4 +54,13 @@ public class MpMenuController { @@ -53,4 +54,13 @@ public class MpMenuController {
53 return success(MpMenuConvert.INSTANCE.convert(menu)); 54 return success(MpMenuConvert.INSTANCE.convert(menu));
54 } 55 }
55 56
  57 + @GetMapping("/list")
  58 + @ApiOperation("获得公众号菜单列表")
  59 + @ApiImplicitParam(name = "accountId", value = "公众号账号的编号", required = true, example = "10", dataTypeClass = Long.class)
  60 + @PreAuthorize("@ss.hasPermission('mp:menu:query')")
  61 + public CommonResult<List<MpMenuRespVO>> getMenuList(@RequestParam("accountId") Long accountId) {
  62 + List<MpMenuDO> list = mpMenuService.getMenuListByAccountId(accountId);
  63 + return success(MpMenuConvert.INSTANCE.convertList(list));
  64 + }
  65 +
56 } 66 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/vo/MpMenuBaseVO.java
1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo; 1 package cn.iocoder.yudao.module.mp.controller.admin.menu.vo;
2 2
  3 +import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO;
  4 +import cn.iocoder.yudao.module.mp.dal.dataobject.message.MpMessageDO;
3 import io.swagger.annotations.ApiModelProperty; 5 import io.swagger.annotations.ApiModelProperty;
4 -import lombok.*;  
5 -import me.chanjar.weixin.common.bean.menu.WxMenuButton; 6 +import lombok.Data;
  7 +import me.chanjar.weixin.common.api.WxConsts;
6 8
7 import javax.validation.constraints.NotNull; 9 import javax.validation.constraints.NotNull;
8 import java.util.List; 10 import java.util.List;
9 11
  12 +// TODO 芋艿:完善 swagger 注解
10 /** 13 /**
11 * 微信菜单 Base VO,提供给添加、修改、详细的子 VO 使用 14 * 微信菜单 Base VO,提供给添加、修改、详细的子 VO 使用
12 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 15 * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
@@ -18,4 +21,112 @@ public class MpMenuBaseVO { @@ -18,4 +21,112 @@ public class MpMenuBaseVO {
18 @NotNull(message = "公众号账号的编号不能为空") 21 @NotNull(message = "公众号账号的编号不能为空")
19 private Long accountId; 22 private Long accountId;
20 23
  24 + /**
  25 + * 微信公众号 appid
  26 + *
  27 + * 冗余 {@link MpAccountDO#getAppId()}
  28 + */
  29 + private String appId;
  30 +
  31 + /**
  32 + * 菜单名称
  33 + */
  34 + private String name;
  35 + /**
  36 + * 菜单标识
  37 + *
  38 + * 支持多 DB 类型时,无法直接使用 key + @TableField("menuKey") 来实现转换,原因是 "menuKey" AS key 而存在报错
  39 + */
  40 + private String menuKey;
  41 + /**
  42 + * 父菜单编号
  43 + */
  44 + private Long parentId;
  45 + /**
  46 + * 排序
  47 + */
  48 + private Integer sort;
  49 +
  50 + // ========== 按钮操作 ==========
  51 +
  52 + /**
  53 + * 按钮类型
  54 + *
  55 + * 枚举 {@link WxConsts.MenuButtonType}
  56 + */
  57 + private String type;
  58 +
  59 + /**
  60 + * 网页链接
  61 + *
  62 + * 用户点击菜单可打开链接,不超过 1024 字节
  63 + *
  64 + * 类型为 {@link WxConsts.XmlMsgType} 的 VIEW、MINIPROGRAM
  65 + */
  66 + private String url;
  67 +
  68 + /**
  69 + * 小程序的 appId
  70 + *
  71 + * 类型为 {@link WxConsts.MenuButtonType} 的 MINIPROGRAM
  72 + */
  73 + private String miniProgramAppId;
  74 + /**
  75 + * 小程序的页面路径
  76 + *
  77 + * 类型为 {@link WxConsts.MenuButtonType} 的 MINIPROGRAM
  78 + */
  79 + private String miniProgramPagePath;
  80 +
  81 + // ========== 消息内容 ==========
  82 +
  83 + /**
  84 + * 消息类型
  85 + *
  86 + * 当 {@link #type} 为 CLICK、SCANCODE_WAITMSG
  87 + *
  88 + * 枚举 {@link WxConsts.XmlMsgType} 中的 TEXT、IMAGE、VOICE、VIDEO、NEWS
  89 + */
  90 + private String replyMessageType;
  91 +
  92 + /**
  93 + * 回复的消息内容
  94 + *
  95 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 TEXT
  96 + */
  97 + private String replyContent;
  98 +
  99 + /**
  100 + * 回复的媒体 id
  101 + *
  102 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
  103 + */
  104 + private String replyMediaId;
  105 + /**
  106 + * 回复的媒体 URL
  107 + *
  108 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 IMAGE、VOICE、VIDEO
  109 + */
  110 + private String replyMediaUrl;
  111 +
  112 + /**
  113 + * 回复的标题
  114 + *
  115 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
  116 + */
  117 + private String replyTitle;
  118 + /**
  119 + * 回复的描述
  120 + *
  121 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 VIDEO
  122 + */
  123 + private String replyDescription;
  124 +
  125 + /**
  126 + * 回复的图文消息数组
  127 + *
  128 + * 消息类型为 {@link WxConsts.XmlMsgType} 的 NEWS
  129 + */
  130 + private List<MpMessageDO.Article> replyArticles;
  131 +
21 } 132 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/convert/menu/MpMenuConvert.java
1 package cn.iocoder.yudao.module.mp.convert.menu; 1 package cn.iocoder.yudao.module.mp.convert.menu;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 - 3 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuRespVO;
  4 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
  5 +import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
5 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; 6 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
6 import org.mapstruct.Mapper; 7 import org.mapstruct.Mapper;
7 import org.mapstruct.Mapping; 8 import org.mapstruct.Mapping;
8 import org.mapstruct.Mappings; 9 import org.mapstruct.Mappings;
9 import org.mapstruct.factory.Mappers; 10 import org.mapstruct.factory.Mappers;
10 -import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;  
11 -import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 11 +
  12 +import java.util.List;
12 13
13 @Mapper 14 @Mapper
14 public interface MpMenuConvert { 15 public interface MpMenuConvert {
@@ -19,6 +20,8 @@ public interface MpMenuConvert { @@ -19,6 +20,8 @@ public interface MpMenuConvert {
19 20
20 MpMenuRespVO convert(MpMenuDO bean); 21 MpMenuRespVO convert(MpMenuDO bean);
21 22
  23 + List<MpMenuRespVO> convertList(List<MpMenuDO> list);
  24 +
22 @Mappings({ 25 @Mappings({
23 @Mapping(source = "menu.appId", target = "appId"), 26 @Mapping(source = "menu.appId", target = "appId"),
24 @Mapping(source = "menu.replyMessageType", target = "type"), 27 @Mapping(source = "menu.replyMessageType", target = "type"),
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/mysql/menu/MpMenuMapper.java
@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 4 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
6 6
  7 +import java.util.List;
  8 +
7 @Mapper 9 @Mapper
8 public interface MpMenuMapper extends BaseMapperX<MpMenuDO> { 10 public interface MpMenuMapper extends BaseMapperX<MpMenuDO> {
9 11
@@ -12,4 +14,8 @@ public interface MpMenuMapper extends BaseMapperX&lt;MpMenuDO&gt; { @@ -12,4 +14,8 @@ public interface MpMenuMapper extends BaseMapperX&lt;MpMenuDO&gt; {
12 MpMenuDO::getMenuKey, menuKey); 14 MpMenuDO::getMenuKey, menuKey);
13 } 15 }
14 16
  17 + default List<MpMenuDO> selectListByAccountId(Long accountId) {
  18 + return selectList(MpMenuDO::getAccountId, accountId);
  19 + }
  20 +
15 } 21 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/menu/MpMenuService.java
1 package cn.iocoder.yudao.module.mp.service.menu; 1 package cn.iocoder.yudao.module.mp.service.menu;
2 2
3 -import javax.validation.*;  
4 -  
5 -import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*; 3 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
6 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 4 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
7 -import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;  
8 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 5 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
9 6
  7 +import javax.validation.Valid;
  8 +import java.util.List;
  9 +
10 /** 10 /**
11 - * 微信菜单 Service 接口 11 + * 公众号菜单 Service 接口
12 * 12 *
13 * @author 芋道源码 13 * @author 芋道源码
14 */ 14 */
15 public interface MpMenuService { 15 public interface MpMenuService {
16 16
17 /** 17 /**
18 - * 保存微信菜单 18 + * 保存公众号菜单
19 * 19 *
20 * @param createReqVO 创建信息 20 * @param createReqVO 创建信息
21 * @return 编号 21 * @return 编号
@@ -23,17 +23,17 @@ public interface MpMenuService { @@ -23,17 +23,17 @@ public interface MpMenuService {
23 Long saveMenu(@Valid MpMenuSaveReqVO createReqVO); 23 Long saveMenu(@Valid MpMenuSaveReqVO createReqVO);
24 24
25 /** 25 /**
26 - * 删除微信菜单 26 + * 删除公众号菜单
27 * 27 *
28 * @param id 编号 28 * @param id 编号
29 */ 29 */
30 void deleteMenu(Long id); 30 void deleteMenu(Long id);
31 31
32 /** 32 /**
33 - * 获得微信菜单 33 + * 获得公众号菜单
34 * 34 *
35 * @param id 编号 35 * @param id 编号
36 - * @return 微信菜单 36 + * @return 公众号菜单
37 */ 37 */
38 MpMenuDO getMenu(Long id); 38 MpMenuDO getMenu(Long id);
39 39
@@ -47,4 +47,12 @@ public interface MpMenuService { @@ -47,4 +47,12 @@ public interface MpMenuService {
47 */ 47 */
48 WxMpXmlOutMessage reply(String appId, String key, String openid); 48 WxMpXmlOutMessage reply(String appId, String key, String openid);
49 49
  50 + /**
  51 + * 获得公众号菜单列表
  52 + *
  53 + * @param accountId 公众号编号
  54 + * @return 公众号菜单列表
  55 + */
  56 + List<MpMenuDO> getMenuListByAccountId(Long accountId);
  57 +
50 } 58 }
yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/menu/MpMenuServiceImpl.java
1 package cn.iocoder.yudao.module.mp.service.menu; 1 package cn.iocoder.yudao.module.mp.service.menu;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.MpMenuSaveReqVO;
4 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert; 5 import cn.iocoder.yudao.module.mp.convert.menu.MpMenuConvert;
5 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO; 6 import cn.iocoder.yudao.module.mp.dal.dataobject.menu.MpMenuDO;
  7 +import cn.iocoder.yudao.module.mp.dal.mysql.menu.MpMenuMapper;
6 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; 8 import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory;
7 import cn.iocoder.yudao.module.mp.service.message.MpMessageService; 9 import cn.iocoder.yudao.module.mp.service.message.MpMessageService;
8 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO; 10 import cn.iocoder.yudao.module.mp.service.message.bo.MpMessageSendOutReqBO;
@@ -13,20 +15,16 @@ import me.chanjar.weixin.mp.api.WxMpService; @@ -13,20 +15,16 @@ import me.chanjar.weixin.mp.api.WxMpService;
13 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 15 import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
14 import org.springframework.context.annotation.Lazy; 16 import org.springframework.context.annotation.Lazy;
15 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
16 -  
17 -import javax.annotation.Resource;  
18 -  
19 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
20 19
21 -import cn.iocoder.yudao.module.mp.controller.admin.menu.vo.*;  
22 -  
23 -import cn.iocoder.yudao.module.mp.dal.mysql.menu.MpMenuMapper; 20 +import javax.annotation.Resource;
  21 +import java.util.List;
24 22
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26 -import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.*; 24 +import static cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants.MENU_NOT_EXISTS;
27 25
28 /** 26 /**
29 - * 微信菜单 Service 实现类 27 + * 公众号菜单 Service 实现类
30 * 28 *
31 * @author 芋道源码 29 * @author 芋道源码
32 */ 30 */
@@ -76,7 +74,6 @@ public class MpMenuServiceImpl implements MpMenuService { @@ -76,7 +74,6 @@ public class MpMenuServiceImpl implements MpMenuService {
76 74
77 private void validateMenuExists(Long id) { 75 private void validateMenuExists(Long id) {
78 if (mpMenuMapper.selectById(id) == null) { 76 if (mpMenuMapper.selectById(id) == null) {
79 - // TODO 芋艿:错误码不太对  
80 throw exception(MENU_NOT_EXISTS); 77 throw exception(MENU_NOT_EXISTS);
81 } 78 }
82 } 79 }
@@ -105,4 +102,9 @@ public class MpMenuServiceImpl implements MpMenuService { @@ -105,4 +102,9 @@ public class MpMenuServiceImpl implements MpMenuService {
105 return mpMessageService.sendOutMessage(sendReqBO); 102 return mpMessageService.sendOutMessage(sendReqBO);
106 } 103 }
107 104
  105 + @Override
  106 + public List<MpMenuDO> getMenuListByAccountId(Long accountId) {
  107 + return mpMenuMapper.selectListByAccountId(accountId);
  108 + }
  109 +
108 } 110 }
yudao-ui-admin/package.json
@@ -90,6 +90,8 @@ @@ -90,6 +90,8 @@
90 "eslint-plugin-prettier": "^3.1.0", 90 "eslint-plugin-prettier": "^3.1.0",
91 "eslint-plugin-vue": "9.0.0", 91 "eslint-plugin-vue": "9.0.0",
92 "fs-extra": "^8.1.0", 92 "fs-extra": "^8.1.0",
  93 + "less": "^4.1.3",
  94 + "less-loader": "^7.3.0",
93 "lint-staged": "12.5.0", 95 "lint-staged": "12.5.0",
94 "runjs": "4.4.2", 96 "runjs": "4.4.2",
95 "sass": "1.32.13", 97 "sass": "1.32.13",
yudao-ui-admin/src/api/mp/menu.js 0 → 100644
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得公众号菜单列表
  4 +export function getMenuList(accountId) {
  5 + return request({
  6 + url: '/mp/menu/list?accountId=' + accountId,
  7 + method: 'get',
  8 + })
  9 +}
yudao-ui-admin/src/views/mp/menu/index.vue
@@ -23,344 +23,405 @@ SOFTWARE. @@ -23,344 +23,405 @@ SOFTWARE.
23 --> 23 -->
24 <template> 24 <template>
25 <div class="app-container"> 25 <div class="app-container">
26 - <div class="public-account-management clearfix" v-loading="saveLoading">  
27 - <!--左边配置菜单-->  
28 - <div class="left">  
29 - <div class="weixin-hd">  
30 - <div class="weixin-title">  
31 - {{menuName}}  
32 - </div>  
33 - </div>  
34 - <div class="weixin-menu menu_main clearfix">  
35 - <div class="menu_bottom" v-for="(item, i) of menu.button" :key="i" >  
36 - <!-- 一级菜单 -->  
37 - <div @click="menuFun(i,item)" class="menu_item el-icon-s-fold" :class="{'active': isActive == i}">{{item.name}}</div>  
38 - <!--以下为二级菜单-->  
39 - <div class="submenu" v-if="isSubMenuFlag == i">  
40 - <div class="subtitle menu_bottom" v-if="item.sub_button" v-for="(subItem, k) in item.sub_button" :key="k">  
41 - <div class="menu_subItem" :class="{'active': isSubMenuActive == i + '' +k}" @click="subMenuFun(subItem, i, k)">{{subItem.name}}</div>  
42 - </div>  
43 - <!-- 二级菜单加号, 当长度 小于 5 才显示二级菜单的加号 -->  
44 - <div class="menu_bottom menu_addicon" v-if="!item.sub_button || item.sub_button.length < 5" @click="addSubMenu(i,item)"><i class="el-icon-plus"></i></div>  
45 - </div>  
46 - </div>  
47 - <!-- 一级菜单加号 -->  
48 - <div class="menu_bottom menu_addicon" v-if="menuKeyLength < 3" @click="addMenu"><i class="el-icon-plus"></i></div>  
49 - </div>  
50 - <div class="save_div">  
51 - <!--<el-button class="save_btn" type="warning" size="small" @click="saveFun">保存菜单</el-button>-->  
52 - <el-button class="save_btn" type="success" size="small" @click="saveAndReleaseFun">保存并发布菜单</el-button>  
53 - </div>  
54 - </div>  
55 - <!--右边配置-->  
56 - <div v-if="showRightFlag" class="right">  
57 - <div class="configure_page">  
58 - <div class="delete_btn">  
59 - <el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteMenu(tempObj)">删除当前菜单</el-button>  
60 - </div>  
61 - <div>  
62 - <span>菜单名称:</span>  
63 - <el-input class="input_width" v-model="tempObj.name" placeholder="请输入菜单名称" :maxlength=nameMaxlength clearable></el-input>  
64 - </div>  
65 - <div v-if="showConfigurContent">  
66 - <div class="menu_content">  
67 - <span>菜单内容:</span>  
68 - <el-select v-model="tempObj.type" clearable placeholder="请选择" class="menu_option">  
69 - <el-option v-for="item in menuOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>  
70 - </el-select>  
71 - </div>  
72 - <div class="configur_content" v-if="tempObj.type == 'view'">  
73 - <span>跳转链接:</span>  
74 - <el-input class="input_width" v-model="tempObj.url" placeholder="请输入链接" clearable></el-input>  
75 - </div>  
76 - <div class="configur_content" v-if="tempObj.type == 'miniprogram'">  
77 - <div class="applet">  
78 - <span>小程序的appid:</span>  
79 - <el-input class="input_width" v-model="tempObj.appid" placeholder="请输入小程序的appid" clearable></el-input>  
80 - </div>  
81 - <div class="applet">  
82 - <span>小程序的页面路径:</span>  
83 - <el-input class="input_width" v-model="tempObj.pagepath" placeholder="请输入小程序的页面路径,如:pages/index" clearable></el-input>  
84 - </div>  
85 - <div class="applet">  
86 - <span>备用网页:</span>  
87 - <el-input class="input_width" v-model="tempObj.url" placeholder="不支持小程序的老版本客户端将打开本网页" clearable></el-input>  
88 - </div>  
89 - <p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p>  
90 - </div>  
91 - <div class="configur_content" v-if="tempObj.type == 'article_view_limited'">  
92 - <el-row>  
93 - <div class="select-item" v-if="tempObj && tempObj.content && tempObj.content.articles">  
94 - <WxNews :objData="tempObj.content.articles"></WxNews>  
95 - <el-row class="ope-row">  
96 - <el-button type="danger" icon="el-icon-delete" circle @click="deleteTempObj"></el-button>  
97 - </el-row>  
98 - </div>  
99 - <div v-if="!tempObj.content || !tempObj.content.articles">  
100 - <el-row>  
101 - <el-col :span="24" style="text-align: center">  
102 - <el-button type="success" @click="openMaterial">素材库选择<i class="el-icon-circle-check el-icon--right"></i></el-button>  
103 - </el-col>  
104 - </el-row>  
105 - </div>  
106 - <el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%">  
107 - <WxMaterialSelect :objData="{repType:'news'}" @selectMaterial="selectMaterial"></WxMaterialSelect>  
108 - </el-dialog>  
109 - </el-row>  
110 - </div>  
111 - <div class="configur_content" v-if="tempObj.type == 'click' || tempObj.type == 'scancode_waitmsg'">  
112 - <WxReplySelect :objData="tempObj" v-if="hackResetWxReplySelect"></WxReplySelect>  
113 - </div>  
114 - </div>  
115 - </div>  
116 - </div>  
117 - <!--一进页面就显示的默认页面,当点击左边按钮的时候,就不显示了-->  
118 - <div v-if="!showRightFlag" class="right">  
119 - <p>请选择菜单配置</p>  
120 - </div>  
121 - </div> 26 + <!-- 搜索工作栏 -->
  27 + <el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  28 + <el-form-item label="公众号" prop="accountId">
  29 + <el-select v-model="accountId" placeholder="请选择公众号">
  30 + <el-option v-for="item in accounts" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
  31 + </el-select>
  32 + </el-form-item>
  33 + <el-form-item>
  34 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  35 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  36 + </el-form-item>
  37 + </el-form>
  38 +
  39 + <div class="public-account-management clearfix" v-loading="loading">
  40 + <!--左边配置菜单-->
  41 + <div class="left">
  42 + <div class="weixin-hd">
  43 + <div class="weixin-title">{{menuName}}</div>
  44 + </div>
  45 + <div class="weixin-menu menu_main clearfix">
  46 + <div class="menu_bottom" v-for="(item, i) of menuList" :key="i" >
  47 + <!-- 一级菜单 -->
  48 + <div @click="menuClick(i, item)" class="menu_item el-icon-s-fold" :class="{'active': isActive === i}">{{item.name}}</div>
  49 + <!-- 以下为二级菜单-->
  50 + <div class="submenu" v-if="isSubMenuFlag === i">
  51 + <div class="subtitle menu_bottom" v-if="item.children" v-for="(subItem, k) in item.children" :key="k">
  52 + <div class="menu_subItem" :class="{'active': isSubMenuActive === i + '' + k}" @click="subMenuClick(subItem, i, k)">
  53 + {{subItem.name}}
  54 + </div>
  55 + </div>
  56 + <!-- 二级菜单加号, 当长度 小于 5 才显示二级菜单的加号 -->
  57 + <div class="menu_bottom menu_addicon" v-if="!item.children || item.children.length < 5" @click="addSubMenu(i,item)">
  58 + <i class="el-icon-plus" />
  59 + </div>
  60 + </div>
  61 + </div>
  62 + <!-- 一级菜单加号 -->
  63 + <div class="menu_bottom menu_addicon" v-if="menuKeyLength < 3" @click="addMenu"><i class="el-icon-plus"></i></div>
  64 + </div>
  65 + <div class="save_div">
  66 + <!--<el-button class="save_btn" type="warning" size="small" @click="saveFun">保存菜单</el-button>-->
  67 + <el-button class="save_btn" type="success" size="small" @click="saveAndReleaseFun">保存并发布菜单</el-button>
  68 + </div>
  69 + </div>
  70 + <!--右边配置-->
  71 + <div v-if="showRightFlag" class="right">
  72 + <div class="configure_page">
  73 + <div class="delete_btn">
  74 + <el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteMenu(tempObj)">删除当前菜单</el-button>
  75 + </div>
  76 + <div>
  77 + <span>菜单名称:</span>
  78 + <el-input class="input_width" v-model="tempObj.name" placeholder="请输入菜单名称" :maxlength=nameMaxlength clearable></el-input>
  79 + </div>
  80 + <div v-if="showConfigurContent">
  81 + <div class="menu_content">
  82 + <span>菜单内容:</span>
  83 + <el-select v-model="tempObj.type" clearable placeholder="请选择" class="menu_option">
  84 + <el-option v-for="item in menuOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
  85 + </el-select>
  86 + </div>
  87 + <div class="configur_content" v-if="tempObj.type === 'view'">
  88 + <span>跳转链接:</span>
  89 + <el-input class="input_width" v-model="tempObj.url" placeholder="请输入链接" clearable></el-input>
  90 + </div>
  91 + <div class="configur_content" v-if="tempObj.type === 'miniprogram'">
  92 + <div class="applet">
  93 + <span>小程序的appid:</span>
  94 + <el-input class="input_width" v-model="tempObj.appid" placeholder="请输入小程序的appid" clearable></el-input>
  95 + </div>
  96 + <div class="applet">
  97 + <span>小程序的页面路径:</span>
  98 + <el-input class="input_width" v-model="tempObj.pagepath" placeholder="请输入小程序的页面路径,如:pages/index" clearable></el-input>
  99 + </div>
  100 + <div class="applet">
  101 + <span>备用网页:</span>
  102 + <el-input class="input_width" v-model="tempObj.url" placeholder="不支持小程序的老版本客户端将打开本网页" clearable></el-input>
  103 + </div>
  104 + <p class="blue">tips:需要和公众号进行关联才可以把小程序绑定带微信菜单上哟!</p>
  105 + </div>
  106 + <div class="configur_content" v-if="tempObj.type == 'article_view_limited'">
  107 + <el-row>
  108 + <div class="select-item" v-if="tempObj && tempObj.content && tempObj.content.articles">
  109 + <WxNews :objData="tempObj.content.articles"></WxNews>
  110 + <el-row class="ope-row">
  111 + <el-button type="danger" icon="el-icon-delete" circle @click="deleteTempObj"></el-button>
  112 + </el-row>
  113 + </div>
  114 + <div v-if="!tempObj.content || !tempObj.content.articles">
  115 + <el-row>
  116 + <el-col :span="24" style="text-align: center">
  117 + <el-button type="success" @click="openMaterial">素材库选择<i class="el-icon-circle-check el-icon--right"></i></el-button>
  118 + </el-col>
  119 + </el-row>
  120 + </div>
  121 + <el-dialog title="选择图文" :visible.sync="dialogNewsVisible" width="90%">
  122 + <WxMaterialSelect :objData="{repType:'news'}" @selectMaterial="selectMaterial"></WxMaterialSelect>
  123 + </el-dialog>
  124 + </el-row>
  125 + </div>
  126 + <div class="configur_content" v-if="tempObj.type == 'click' || tempObj.type == 'scancode_waitmsg'">
  127 + <WxReplySelect :objData="tempObj" v-if="hackResetWxReplySelect"></WxReplySelect>
  128 + </div>
  129 + </div>
  130 + </div>
  131 + </div>
  132 + <!--一进页面就显示的默认页面,当点击左边按钮的时候,就不显示了-->
  133 + <div v-if="!showRightFlag" class="right">
  134 + <p>请选择菜单配置</p>
  135 + </div>
122 </div> 136 </div>
  137 + </div>
123 </template> 138 </template>
124 139
125 <script> 140 <script>
126 - // import { save, saveAndRelease ,getList} from '@/api/wxmp/wxmenu'  
127 - import WxReplySelect from '@/views/mp/components/wx-news/main.vue'  
128 - import WxNews from '@/views/mp/components/wx-news/main.vue';  
129 - import WxMaterialSelect from '@/views/mp/components/wx-news/main.vue' 141 +// import { save, saveAndRelease ,getList} from '@/api/wxmp/wxmenu'
  142 +import WxReplySelect from '@/views/mp/components/wx-news/main.vue'
  143 +import WxNews from '@/views/mp/components/wx-news/main.vue';
  144 +import WxMaterialSelect from '@/views/mp/components/wx-news/main.vue'
  145 +import {getMenuList} from "@/api/mp/menu";
  146 +import {getSimpleAccounts} from "@/api/mp/account";
130 147
131 - export default {  
132 - name: 'mpMenu',  
133 - components: {  
134 - WxReplySelect,  
135 - WxNews,  
136 - WxMaterialSelect  
137 - },  
138 - data(){  
139 - return{  
140 - showRightFlag:false,//右边配置显示默认详情还是配置详情  
141 - menu:{  
142 - button:[  
143 - ]  
144 - },//横向菜单  
145 - isActive: -1,// 一级菜单点中样式  
146 - isSubMenuActive: -1,// 一级菜单点中样式  
147 - isSubMenuFlag: -1,// 二级菜单显示标志  
148 - tempObj:{},//右边临时变量,作为中间值牵引关系  
149 - tempSelfObj:{  
150 - //一些临时值放在这里进行判断,如果放在tempObj,由于引用关系,menu也会多了多余的参数  
151 - },  
152 - visible2: false,//素材内容 "选择素材"按钮弹框显示隐藏  
153 - tableData:[], //素材内容弹框数据,  
154 - menuName:'',  
155 - showConfigurContent:true,  
156 - nameMaxlength:0,//名称最大长度  
157 - menuOptions: [{  
158 - value: 'view',  
159 - label: '跳转网页'  
160 - }, {  
161 - value: 'miniprogram',  
162 - label: '跳转小程序'  
163 - }, {  
164 - value: 'click',  
165 - label: '点击回复'  
166 - }, {  
167 - value: 'article_view_limited',  
168 - label: '跳转图文消息'  
169 - }, {  
170 - value: 'scancode_push',  
171 - label: '扫码直接返回结果'  
172 - }, {  
173 - value: 'scancode_waitmsg',  
174 - label: '扫码回复'  
175 - }, {  
176 - value: 'pic_sysphoto',  
177 - label: '系统拍照发图'  
178 - }, {  
179 - value: 'pic_photo_or_album',  
180 - label: '拍照或者相册'  
181 - }, {  
182 - value: 'pic_weixin',  
183 - label: '微信相册'  
184 - }, {  
185 - value: 'location_select',  
186 - label: '选择地理位置'  
187 - }],  
188 - dialogNewsVisible:false,  
189 - saveLoading:false,  
190 - hackResetWxReplySelect:false  
191 - }  
192 - },  
193 - created() {  
194 - this.getMenuFun()  
195 - },  
196 - mounted() { 148 +export default {
  149 + name: 'mpMenu',
  150 + components: {
  151 + WxReplySelect,
  152 + WxNews,
  153 + WxMaterialSelect
  154 + },
  155 + data(){
  156 + return {
  157 + // 遮罩层
  158 + loading: true,
  159 + // 显示搜索条件
  160 + showSearch: true,
  161 + // 查询参数
  162 + accountId: undefined,
  163 + menuList: {
  164 + children: [],
  165 + },
197 166
198 - },  
199 - filters:{  
200 - },  
201 - computed: {  
202 - },  
203 - methods:{  
204 - deleteTempObj(){  
205 - this.$delete(this.tempObj,'repName')  
206 - this.$delete(this.tempObj,'repUrl')  
207 - this.$delete(this.tempObj,'content')  
208 - },  
209 - openMaterial(){  
210 - this.dialogNewsVisible = true  
211 - },  
212 - selectMaterial(item){  
213 - if(item.content.articles.length>1){  
214 - this.$alert('您选择的是多图文,将默认跳转第一篇', '提示', {  
215 - confirmButtonText: '确定'  
216 - })  
217 - }  
218 - this.dialogNewsVisible = false  
219 - this.tempObj.article_id = item.articleId  
220 - this.tempObj.mediaName = item.name  
221 - this.tempObj.url = item.url  
222 - item.mediaType = this.tempObj.mediaType  
223 - item.content.articles = item.content.articles.slice(0,1)  
224 - this.tempObj.content = item.content  
225 - },  
226 - getMenuFun(){  
227 - getList().then((res)=>{  
228 - this.menu.button = JSON.parse(res.msg).button;  
229 - });  
230 - },  
231 - handleClick(tab, event){  
232 - this.tempObj.mediaType = tab.name  
233 - },  
234 - saveAndReleaseFun(){  
235 - this.$confirm('确定要保证并发布该菜单吗?', '提示', {  
236 - confirmButtonText: '确定',  
237 - cancelButtonText: '取消',  
238 - type: 'warning'  
239 - }).then(() => {  
240 - this.saveLoading = true  
241 - saveAndRelease({  
242 - strWxMenu:this.menu  
243 - }).then(response => {  
244 - this.saveLoading = false  
245 - if(response.code == 200){  
246 - this.$message({  
247 - showClose: true,  
248 - message: '发布成功',  
249 - type: 'success'  
250 - })  
251 - }else{  
252 - this.$message.error(response.data.msg)  
253 - }  
254 - }).catch(() => {  
255 - this.saveLoading = false 167 + showRightFlag:false, // 右边配置显示默认详情还是配置详情
  168 + menu:{ // 横向菜单
  169 + button:[
  170 + ]
  171 + },
  172 +
  173 + isActive: -1,// 一级菜单点中样式
  174 + isSubMenuActive: -1, // 一级菜单点中样式
  175 + isSubMenuFlag: -1, // 二级菜单显示标志
  176 + tempObj:{}, // 右边临时变量,作为中间值牵引关系
  177 + tempSelfObj: {
  178 + // 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数
  179 + },
  180 + visible2: false, //素材内容 "选择素材"按钮弹框显示隐藏
  181 + tableData:[], //素材内容弹框数据,
  182 + menuName:'',
  183 + showConfigurContent:true,
  184 + nameMaxlength:0,//名称最大长度
  185 + menuOptions: [{
  186 + value: 'view',
  187 + label: '跳转网页'
  188 + }, {
  189 + value: 'miniprogram',
  190 + label: '跳转小程序'
  191 + }, {
  192 + value: 'click',
  193 + label: '点击回复'
  194 + }, {
  195 + value: 'article_view_limited',
  196 + label: '跳转图文消息'
  197 + }, {
  198 + value: 'scancode_push',
  199 + label: '扫码直接返回结果'
  200 + }, {
  201 + value: 'scancode_waitmsg',
  202 + label: '扫码回复'
  203 + }, {
  204 + value: 'pic_sysphoto',
  205 + label: '系统拍照发图'
  206 + }, {
  207 + value: 'pic_photo_or_album',
  208 + label: '拍照或者相册'
  209 + }, {
  210 + value: 'pic_weixin',
  211 + label: '微信相册'
  212 + }, {
  213 + value: 'location_select',
  214 + label: '选择地理位置'
  215 + }],
  216 + dialogNewsVisible: false,
  217 + hackResetWxReplySelect: false,
  218 +
  219 + // 公众号账号列表
  220 + accounts: [],
  221 + }
  222 + },
  223 + created() {
  224 + getSimpleAccounts().then(response => {
  225 + this.accounts = response.data;
  226 + // 默认选中第一个
  227 + if (this.accounts.length > 0) {
  228 + this.setAccountId(this.accounts[0].id);
  229 + }
  230 + // 加载数据
  231 + this.getList();
  232 + })
  233 + },
  234 + computed: {
  235 + menuKeyLength:function() {
  236 + // menuObj 的长度,当长度小于 3 才显示一级菜单的加号
  237 + return this.menu.button.length;
  238 + }
  239 + },
  240 + methods: {
  241 + // ======================== 列表查询 ========================
  242 + /** 设置账号编号 */
  243 + setAccountId(accountId) {
  244 + this.accountId = accountId;
  245 + // this.uploadData.accountId = accountId;
  246 + },
  247 + getList() {
  248 + this.loading = false;
  249 + getMenuList(this.accountId).then(response => {
  250 + this.menuList = this.handleTree(response.data, "id");
  251 + console.log(this.menuList)
  252 + }).finally(() => {
  253 + this.loading = false;
  254 + })
  255 + },
  256 + /** 搜索按钮操作 */
  257 + handleQuery() {
  258 + // 默认选中第一个
  259 + if (this.accountId) {
  260 + this.setAccountId(this.accountId)
  261 + }
  262 + this.getList()
  263 + },
  264 + /** 重置按钮操作 */
  265 + resetQuery() {
  266 + this.resetForm('queryForm')
  267 + // 默认选中第一个
  268 + if (this.accounts.length > 0) {
  269 + this.setAccountId(this.accounts[0].id)
  270 + }
  271 + this.handleQuery()
  272 + },
  273 +
  274 + // TODO 芋艿:未归类
  275 +
  276 + deleteTempObj(){
  277 + this.$delete(this.tempObj,'repName')
  278 + this.$delete(this.tempObj,'repUrl')
  279 + this.$delete(this.tempObj,'content')
  280 + },
  281 + openMaterial(){
  282 + this.dialogNewsVisible = true
  283 + },
  284 + selectMaterial(item){
  285 + if(item.content.articles.length>1){
  286 + this.$alert('您选择的是多图文,将默认跳转第一篇', '提示', {
  287 + confirmButtonText: '确定'
  288 + })
  289 + }
  290 + this.dialogNewsVisible = false
  291 + this.tempObj.article_id = item.articleId
  292 + this.tempObj.mediaName = item.name
  293 + this.tempObj.url = item.url
  294 + item.mediaType = this.tempObj.mediaType
  295 + item.content.articles = item.content.articles.slice(0,1)
  296 + this.tempObj.content = item.content
  297 + },
  298 + handleClick(tab, event){
  299 + this.tempObj.mediaType = tab.name
  300 + },
  301 + saveAndReleaseFun(){
  302 + this.$confirm('确定要保证并发布该菜单吗?', '提示', {
  303 + confirmButtonText: '确定',
  304 + cancelButtonText: '取消',
  305 + type: 'warning'
  306 + }).then(() => {
  307 + this.loading = true
  308 + saveAndRelease({
  309 + strWxMenu:this.menu
  310 + }).then(response => {
  311 + this.loading = false
  312 + if(response.code == 200){
  313 + this.$message({
  314 + showClose: true,
  315 + message: '发布成功',
  316 + type: 'success'
256 }) 317 })
257 - }).catch(() => {  
258 - })  
259 - },  
260 - // 一级菜单点击事件  
261 - menuFun(i, item){  
262 - this.hackResetWxReplySelect = false//销毁组件  
263 - this.$nextTick(() => {  
264 - this.hackResetWxReplySelect = true//重建组件  
265 - })  
266 - this.showRightFlag = true;//右边菜单  
267 - this.tempObj = item;//这个如果放在顶部,flag会没有。因为重新赋值了。  
268 - this.tempSelfObj.grand = "1";//表示一级菜单  
269 - this.tempSelfObj.index = i;//表示一级菜单索引  
270 - this.isActive = i; //一级菜单选中样式  
271 - this.isSubMenuFlag = i; //二级菜单显示标志  
272 - this.isSubMenuActive = -1; //二级菜单去除选中样式  
273 - this.nameMaxlength = 4  
274 - if(item.sub_button&&item.sub_button.length>0){  
275 - this.showConfigurContent = false  
276 }else{ 318 }else{
277 - this.showConfigurContent = true  
278 - }  
279 - },  
280 - // 二级菜单点击事件  
281 - subMenuFun(subItem, index, k){  
282 - this.hackResetWxReplySelect = false//销毁组件  
283 - this.$nextTick(() => {  
284 - this.hackResetWxReplySelect = true//重建组件  
285 - })  
286 - this.showRightFlag = true;//右边菜单  
287 - this.tempObj = subItem;//将点击的数据放到临时变量,对象有引用作用  
288 - this.tempSelfObj.grand = "2";//表示二级菜单  
289 - this.tempSelfObj.index = index;//表示一级菜单索引  
290 - this.tempSelfObj.secondIndex = k;//表示二级菜单索引  
291 - this.isSubMenuActive = index + "" + k; //二级菜单选中样式  
292 - this.isActive = -1;//一级菜单去除样式  
293 - this.showConfigurContent = true;  
294 - this.nameMaxlength = 7  
295 - },  
296 - // 添加横向一级菜单  
297 - addMenu(){  
298 - let menuKeyLength = this.menuKeyLength  
299 - let addButton = {  
300 - name: "菜单名称",  
301 - sub_button: []  
302 - }  
303 - this.$set(this.menu.button,menuKeyLength,addButton)  
304 - this.menuFun(this.menuKeyLength-1, addButton)  
305 - },  
306 - // 添加横向二级菜单  
307 - addSubMenu(i,item){  
308 - if(!item.sub_button||item.sub_button.length<=0){  
309 - this.$set( item, 'sub_button',[])  
310 - this.$delete( item, 'type')  
311 - this.$delete( item, 'pagepath')  
312 - this.$delete( item, 'url')  
313 - this.$delete( item, 'key')  
314 - this.$delete( item, 'article_id')  
315 - this.$delete( item, 'textContent')  
316 - this.showConfigurContent = false  
317 - }  
318 - let subMenuKeyLength = item.sub_button.length;//获取二级菜单key长度  
319 - let addButton = {  
320 - name: "子菜单名称" 319 + this.$message.error(response.data.msg)
321 } 320 }
322 - this.$set(item.sub_button,subMenuKeyLength,addButton);  
323 - this.subMenuFun(item.sub_button[subMenuKeyLength], i, subMenuKeyLength)  
324 - },  
325 - //删除当前菜单  
326 - deleteMenu(obj){  
327 - let _this = this;  
328 - this.$confirm('确定要删除吗?', '提示', {  
329 - confirmButtonText: '确定',  
330 - cancelButtonText: '取消',  
331 - type: 'warning'  
332 - }).then(() => {  
333 - _this.deleteData();// 删除菜单数据  
334 - _this.tempObj = {};  
335 - _this.showRightFlag = false;  
336 - this.isActive = -1;  
337 - this.isSubMenuActive = -1;  
338 - }).catch(() => {  
339 - });  
340 - },  
341 - // 删除菜单数据  
342 - deleteData(){  
343 - // 一级菜单的删除方法  
344 - if(this.tempSelfObj.grand == "1"){  
345 - this.menu.button.splice(this.tempSelfObj.index,1);  
346 - }  
347 - // 二级菜单的删除方法  
348 - if(this.tempSelfObj.grand == "2"){  
349 - this.menu.button[this.tempSelfObj.index].sub_button.splice(this.tempSelfObj.secondIndex, 1);  
350 - }  
351 - this.$message({  
352 - type: 'success',  
353 - message: '删除成功!'  
354 - });  
355 - }  
356 - },  
357 - computed:{  
358 - // menuObj的长度,当长度 小于 3 才显示一级菜单的加号  
359 - menuKeyLength:function(){  
360 - return this.menu.button.length;  
361 - } 321 + }).catch(() => {
  322 + this.loading = false
  323 + })
  324 + }).catch(() => {
  325 + })
  326 + },
  327 + // 一级菜单点击事件
  328 + menuClick(i, item){
  329 + this.hackResetWxReplySelect = false//销毁组件
  330 + this.$nextTick(() => {
  331 + this.hackResetWxReplySelect = true//重建组件
  332 + })
  333 + this.showRightFlag = true;//右边菜单
  334 + this.tempObj = item;//这个如果放在顶部,flag会没有。因为重新赋值了。
  335 + this.tempSelfObj.grand = "1";//表示一级菜单
  336 + this.tempSelfObj.index = i;//表示一级菜单索引
  337 + this.isActive = i; //一级菜单选中样式
  338 + this.isSubMenuFlag = i; //二级菜单显示标志
  339 + this.isSubMenuActive = -1; //二级菜单去除选中样式
  340 + this.nameMaxlength = 4
  341 + if(item.sub_button && item.sub_button.length > 0){
  342 + this.showConfigurContent = false
  343 + }else{
  344 + this.showConfigurContent = true
  345 + }
  346 + },
  347 + // 二级菜单点击事件
  348 + subMenuClick(subItem, index, k){
  349 + this.hackResetWxReplySelect = false//销毁组件
  350 + this.$nextTick(() => {
  351 + this.hackResetWxReplySelect = true//重建组件
  352 + })
  353 + this.showRightFlag = true;//右边菜单
  354 + this.tempObj = subItem;//将点击的数据放到临时变量,对象有引用作用
  355 + this.tempSelfObj.grand = "2";//表示二级菜单
  356 + this.tempSelfObj.index = index;//表示一级菜单索引
  357 + this.tempSelfObj.secondIndex = k;//表示二级菜单索引
  358 + this.isSubMenuActive = index + "" + k; //二级菜单选中样式
  359 + this.isActive = -1;//一级菜单去除样式
  360 + this.showConfigurContent = true;
  361 + this.nameMaxlength = 7
  362 + },
  363 + // 添加横向一级菜单
  364 + addMenu(){
  365 + let menuKeyLength = this.menuKeyLength
  366 + let addButton = {
  367 + name: "菜单名称",
  368 + sub_button: []
  369 + }
  370 + this.$set(this.menu.button,menuKeyLength,addButton)
  371 + this.menuClick(this.menuKeyLength-1, addButton)
  372 + },
  373 + // 添加横向二级菜单
  374 + addSubMenu(i,item){
  375 + if(!item.sub_button||item.sub_button.length<=0){
  376 + this.$set( item, 'sub_button',[])
  377 + this.$delete( item, 'type')
  378 + this.$delete( item, 'pagepath')
  379 + this.$delete( item, 'url')
  380 + this.$delete( item, 'key')
  381 + this.$delete( item, 'article_id')
  382 + this.$delete( item, 'textContent')
  383 + this.showConfigurContent = false
362 } 384 }
  385 + let subMenuKeyLength = item.sub_button.length;//获取二级菜单key长度
  386 + let addButton = {
  387 + name: "子菜单名称"
  388 + }
  389 + this.$set(item.sub_button,subMenuKeyLength,addButton);
  390 + this.subMenuClick(item.sub_button[subMenuKeyLength], i, subMenuKeyLength)
  391 + },
  392 + //删除当前菜单
  393 + deleteMenu(obj){
  394 + let _this = this;
  395 + this.$confirm('确定要删除吗?', '提示', {
  396 + confirmButtonText: '确定',
  397 + cancelButtonText: '取消',
  398 + type: 'warning'
  399 + }).then(() => {
  400 + _this.deleteData();// 删除菜单数据
  401 + _this.tempObj = {};
  402 + _this.showRightFlag = false;
  403 + this.isActive = -1;
  404 + this.isSubMenuActive = -1;
  405 + }).catch(() => {
  406 + });
  407 + },
  408 + // 删除菜单数据
  409 + deleteData(){
  410 + // 一级菜单的删除方法
  411 + if(this.tempSelfObj.grand == "1"){
  412 + this.menu.button.splice(this.tempSelfObj.index,1);
  413 + }
  414 + // 二级菜单的删除方法
  415 + if(this.tempSelfObj.grand == "2"){
  416 + this.menu.button[this.tempSelfObj.index].sub_button.splice(this.tempSelfObj.secondIndex, 1);
  417 + }
  418 + this.$message({
  419 + type: 'success',
  420 + message: '删除成功!'
  421 + });
363 } 422 }
  423 + },
  424 +}
364 </script> 425 </script>
365 <!--本组件样式--> 426 <!--本组件样式-->
366 <style lang="less" scoped="scoped"> 427 <style lang="less" scoped="scoped">
@@ -368,153 +429,153 @@ SOFTWARE. @@ -368,153 +429,153 @@ SOFTWARE.
368 .clearfix{*zoom:1;} 429 .clearfix{*zoom:1;}
369 .clearfix::after{content: "";display: table; clear: both;} 430 .clearfix::after{content: "";display: table; clear: both;}
370 div{ 431 div{
371 - text-align: left; 432 + text-align: left;
372 } 433 }
373 .weixin-hd{ 434 .weixin-hd{
374 - color: #fff;  
375 - text-align: center;  
376 - position: relative;  
377 - bottom: 426px;  
378 - left:0px;  
379 - width: 300px;  
380 - height:64px;  
381 - background: transparent url(assets/menu_head.png) no-repeat 0 0;  
382 - background-position: 0 0;  
383 - background-size: 100% 435 + color: #fff;
  436 + text-align: center;
  437 + position: relative;
  438 + bottom: 426px;
  439 + left:0px;
  440 + width: 300px;
  441 + height:64px;
  442 + background: transparent url(assets/menu_head.png) no-repeat 0 0;
  443 + background-position: 0 0;
  444 + background-size: 100%
384 } 445 }
385 .weixin-title{ 446 .weixin-title{
386 - color:#fff;  
387 - font-size:14px;  
388 - width:100%;  
389 - text-align: center;  
390 - position:absolute;  
391 - top: 33px;  
392 - left: 0px; 447 + color:#fff;
  448 + font-size:14px;
  449 + width:100%;
  450 + text-align: center;
  451 + position:absolute;
  452 + top: 33px;
  453 + left: 0px;
393 } 454 }
394 .weixin-menu{ 455 .weixin-menu{
395 - background: transparent url(assets/menu_foot.png) no-repeat 0 0;  
396 - padding-left: 43px;  
397 - font-size: 12px 456 + background: transparent url(assets/menu_foot.png) no-repeat 0 0;
  457 + padding-left: 43px;
  458 + font-size: 12px
398 } 459 }
399 .menu_option{ 460 .menu_option{
400 - width: 40%!important; 461 + width: 40%!important;
401 } 462 }
402 .public-account-management{ 463 .public-account-management{
403 - min-width: 1200px;  
404 - width: 1200px;  
405 - margin: 0 auto;  
406 - .left{  
407 - float: left;  
408 - display: inline-block;  
409 - width: 350px;  
410 - height: 715px;  
411 - background: url("assets/iphone_backImg.png") no-repeat;  
412 - background-size: 100% auto;  
413 - padding: 518px 25px 88px;  
414 - position: relative;  
415 - box-sizing: border-box;  
416 - /*第一级菜单*/  
417 - .menu_main{  
418 - .menu_bottom{  
419 - position: relative;  
420 - float: left;  
421 - display: inline-block;  
422 - box-sizing: border-box;  
423 - width: 85.5px;  
424 - text-align: center;  
425 - border: 1px solid #ebedee;  
426 - background-color: #fff;  
427 - cursor: pointer;  
428 - &.menu_addicon{  
429 - height: 46px;  
430 - line-height: 46px;  
431 - }  
432 - .menu_item{  
433 - height: 44px;  
434 - line-height: 44px;  
435 - text-align: center;  
436 - box-sizing: border-box;  
437 - width: 100%;  
438 - &.active{  
439 - border: 1px solid #2bb673;  
440 - }  
441 - }  
442 - .menu_subItem{  
443 - height: 44px;  
444 - line-height: 44px;  
445 - text-align: center;  
446 - box-sizing: border-box;  
447 - &.active{  
448 - border: 1px solid #2bb673;  
449 - }  
450 - }  
451 - }  
452 - i{  
453 - color:#2bb673;  
454 - }  
455 - /*第二级菜单*/  
456 - .submenu{  
457 - position: absolute;  
458 - width: 85.5px;  
459 - bottom: 45px;  
460 - .subtitle{  
461 - background-color: #fff;  
462 - box-sizing: border-box;  
463 - }  
464 - }  
465 - }  
466 - .save_div{  
467 - margin-top: 15px;  
468 - text-align: center;  
469 - .save_btn{  
470 - bottom: 20px;  
471 - left: 100px;  
472 - }  
473 - }  
474 - }  
475 - /*右边菜单内容*/  
476 - .right{  
477 - float: left;  
478 - width: 63%;  
479 - background-color: #e8e7e7;  
480 - padding: 20px;  
481 - margin-left: 20px;  
482 - -webkit-box-sizing: border-box;  
483 - box-sizing: border-box;  
484 - .configure_page{  
485 - .delete_btn{  
486 - text-align: right;  
487 - margin-bottom: 15px;  
488 - }  
489 - .menu_content{  
490 - margin-top: 20px;  
491 - }  
492 - .configur_content{  
493 - margin-top: 20px;  
494 - background-color: #fff;  
495 - padding: 20px 10px;  
496 - border-radius: 5px  
497 - }  
498 - .blue{  
499 - color:#29b6f6;  
500 - margin-top: 10px;  
501 - }  
502 - .applet{  
503 - margin-bottom: 20px;  
504 - span{  
505 - width: 20%;  
506 - }  
507 - }  
508 - .material{  
509 - .input_width{  
510 - width: 30%;  
511 - }  
512 - .el-textarea{  
513 - width: 80%  
514 - }  
515 - }  
516 - }  
517 - } 464 + min-width: 1200px;
  465 + width: 1200px;
  466 + margin: 0 auto;
  467 + .left{
  468 + float: left;
  469 + display: inline-block;
  470 + width: 350px;
  471 + height: 715px;
  472 + background: url("assets/iphone_backImg.png") no-repeat;
  473 + background-size: 100% auto;
  474 + padding: 518px 25px 88px;
  475 + position: relative;
  476 + box-sizing: border-box;
  477 + /*第一级菜单*/
  478 + .menu_main{
  479 + .menu_bottom{
  480 + position: relative;
  481 + float: left;
  482 + display: inline-block;
  483 + box-sizing: border-box;
  484 + width: 85.5px;
  485 + text-align: center;
  486 + border: 1px solid #ebedee;
  487 + background-color: #fff;
  488 + cursor: pointer;
  489 + &.menu_addicon{
  490 + height: 46px;
  491 + line-height: 46px;
  492 + }
  493 + .menu_item{
  494 + height: 44px;
  495 + line-height: 44px;
  496 + text-align: center;
  497 + box-sizing: border-box;
  498 + width: 100%;
  499 + &.active{
  500 + border: 1px solid #2bb673;
  501 + }
  502 + }
  503 + .menu_subItem{
  504 + height: 44px;
  505 + line-height: 44px;
  506 + text-align: center;
  507 + box-sizing: border-box;
  508 + &.active{
  509 + border: 1px solid #2bb673;
  510 + }
  511 + }
  512 + }
  513 + i{
  514 + color:#2bb673;
  515 + }
  516 + /*第二级菜单*/
  517 + .submenu{
  518 + position: absolute;
  519 + width: 85.5px;
  520 + bottom: 45px;
  521 + .subtitle{
  522 + background-color: #fff;
  523 + box-sizing: border-box;
  524 + }
  525 + }
  526 + }
  527 + .save_div{
  528 + margin-top: 15px;
  529 + text-align: center;
  530 + .save_btn{
  531 + bottom: 20px;
  532 + left: 100px;
  533 + }
  534 + }
  535 + }
  536 + /*右边菜单内容*/
  537 + .right{
  538 + float: left;
  539 + width: 63%;
  540 + background-color: #e8e7e7;
  541 + padding: 20px;
  542 + margin-left: 20px;
  543 + -webkit-box-sizing: border-box;
  544 + box-sizing: border-box;
  545 + .configure_page{
  546 + .delete_btn{
  547 + text-align: right;
  548 + margin-bottom: 15px;
  549 + }
  550 + .menu_content{
  551 + margin-top: 20px;
  552 + }
  553 + .configur_content{
  554 + margin-top: 20px;
  555 + background-color: #fff;
  556 + padding: 20px 10px;
  557 + border-radius: 5px
  558 + }
  559 + .blue{
  560 + color:#29b6f6;
  561 + margin-top: 10px;
  562 + }
  563 + .applet{
  564 + margin-bottom: 20px;
  565 + span{
  566 + width: 20%;
  567 + }
  568 + }
  569 + .material{
  570 + .input_width{
  571 + width: 30%;
  572 + }
  573 + .el-textarea{
  574 + width: 80%
  575 + }
  576 + }
  577 + }
  578 + }
518 } 579 }
519 </style> 580 </style>
520 <!--修改UI框架样式--> 581 <!--修改UI框架样式-->
yudao-ui-admin/yarn.lock
@@ -3340,6 +3340,13 @@ @@ -3340,6 +3340,13 @@
3340 "resolved" "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz" 3340 "resolved" "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz"
3341 "version" "0.5.0" 3341 "version" "0.5.0"
3342 3342
  3343 +"copy-anything@^2.0.1":
  3344 + "integrity" "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw=="
  3345 + "resolved" "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz"
  3346 + "version" "2.0.6"
  3347 + dependencies:
  3348 + "is-what" "^3.14.1"
  3349 +
3343 "copy-concurrently@^1.0.0": 3350 "copy-concurrently@^1.0.0":
3344 "integrity" "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==" 3351 "integrity" "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A=="
3345 "resolved" "https://registry.npmmirror.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz" 3352 "resolved" "https://registry.npmmirror.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz"
@@ -3696,6 +3703,13 @@ @@ -3696,6 +3703,13 @@
3696 dependencies: 3703 dependencies:
3697 "ms" "2.0.0" 3704 "ms" "2.0.0"
3698 3705
  3706 +"debug@^3.2.6":
  3707 + "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="
  3708 + "resolved" "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz"
  3709 + "version" "3.2.7"
  3710 + dependencies:
  3711 + "ms" "^2.1.1"
  3712 +
3699 "debug@^3.2.7": 3713 "debug@^3.2.7":
3700 "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" 3714 "integrity" "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="
3701 "resolved" "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz" 3715 "resolved" "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz"
@@ -4215,7 +4229,7 @@ @@ -4215,7 +4229,7 @@
4215 "resolved" "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz" 4229 "resolved" "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz"
4216 "version" "2.2.0" 4230 "version" "2.2.0"
4217 4231
4218 -"errno@^0.1.3", "errno@~0.1.7": 4232 +"errno@^0.1.1", "errno@^0.1.3", "errno@~0.1.7":
4219 "integrity" "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==" 4233 "integrity" "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A=="
4220 "resolved" "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz" 4234 "resolved" "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz"
4221 "version" "0.1.8" 4235 "version" "0.1.8"
@@ -5633,6 +5647,13 @@ @@ -5633,6 +5647,13 @@
5633 dependencies: 5647 dependencies:
5634 "safer-buffer" ">= 2.1.2 < 3" 5648 "safer-buffer" ">= 2.1.2 < 3"
5635 5649
  5650 +"iconv-lite@^0.6.3":
  5651 + "integrity" "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="
  5652 + "resolved" "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz"
  5653 + "version" "0.6.3"
  5654 + dependencies:
  5655 + "safer-buffer" ">= 2.1.2 < 3.0.0"
  5656 +
5636 "icss-utils@^4.0.0", "icss-utils@^4.1.1": 5657 "icss-utils@^4.0.0", "icss-utils@^4.1.1":
5637 "integrity" "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==" 5658 "integrity" "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA=="
5638 "resolved" "https://registry.npmmirror.com/icss-utils/-/icss-utils-4.1.1.tgz" 5659 "resolved" "https://registry.npmmirror.com/icss-utils/-/icss-utils-4.1.1.tgz"
@@ -5665,7 +5686,7 @@ @@ -5665,7 +5686,7 @@
5665 "resolved" "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz" 5686 "resolved" "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz"
5666 "version" "4.0.6" 5687 "version" "4.0.6"
5667 5688
5668 -"image-size@^0.5.1": 5689 +"image-size@^0.5.1", "image-size@~0.5.0":
5669 "integrity" "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==" 5690 "integrity" "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ=="
5670 "resolved" "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz" 5691 "resolved" "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz"
5671 "version" "0.5.5" 5692 "version" "0.5.5"
@@ -6150,6 +6171,11 @@ @@ -6150,6 +6171,11 @@
6150 dependencies: 6171 dependencies:
6151 "call-bind" "^1.0.2" 6172 "call-bind" "^1.0.2"
6152 6173
  6174 +"is-what@^3.14.1":
  6175 + "integrity" "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
  6176 + "resolved" "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz"
  6177 + "version" "3.14.1"
  6178 +
6153 "is-windows@^1.0.2": 6179 "is-windows@^1.0.2":
6154 "integrity" "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" 6180 "integrity" "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
6155 "resolved" "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz" 6181 "resolved" "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz"
@@ -6402,6 +6428,32 @@ @@ -6402,6 +6428,32 @@
6402 "picocolors" "^1.0.0" 6428 "picocolors" "^1.0.0"
6403 "shell-quote" "^1.7.3" 6429 "shell-quote" "^1.7.3"
6404 6430
  6431 +"less-loader@^7.3.0":
  6432 + "integrity" "sha512-Mi8915g7NMaLlgi77mgTTQvK022xKRQBIVDSyfl3ErTuBhmZBQab0mjeJjNNqGbdR+qrfTleKXqbGI4uEFavxg=="
  6433 + "resolved" "https://registry.npmmirror.com/less-loader/-/less-loader-7.3.0.tgz"
  6434 + "version" "7.3.0"
  6435 + dependencies:
  6436 + "klona" "^2.0.4"
  6437 + "loader-utils" "^2.0.0"
  6438 + "schema-utils" "^3.0.0"
  6439 +
  6440 +"less@^3.5.0 || ^4.0.0", "less@^4.1.3":
  6441 + "integrity" "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA=="
  6442 + "resolved" "https://registry.npmmirror.com/less/-/less-4.1.3.tgz"
  6443 + "version" "4.1.3"
  6444 + dependencies:
  6445 + "copy-anything" "^2.0.1"
  6446 + "parse-node-version" "^1.0.1"
  6447 + "tslib" "^2.3.0"
  6448 + optionalDependencies:
  6449 + "errno" "^0.1.1"
  6450 + "graceful-fs" "^4.1.2"
  6451 + "image-size" "~0.5.0"
  6452 + "make-dir" "^2.1.0"
  6453 + "mime" "^1.4.1"
  6454 + "needle" "^3.1.0"
  6455 + "source-map" "~0.6.0"
  6456 +
6405 "levn@^0.4.1": 6457 "levn@^0.4.1":
6406 "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==" 6458 "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="
6407 "resolved" "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz" 6459 "resolved" "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz"
@@ -6629,6 +6681,14 @@ @@ -6629,6 +6681,14 @@
6629 "pify" "^4.0.1" 6681 "pify" "^4.0.1"
6630 "semver" "^5.6.0" 6682 "semver" "^5.6.0"
6631 6683
  6684 +"make-dir@^2.1.0":
  6685 + "integrity" "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="
  6686 + "resolved" "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz"
  6687 + "version" "2.1.0"
  6688 + dependencies:
  6689 + "pify" "^4.0.1"
  6690 + "semver" "^5.6.0"
  6691 +
6632 "make-dir@^3.0.2", "make-dir@^3.1.0": 6692 "make-dir@^3.0.2", "make-dir@^3.1.0":
6633 "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==" 6693 "integrity" "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw=="
6634 "resolved" "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz" 6694 "resolved" "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz"
@@ -6819,6 +6879,11 @@ @@ -6819,6 +6879,11 @@
6819 dependencies: 6879 dependencies:
6820 "mime-db" "1.52.0" 6880 "mime-db" "1.52.0"
6821 6881
  6882 +"mime@^1.4.1":
  6883 + "integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
  6884 + "resolved" "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz"
  6885 + "version" "1.6.0"
  6886 +
6822 "mime@^2.4.4": 6887 "mime@^2.4.4":
6823 "integrity" "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" 6888 "integrity" "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
6824 "resolved" "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz" 6889 "resolved" "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz"
@@ -7111,6 +7176,15 @@ @@ -7111,6 +7176,15 @@
7111 "resolved" "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz" 7176 "resolved" "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz"
7112 "version" "1.4.0" 7177 "version" "1.4.0"
7113 7178
  7179 +"needle@^3.1.0":
  7180 + "integrity" "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ=="
  7181 + "resolved" "https://registry.npmmirror.com/needle/-/needle-3.2.0.tgz"
  7182 + "version" "3.2.0"
  7183 + dependencies:
  7184 + "debug" "^3.2.6"
  7185 + "iconv-lite" "^0.6.3"
  7186 + "sax" "^1.2.4"
  7187 +
7114 "negotiator@0.6.3": 7188 "negotiator@0.6.3":
7115 "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 7189 "integrity" "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
7116 "resolved" "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz" 7190 "resolved" "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz"
@@ -7607,6 +7681,11 @@ @@ -7607,6 +7681,11 @@
7607 "json-parse-even-better-errors" "^2.3.0" 7681 "json-parse-even-better-errors" "^2.3.0"
7608 "lines-and-columns" "^1.1.6" 7682 "lines-and-columns" "^1.1.6"
7609 7683
  7684 +"parse-node-version@^1.0.1":
  7685 + "integrity" "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
  7686 + "resolved" "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz"
  7687 + "version" "1.0.1"
  7688 +
7610 "parse5-htmlparser2-tree-adapter@^6.0.0": 7689 "parse5-htmlparser2-tree-adapter@^6.0.0":
7611 "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==" 7690 "integrity" "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA=="
7612 "resolved" "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" 7691 "resolved" "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz"
@@ -8850,7 +8929,7 @@ @@ -8850,7 +8929,7 @@
8850 dependencies: 8929 dependencies:
8851 "ret" "~0.1.10" 8930 "ret" "~0.1.10"
8852 8931
8853 -"safer-buffer@^2.0.2", "safer-buffer@^2.1.0", "safer-buffer@>= 2.1.2 < 3", "safer-buffer@~2.1.0": 8932 +"safer-buffer@^2.0.2", "safer-buffer@^2.1.0", "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", "safer-buffer@~2.1.0":
8854 "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 8933 "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
8855 "resolved" "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz" 8934 "resolved" "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
8856 "version" "2.1.2" 8935 "version" "2.1.2"
@@ -10017,7 +10096,7 @@ @@ -10017,7 +10096,7 @@
10017 "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz" 10096 "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz"
10018 "version" "2.4.1" 10097 "version" "2.4.1"
10019 10098
10020 -"tslib@2.3.0": 10099 +"tslib@^2.3.0", "tslib@2.3.0":
10021 "integrity" "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" 10100 "integrity" "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
10022 "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz" 10101 "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz"
10023 "version" "2.3.0" 10102 "version" "2.3.0"