element Ui树状图控件 spring boot Vue 实现角色授权功能

news2024/11/18 13:47:12

目录

前言:

二. element ui

 2.1官网提供的核心代码

三.表结构

​编辑 

四.后端

4.1功能分析

4.2实体类

4.3 查询全部权限显示的结果

4.2修改角色权限的后台方法 

 五.vue

5.0代码总览

5.1树形图

 5.2所需要的绑定数据

5.3所需方法


前言:

先上图看效果,页面不是很美观

 

 

 

二. element ui

 2.1官网提供的核心代码

<el-tree
  :data="data"
  show-checkbox
  default-expand-all
  node-key="id"
  ref="tree"
  highlight-current
  :props="defaultProps">
</el-tree>

<div class="buttons">
  <el-button @click="getCheckedNodes">通过 node 获取</el-button>
  <el-button @click="getCheckedKeys">通过 key 获取</el-button>
  <el-button @click="setCheckedNodes">通过 node 设置</el-button>
  <el-button @click="setCheckedKeys">通过 key 设置</el-button>
  <el-button @click="resetChecked">清空</el-button>
</div>

<script>
  export default {
    methods: {
      getCheckedNodes() {
        console.log(this.$refs.tree.getCheckedNodes());
      },
      getCheckedKeys() {
        console.log(this.$refs.tree.getCheckedKeys());
      },
      setCheckedNodes() {
        this.$refs.tree.setCheckedNodes([{
          id: 5,
          label: '二级 2-1'
        }, {
          id: 9,
          label: '三级 1-1-1'
        }]);
      },
      setCheckedKeys() {
        this.$refs.tree.setCheckedKeys([3]);
      },
      resetChecked() {
        this.$refs.tree.setCheckedKeys([]);
      }
    },

    data() {
      return {
        data: [{
          id: 1,
          label: '一级 1',
          children: [{
            id: 4,
            label: '二级 1-1',
            children: [{
              id: 9,
              label: '三级 1-1-1'
            }, {
              id: 10,
              label: '三级 1-1-2'
            }]
          }]
        }, {
          id: 2,
          label: '一级 2',
          children: [{
            id: 5,
            label: '二级 2-1'
          }, {
            id: 6,
            label: '二级 2-2'
          }]
        }, {
          id: 3,
          label: '一级 3',
          children: [{
            id: 7,
            label: '二级 3-1'
          }, {
            id: 8,
            label: '二级 3-2'
          }]
        }],
        defaultProps: {
          children: 'children',
          label: 'label'
        }
      };
    }
  };
</script>

 

 

 打开官网找到Tree树形控件,第一次应该很难看懂,我来详细跟大家讲解一下

1.

<el-tree
  :data="data"
  show-checkbox
  default-expand-all
  node-key="id"
  ref="tree"
  highlight-current
  :props="defaultProps">
</el-tree>

:data="data" 绑定的数据叫data,data可以替换成自己写的集合

node-key里的id对应的是数据库中表的id  

  ref="tree" 表示这个树形图。

:props="defaultProps"  用来设置树形图的属性 说白了 就是设置节点叫啥,子节点是什么集合。估计还是很懵,到时候直接带大家看项目

2.

 

 getCheckedNodes() {
        console.log(this.$refs.tree.getCheckedNodes());
      },

将勾选的节点以json集合的形式打印到控制台


      getCheckedKeys() {
        console.log(this.$refs.tree.getCheckedKeys());
      },

将勾选中的节点以字符串数组的形式打印到控制台

 

3.

 setCheckedKeys() {
        this.$refs.tree.setCheckedKeys([3]);
      },
    

设置选中的节点的key值,将其样式改为勾选。 里面填的是3,就是把id为3的节点设置为选中状态

setCheckedNodes() {
        this.$refs.tree.setCheckedNodes([{
          id: 5,
          label: '二级 2-1'
        }, {
          id: 9,
          label: '三级 1-1-1'
        }]);
      },

通过json的集合类型来设置选中的节点

4. data: [{
          id: 1,
          label: '一级 1',
          children: [{
            id: 4,
            label: '二级 1-1',
            children: [{
              id: 9,
              label: '三级 1-1-1'
            }, {
              id: 10,
              label: '三级 1-1-2'
            }]
          }]
        }, {
          id: 2,
          label: '一级 2',
          children: [{
            id: 5,
            label: '二级 2-1'
          }, {
            id: 6,
            label: '二级 2-2'
          }]
        }, {
          id: 3,
          label: '一级 3',
          children: [{
            id: 7,
            label: '二级 3-1'
          }, {
            id: 8,
            label: '二级 3-2'
          }]
        }],

该示例 树形图绑定的数据类型为data,data数据内部细节为父子孙结构,按照该结构显示到前端页面上树形图的显示是由绑定的数据结构来决定的。

三.表结构

 

 

四.后端

4.1功能分析

1.查询所有的权限,将含有所有权限的集合跟树形图绑定

2.查询不同的角色所拥有的不同权限,用集合存储并调用setCheckedNodes()用来设置选中的节点 

3.修改节点功能

删除该角色所拥有的权限

添加该角色的权限

前端勾选节点,将已勾选的节点的id,打包成string数组,转换成字符串,传给服务器。服务器通过对象的属性保存分割后的字符串id。

4.2实体类

package com.dmdd.javasecuritypractice.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * <p>
 * 
 * </p>
 *
 * @author xray
 * @since 2023-01-31
 */
@Data
public class Permission implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String name;

    private String description;

    private String url;

    private Integer pid;

    private String icon;

    private Integer sort;
    //子权限集合
    @TableField(exist = false)
    private List<Permission> subPermissions;

    @Override
    public String toString() {
        return "Permission{" +
            "id=" + id +
            ", name=" + name +
            ", description=" + description +
            ", url=" + url +
            ", pid=" + pid +
            ", icon=" + icon +
            ", sort=" + sort +
        "}";
    }
}

通过一级权限查询二级权限,实体类里面定义一个集合用来存储二级权限 

4.3 查询全部权限显示的结果

包含所有权限的集合的数据形式 ,将该集合传给前端,树形控件绑定该集合,就会以该方式显示到页面上。

4.2修改角色权限的后台方法 

 //1,2,3,4,5 中间表的 permission_id permission的 id
    @Transactional
    @Override
    public void setRolePermissions(Long roleId, String permissions) {
        ArrayList<RolePermission> rolePermissions = new ArrayList<>();
        //清空该角色权限
//        boolean b = this.removeById(roleId);
        boolean b = this.remove(new QueryWrapper<RolePermission>().lambda().eq(RolePermission::getRoleId, roleId));
        if (b) {
            //将权限依次赋值给rolePermission
            String[] strings = permissions.split(",");
            for (String permissionId : strings) {
                RolePermission rolePermission = new RolePermission();
                //设置当前角色id 权限id id会自增
                rolePermission.setRoleId(roleId);
                rolePermission.setPermissionId(Long.valueOf(permissionId));
                rolePermissions.add(rolePermission);
            }
            //mybats-plus批量添加方法
            boolean b1 = this.saveBatch(rolePermissions);

        }
    }

对中间表进行操作,实现角色权限的修改 

 

 五.vue

5.0代码总览

<template>
  <div>
<!--    添加角色的树形结构-->
    <!--权限树对话框-->
    <el-dialog title="权限信息" :visible.sync="dialogTreeVisible">
      <el-tree
          :data="allPermissions"
          show-checkbox
          default-expand-all
          node-key="id"
          ref="tree"
          highlight-current
          :props="defaultProps">
      </el-tree>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogTreeVisible = false">取 消</el-button>
        <el-button type="primary" @click="setRolePermissions()">确 定</el-button>
      </div>
    </el-dialog>


    <el-button type="text" @click="dialogFormVisible= true">新增模块</el-button>
    <el-dialog title="权限操作" :visible.sync="dialogFormVisible">
      <el-form :model="role">
        <el-form-item label="编号" :label-width="formLabelWidth">
          <el-input v-model="role.id" autocomplete="off"></el-input>
        </el-form-item>

        <el-form-item label="用户名" :label-width="formLabelWidth">
          <el-input v-model="role.name" autocomplete="off"></el-input>
        </el-form-item>

        <el-form-item label="描述" :label-width="formLabelWidth">
          <el-input v-model="role.description" autocomplete="off"></el-input>
        </el-form-item>

        <el-form-item label="地区id" :label-width="formLabelWidth">
          <el-input v-model="role.siteId" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="update()">确 定</el-button>
      </div>
    </el-dialog>
    <el-table
        :data="roles"
        style="width: 100%">
      <el-table-column
          label="id"
          width="180">
        <template slot-scope="scope">
          <i class="el-icon-time"></i>
          <span style="margin-left: 10px">{{ scope.row.id }}</span>
        </template>
      </el-table-column>
      <el-table-column
          label="用户名"
          width="180">
        <template slot-scope="scope">
          <el-popover trigger="hover" placement="top">
            <p>姓名: {{ scope.row.name }}</p>
            <p>id: {{ scope.row.id }}</p>
            <div slot="reference" class="name-wrapper">
              <el-tag size="medium">{{ scope.row.name }}</el-tag>
            </div>
          </el-popover>
        </template>
      </el-table-column>
      <el-table-column
          label="描述"
          width="180">
        <template slot-scope="scope">
          <i class="el-icon-time"></i>
          <span style="margin-left: 10px">{{ scope.row.description }}</span>
        </template>
      </el-table-column>
      <el-table-column
          label="siteId"
          width="180">
        <template slot-scope="scope">
          <i class="el-icon-time"></i>
          <span style="margin-left: 10px">{{ scope.row.siteId }}</span>
        </template>
      </el-table-column>



      <el-table-column label="操作">
        <template slot-scope="scope">
          <el-button
              size="mini"
              @click="handleEdit(scope.$index, scope.row)">编辑
          </el-button>
          <el-button
              size="mini"
              @click="openTree(scope.row.id)">修改权限
          </el-button>
          <el-button
              size="mini"
              type="danger"
              @click="handleDelete(scope.$index, scope.row)">删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    //分页功能
    <el-pagination
        background
        layout="prev, pager, next"
        :total="total"
        :page-size="pageSize"
        @current-change="loadRole"
    >
    </el-pagination>
  </div>
</template>

<script>
export default {
  name: "RoleView",
  data() {
    return {
      roles: [],
      role: {},
      current: 1,
      total: 0,
      pageSize: 10,
      dialogFormVisible: false,
      formLabelWidth: "120px",
      dialogTreeVisible: false,
      allPermissions: [],
      rolePermissions:[],
      defaultProps: {
        children: 'subPermissions',
        label: 'name'
      },
      RoleId:-1
    }
  },
  methods:{
    //点击修改角色权限 弹出树形图
    openTree(roleId){
      this.RoleId=roleId;
      this.dialogTreeVisible=true
      //查询所有权限通过角色
      this.axios.get("http://localhost:8080/permissionsByRole")
      .then(result=>{
        console.log("所有权限:"+result)
        if (result.data.status=="OK"){
          this.allPermissions=result.data.data;
          //查询该角色拥有的权限
          this.axios.get("http://localhost:8080/permissionsByRoleId?RoleId="+roleId)
          .then(result=>{
            console.log("当前角色的权限"+result)
            if (result.data.status=="OK"){
              this.rolePermissions=result.data.data;
              //设置勾选效果
              this.$refs.tree.setCheckedNodes(this.rolePermissions);
            }
          })
        }
      })
    },
    //设置角色权限
    setRolePermissions(){
      console.log(this.$refs.tree.getCheckedKeys());
      //将数组转换成字符串
      let keys = this.$refs.tree.getCheckedKeys();
      let keyStr=""
      for (let i=0;i<keys.length;i++){
        keyStr+=keys[i]+",";
      }
      keyStr.substr(0,keyStr.length-1);
      //调用后台接口
      this.axios.post("http://localhost:8080/permission/set-role","roleId="+this.RoleId+"&permissions="+keyStr)
      .then(result=>{
        if (result.data.status=="OK"){
          this.$message(result.data.data);
        }
      })
      this.dialogTreeVisible=false;
    },

    //查询角色表
    loadRole(current,pageSize){
      this.current=current;
      this.axios.get("http://localhost:8080/role/page?current="+this.current+"&pageSize="+this.pageSize)
      .then(result=>{
        console.log(result)
        this.roles=result.data.data.records
        this.total=result.data.data.total
      })
    },
    update() {
      if (this.role.id) {
        //执行修改方法
        this.axios.put("http://localhost:8080/role", this.role)
            .then(result => {
              if (result.data.status == "OK") {
                console.log(result.data)
                this.role={}
                this.dialogFormVisible=false
                this.loadRole(this.current)
              }
            })
      }
      else{
        this.axios.post("http://localhost:8080/role",this.role)
            .then(result=>{
              if (result.data.status=="OK"){
                console.log(result)
                this.role={}
                this.dialogFormVisible=false
                this.loadRole(1)
              }
            })
      }
    },
    //回显
    handleEdit(index,row){
      this.role=JSON.parse(JSON.stringify(row));
      this.dialogFormVisible=true
    },
  },
  mounted() {
    this.loadRole(1)
  }
}
</script>

<style scoped>

</style>

5.1树形图


<!--    添加角色的树形结构-->
    <!--权限树对话框-->
    <el-dialog title="权限信息" :visible.sync="dialogTreeVisible">
      <el-tree
          :data="allPermissions"
          show-checkbox
          default-expand-all
          node-key="id"
          ref="tree"
          highlight-current
          :props="defaultProps">
      </el-tree>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogTreeVisible = false">取 消</el-button>
        <el-button type="primary" @click="setRolePermissions()">确 定</el-button>
      </div>
    </el-dialog>

树形图控件 

 5.2所需要的绑定数据

 allPermissions: [],
      rolePermissions:[],
      defaultProps: {
        children: 'subPermissions',
        label: 'name'
      },
      RoleId:-1

 defaultProps: {
        children: 'subPermissions',
        label: 'name'
      },

设置树形图父节点下的集合名字为subPermissions.  该集合名allPermissions是集合里的对象的属性

label 表示每个节点的名字 我设置的name表示的是 allPermissions里的对象的name属性值

 

5.3所需方法

 //点击修改角色权限 弹出树形图
    openTree(userId) {
      this.userId = userId;
      this.dialogTreeVisible = true
      //查询所有权限通过角色
      this.axios.get("http://localhost:8080/selectAllRole")
          .then(result => {
            console.log("所有权限:" + result)
            if (result.data.status == "OK") {
              this.allRoles = result.data.data;
              //查询该角色拥有的权限
              this.axios.get("http://localhost:8080/select-role?userId=" + userId)
                  .then(result => {
                    console.log("当前角色的权限" + result)
                    if (result.data.status == "OK") {
                      this.userRoles = result.data.data;
                      //设置勾选效果
                      this.$refs.tree.setCheckedNodes(this.userRoles);
                    }
                  })
            }
          })
    },
    //设置用户的角色
    setUserRoles() {
      console.log(this.$refs.tree.getCheckedKeys());
      //将数组转换成字符串
      let keys = this.$refs.tree.getCheckedKeys();
      let keyStr = ""
      for (let i = 0; i < keys.length; i++) {
        keyStr += keys[i] + ",";
      }
      keyStr.substr(0, keyStr.length - 1);
      //调用后台接口
      this.axios.post("http://localhost:8080/setRole", "userId=" + this.userId + "&roles=" + keyStr)
          .then(result => {
            // if (result.request.status == 200) {
              this.$message(result.data);
            // }
          })
      this.dialogTreeVisible = false;
    },

点击修改权限的按钮,设置树形图为可见,显示所有节点数据,设置勾选节点是哪些。

1.将后端的通过角色查询到的权限的集合传入前台,通过

this.$refs.tree.setCheckedNodes(this.rolePermissions);
设置勾选节点

其他的大家可以自己去体会,如果完全了解的话,不管是怎么样的数据结构的树形图都会写

最后附上我的另一个通过用户查询角色权限的实现图。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/338567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微搭低代码从入门到精通04-创建自定义应用

微搭中的应用分为两类&#xff0c;模型应用和自定义应用。上一篇我们介绍了模型应用的创建方法&#xff0c;本篇我们介绍一下自定义应用的创建方法。 登录微搭的控制台&#xff0c;在左侧的菜单里点击应用&#xff0c;点击新建应用&#xff0c;选择新建自定义应用 输入应用的名…

logstash毫秒时间戳转日期以及使用业务日志时间戳替换原始@timestamp

文章目录问题解决方式参考问题 在使用Kibana观察日志排查问题时发现存在很多组的timestamp 数据一样&#xff0c;如下所示 详细观察内部数据发现其中日志数据有一个timestamp字段保存的是业务日志的毫秒级时间戳&#xff0c;经过和timestamp数据对比发现二者的时间不匹配。经…

1630_GNU的二进制分析工具nm简单使用探索

全部学习汇总&#xff1a; GreyZhang/toolbox: 常用的工具使用查询&#xff0c;非教程&#xff0c;仅作为自我参考&#xff01; (github.com) GNU有一套二进制的分析工具&#xff0c;之前是用过objdump的&#xff0c;但是也没有系统掌握。如果做底层软件的设计&#xff0c;这些…

OpenWrt路由器设置域名动态解析手把手教程

文章目录0、前言1、准备工作2、详细步骤2.1、OpenWrt路由器软件包安装2.2、防火墙放行入站数据&#xff08;修改为“接受”并保存应用&#xff09;2.3、域名解析服务商对域名的解析设置2.4、路由器中动态域名插件的设置0、前言 因为一直用着内网穿透&#xff08;zerotier或者是…

SQLyog Ultimate 13.2 Crack

SQLyog终极版 MySQL开发工具 让您的数据库开发人员在短时间内完成大量工作并不是确保质量结果的最佳方式。当涉及到您的 MySQL 数据库时&#xff0c;您不想偷工减料 — 尤其是当这样做会导致代价高昂的中断时。 使用 SQLyog 获得可靠的数据库备份和数据同步&#xff1a;一个强大…

HTTP协议路由转发小结

问题介绍&#xff1a;个人电脑 hosts 文件配置如下&#xff1a; xx.xx.xx.243 xxx.domain.com 其中 xx.xx.xx.243 是里约网关的服务器ip&#xff0c;而应用部署在 xx.xx.xx.93 上&#xff0c;但最终请求为何会转发到93这台服务器上呢&#xff1f; 原因是里约网关配置了转发规则…

【hello, world】计算机系统漫游

文章目录hello程序信息就是位 上下文程序被其他程序翻译成不同的格式预处理阶段编译阶段汇编阶段链接阶段了解编译系统如何工作是大有益处的优化程序性能理解链接时出现的错误避免安全漏洞处理器读并解释储存在内存中的指令系统的硬件组成总线I/O设备主存处理器运行hello程序高…

web3:区块链共识机制系列-POS(Proof of Stake)股权证明算法

web3相关学习一并收录至该博客&#xff1a;web3学习博客目录大全 前情衔接&#xff1a;web3:区块链常见的几大共识机制及优缺点 目录前言算法公式与原理算法公式运作原理以Peer Coin为例缺陷优点缺点特点分类发展历程casper协议1.什么是无成本利益关系问题2.引入casper协议解决…

力扣33.搜索旋转排序数组

文章目录力扣33.搜索旋转排序数组题目描述方法1&#xff1a;二分查找力扣33.搜索旋转排序数组 题目描述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&…

Android框架WiFi架构

同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 一、wpa_supplicant:wpa_supplicant本身开源项目源码,被谷歌收购之后加入Android移…

Python中的类和对象(4)

1. 构造函数 &#xff08;_ init _(self[,……])&#xff09; 在类中定义 _init _() 方法&#xff0c;可以实现在实例化对象的时候进行个性化定制&#xff1a; >>> class C: ... def __init__(self, x, y): ... self.x x ... self.y y ... …

离散数学 课时二 命题逻辑等值演算

等值式(等值联结词) 1、设A、B是两个命题公式,若A、B构成的等价式 A等价于B 为重言式,那么称A与B是等值的 2、常用等值式&#xff1a; 注意&#xff1a; 1 双否定律 2 幂等律 3 交换律 4 结合律 5 吸收律 6 德摩根律 7 同一律 8 零律 9 矛盾律 10 排中律 11 蕴含表达式 12 …

十分钟带你体验一下什么是分布式事务

我们经常在网上看到很多人发关于分布式事务的理论&#xff0c;但是讲实战的却非常少&#xff0c;所以我今天想通过一个案例&#xff0c;来让小伙伴们都感受一下什么是分布式事务&#xff0c;这篇文章理论偏少&#xff0c;请文明观看。咱们今天的主角是 Seata&#xff01; 分布…

Django项目开发

一.认识NoSQL 1.SQL 关系型数据库 结构化: 定义主键&#xff0c;无符号型数据等关联的&#xff1a;结构化表和表之间的关系通过外键进行关联&#xff0c;节省存储空间SQL查询&#xff1a;语法固定 SELECT id,name,age FROM tb_user WHERE id1 ACID 2.NoSQL 非关系型数据库 Re…

通过实例告诉你lua中ipairs到底是怎么遍历的!

这个的文章挺多的&#xff0c;但是有好几种说法并且不全。有人说是忽略手动设定值&#xff0c;有人说是从1开始数&#xff0c;直到序号断开&#xff0c;还有人给出结果&#xff0c;但是和我实机测试的效果不一样&#xff0c; 所以我自己总结一篇。经过我的测试和总结得到以下结…

【2023】Prometheus-Alertmanager高可用集群

本次实验准备了三个节点&#xff0c;分别为laert-01~03 目录1.安装Alertmanager2.配置启动文件3.验证集群4.关于集群的配置项1.安装Alertmanager 这部分内容在三个节点上都要执行 下载安装包&#xff0c;将安装包解压至/data目录下 wget https://github.com/prometheus/aler…

javassm高校学生评教系统的设计与实现idea msyql

伴随着社会以及科学技术的发展&#xff0c;互联网已经渗透在人们的身边&#xff0c;网络慢慢的变成了人们的生活必不可少的一部分&#xff0c;紧接着网络飞速的发展&#xff0c;管理系统这一名词已不陌生&#xff0c;越来越多的学校、公司等机构都会定制一款属于自己个性化的管…

Scout:一款功能强大的轻量级URL模糊测试与爬取工具

关于Scout Scout是一款功能强大的轻量级URL模糊测试与爬取工具&#xff0c;可以帮助广大研究人员进行URL模糊测试&#xff0c;并爬取目标Web服务器中难以扫描发现的VHSOT、文件和目录等资源。 项目中包含了一个完整的字典文件&#xff0c;并尽可能地提供了更多的便携性&#…

【寻人启事】达坦科技持续招人ing

​​​​​​​ ❤️一起来探索前沿科技&#xff0c;做有意思的事情~ 我们是谁 达坦科技&#xff08;DatenLord&#xff09;专注于打造新一代开源跨云存储平台。通过软硬件深度融合的方式打通云云壁垒&#xff0c;实现无限制跨云存储、跨云联通&#xff0c;建立海量异地、异构…

活动星投票在时间的河流上造园分组怎么设置如何进行分组报名

“在时间的河流上造园”网络评选投票_免费小程序运行系统_企业有关的投票_微信投票的应用小程序投票活动如何做&#xff1f;很多企业在运营当中&#xff0c;都会通过投票活动来进行推广&#xff0c;从而达到吸粉、增加用户粘度等效果。而此类投票活动&#xff0c;通过小程序就可…