当前位置:   article > 正文

Element-UI+Vue实现开发权限_elementui权限

elementui权限

前面我们已经进行过表单的提交以及侧边栏的开发,现在我们来学习实现开发权限

目录

一、权限列表基本设置

1.1 配置路由规则

1.2 基本布局

1.3 获取所有权限列表

1.31获取权限列表的代码

1.3.2 渲染页面

二、角色列表基本设置

2.1 分析用户、角色、权限三者的关系

2.2 角色列表路由设置

2.3 获取角色列表数据

2.4  基本渲染角色列表页面(后期会有更牛的地方)

2.5  渲染展开列(这个就是更牛的地方)

2.6  删除角色制定权限方法实现

2.7   分配权限功能实现

2.7.1 获取所有权限列表

2.7.2 使用ElementUI中Tree树形控件,并实现复选框功能

2.7.3 真正的实现分配权限功能

三、用户管理——用户列表中的分配角色



一、权限列表基本设置

   1.1 配置路由规则

        注意,是在Home下面的子路由中配置

  1. {
  2. // 这个地方不能乱写,应该是我们点击页面之后展示的那个路径
  3. path:'/rights',
  4. component:Rights
  5. }

  1.2 基本布局

面包屑导航区、卡片视图

  1. <!-- 权限控制 -->
  2. <template>
  3. <div>
  4. <!-- 面包屑导航区域 -->
  5. <el-breadcrumb separator-class="el-icon-arrow-right">
  6. <!-- 当点击首页的时候跳转到/home -->
  7. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
  8. <el-breadcrumb-item>权限管理</el-breadcrumb-item>
  9. <el-breadcrumb-item>权限列表</el-breadcrumb-item>
  10. </el-breadcrumb>
  11. <!-- 卡片视图 -->
  12. <el-card>
  13. </el-card>
  14. </div>
  15. </template>
  16. <script>
  17. export default {
  18. name:'Rights'
  19. }
  20. </script>
  21. <style lang="less" scoped>
  22. </style>

   

1.3 获取所有权限列表

  获取所有权限列表的请求路径及参数如下图所示:这个地方我们传入一个list就可以了,传入list列表显示权限

    1.31获取权限列表的代码

  1. data(){
  2. return{
  3. // 权限列表
  4. rightsList:[]
  5. }
  6. },
  7. created(){
  8. // 我们要在这个地方获取权限列表,渲染页面
  9. this.getRightsList()
  10. },
  11. methods:{
  12. // 获取用户所有权限列表
  13. async getRightsList(){
  14. // list,这并不是一个路径参数,就是一个路径的一部分,如果我们传入一个list的话,就代表服务器给我们返回一个list集合
  15. const{data:res} = await this.$http.get('rights/list')
  16. if(res.meta.status !==200){
  17. // 运行到这里代表着获取权限列表失败
  18. return this.$message.error('获取权限列表失败!')
  19. }
  20. this.rightsList = res.data
  21. }

 当我们在控制台输出的时候,是获取的下图中的数据

1.3.2 渲染页面

首先按需导入一下Tag标签

Vue.component('el-tag',Tag)

下面来渲染页面

  1. <!-- 卡片视图 -->
  2. <el-card>
  3. <!-- :data 代表着获取数据源 border添加一个竖向的边框线 stripe隔行阴影效果-->
  4. <el-table :data="rightsList" border stripe>
  5. <!-- 索引列 -->
  6. <el-table-column type="index" ></el-table-column>
  7. <!-- prop="authName"指定回去数据源中的哪一个 -->
  8. <el-table-column label="权限名称" prop="authName" ></el-table-column>
  9. <el-table-column label="路径" prop="path" ></el-table-column>
  10. <el-table-column label="权限等级" prop="level" >
  11. <!-- 作用域插槽自定义格式 使用slot-scope="scope"接收所有的数据-->
  12. <template slot-scope="scope">
  13. <!-- 适用于:切换频率较低的场景 -->
  14. <!-- 特点:不展示的DOM元素直接被移除,在页面源码中无法看到 -->
  15. <!-- 注意:v-if,v-else-if 、 else if,div要紧紧的挨在一起,不能打断 -->
  16. <el-tag v-if="scope.row.level ==='0'">一级</el-tag>
  17. <el-tag type="success" v-else-if="scope.row.level ==='1'">二级</el-tag>
  18. <el-tag type="warning" v-else-if="scope.row.level ==='2'">三级</el-tag>
  19. </template>
  20. </el-table-column>
  21. </el-table>
  22. </el-card>

二、角色列表基本设置

2.1 分析用户、角色、权限三者的关系

每一个操作都是一个权限,比如增删改查,有的用户只有增加的权限,有的有删除的权限等等等等

下面我们的分析思路就是给用户分配一个角色,这个角色就对应这某个操作的操作权限

所以下面我们就来开发角色列表

2.2 角色列表路由设置

  1. {
  2. path:'/roles',
  3. component:Roles
  4. }

 2.3 获取角色列表数据

下面就是响应的数据,最多有三级结构,我们可以判断一下是否有children,然后是否有三级权限

我们可以仔细看一下下面返回数据的列表是一个数组,数组的每一项就是一个对象(角色),每一个对象下面还有一个children属性,这个children属性就是当前角色所拥有的所有权限

  1. data(){
  2. return{
  3. // 所有角色的列表数据
  4. rolelist:[]
  5. }
  6. },
  7. created(){
  8. this.getRolesList()
  9. },
  10. methods:{
  11. async getRolesList(){
  12. const {data:res} = await this.$http.get('roles')
  13. if(res.meta.status !==200){
  14. return this.$message.error('获取角色列表失败!')
  15. }
  16. // 赋值
  17. this.rolelist = res.data
  18. }
  19. }

 2.4  基本渲染角色列表页面(后期会有更牛的地方)

  1. <div>
  2. <!-- 面包屑导航区域 -->
  3. <el-breadcrumb separator-class="el-icon-arrow-right">
  4. <!-- 当点击首页的时候跳转到/home -->
  5. <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
  6. <el-breadcrumb-item>权限管理</el-breadcrumb-item>
  7. <el-breadcrumb-item>角色列表</el-breadcrumb-item>
  8. </el-breadcrumb>
  9. <!-- 卡片视图 -->
  10. <el-card>
  11. <!--添加角色按钮区 一行 -->
  12. <el-row>
  13. <!--再放一列 -->
  14. <el-col>
  15. <el-button type="primary"> 添加角色</el-button>
  16. </el-col>
  17. </el-row>
  18. <!-- 角色列表区 -->
  19. <el-table :data="rolelist" border stripe>
  20. <!-- 展开列 -->
  21. <el-table-column type="expand"></el-table-column>
  22. <!-- 索引列 -->
  23. <el-table-column type="index"></el-table-column>
  24. <el-table-column label="角色名称" prop="roleName"></el-table-column>
  25. <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
  26. <el-table-column label="操作">
  27. <template slot-scope="scope">
  28. <el-button type="primary" icon="el-icon-edit">编辑</el-button>
  29. <el-button type="danger" icon="el-icon-delete">删除</el-button>
  30. <el-button type="warning" icon="el-icon-setting">分配权限</el-button>
  31. </template>
  32. </el-table-column>
  33. </el-table>
  34. </el-card>
  35. </div>

如下图所示, 我们目前就渲染成了现在的这个样子

2.5  渲染展开列(这个就是更牛的地方)

我们可以先利用作用域插槽查看一下我们展开列有哪一些内容

  1. <!-- 展开列 -->
  2. <el-table-column type="expand">
  3. <template slot-scope="scope">
  4. <pre>
  5. {{scope.row}}
  6. </pre>
  7. </template>

其实我们仔细观察就能看出来,一个角色下面最多有三层结构,我们利用三个for循环遍历得出

  1. <!-- 卡片视图 -->
  2. <el-card>
  3. <!--添加角色按钮区 一行 -->
  4. <el-row>
  5. <!--再放一列 -->
  6. <el-col>
  7. <el-button type="primary"> 添加角色</el-button>
  8. </el-col>
  9. </el-row>
  10. <!-- 角色列表区 -->
  11. <el-table :data="rolelist" border stripe>
  12. <!-- 展开列 -->
  13. <el-table-column type="expand">
  14. <template slot-scope="scope">
  15. <!--Layout布局,将一行分为了24块 将用户的权限逐步的遍历出来-->
  16. <!-- :class="['bdbottom',i1 ===0?'bdtop':'']"使用语法我们绑定一些样式:bdbottom代表着都在底部加一个底边框,如果i1是0的话,就代表着是顶部,我们也要在顶部加一个边框 -->
  17. <el-row :class="['bdbottom',i1 ===0?'bdtop':'','vcenter']" v-for="(item1,i1) in scope.row.children" :key="item1.id">
  18. <!-- 渲染一级权限 scope.row就是我们这一个角色的数据对象,我们使用了一个作用域插槽 -->
  19. <el-col :span="5" >
  20. <el-tag closable @close="removeRightById(scope.row,item1.id)" >{{item1.authName}}</el-tag>
  21. <!-- 在每一个图标后面加一个小箭头 -->
  22. <i class="el-icon-caret-right"></i>
  23. </el-col>
  24. <!-- 渲染二、 9三级权限 -->
  25. <el-col :span="19">
  26. <!-- 通过for循环嵌套渲染二级权限 i2===0 ?'':'bdtop' 当i2不是0的时候,我们就添加一个顶部边框线-->
  27. <el-row :class="[i2===0 ?'':'bdtop','vcenter']" v-for="(item2,i2) in item1.children" :key="item2.id">
  28. <!-- 这一列是二级权限
  29. 我们为什么还是24列,我们前面不是用过了5列?因为又把剩下的重新分配了 -->
  30. <el-col :span="6">
  31. <el-tag closable @close="removeRightById(scope.row,item2.id)" type="success">{{item2.authName}}</el-tag>
  32. <i class="el-icon-caret-right"></i>
  33. </el-col>
  34. <!-- 这一列是三级权限 closable代表着这个标签可以关闭 @close这是ElementUI给我们提供的一个关闭标签的事件,当我们点击叉号执行的时候就会执行下面这个函数-->
  35. <el-col :span="18">
  36. <el-tag type="warning" closable @close="removeRightById(scope.row,item3.id)"
  37. v-for="(item3,i3) in item2.children" :key="item3.id">{{item3.authName}}</el-tag>
  38. </el-col>
  39. </el-row>
  40. </el-col>
  41. </el-row>
  42. </template>
  43. </el-table-column>
  44. <!-- 索引列 -->
  45. <el-table-column type="index"></el-table-column>
  46. <el-table-column label="角色名称" prop="roleName"></el-table-column>
  47. <el-table-column label="角色描述" prop="roleDesc"></el-table-column>
  48. <el-table-column label="操作">
  49. <!-- 作用域插槽 -->
  50. <template slot-scope="scope">
  51. <el-button type="primary" icon="el-icon-edit">编辑</el-button>
  52. <el-button type="danger" icon="el-icon-delete">删除</el-button>
  53. <el-button type="warning" icon="el-icon-setting">分配权限</el-button>
  54. </template>
  55. </el-table-column>
  56. </el-table>
  57. </el-card>
  1. // 当我们点击标签的叉号的时候,就会执行这个函数
  2. // role是角色,rightId是我们想要删除的那个权限的id
  3. async removeRightById(role,rightId){
  4. // 这段代码是从ElementUI中复制的,属于MessageBox 弹框组件
  5. this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
  6. confirmButtonText: '确定',
  7. cancelButtonText: '取消',
  8. type: 'warning'
  9. }).then(() => {
  10. this.$message({
  11. type: 'success',
  12. message: '删除成功!'
  13. });
  14. // 在这里再写之后的逻辑
  15. // console.log("确认了删除")
  16. }).catch(() => {
  17. this.$message({
  18. type: 'info',
  19. message: '已取消删除'
  20. });
  21. });
  22. }

  1. // 让每一个标签之间有间隙
  2. .el-tag{
  3. margin:7px;
  4. }
  5. // 在顶部加一个边框
  6. .bdtop{
  7. border-top:1px solid #eee;
  8. }
  9. // 在底部加一个边框
  10. .bdbottom{
  11. border-bottom: 1px solid #eee;
  12. }
  13. // 纵向居中对齐
  14. .vcenter{
  15. display: flex;
  16. align-items:center
  17. }

  我们为了让页面有一个最小的宽度,我们还设置了一个全局样式

  1. html,body,#app{
  2. height: 100%;
  3. margin:0;
  4. padding: 0;
  5. /* 给页面加一个最小宽度 */
  6. min-width: 1500px;
  7. }

最终我们实现了下面的页面效果

2.6  删除角色制定权限方法实现

这个地方的亮点就是刷新之后页面不会发生整个页面的刷新,但是局部的数据改变了

  1. // 当我们点击标签的叉号的时候,就会执行这个函数
  2. // role是角色,rightId是我们想要删除的那个权限的id
  3. async removeRightById(role,rightId){
  4. // 这段代码是从ElementUI中复制的,属于MessageBox 弹框组件
  5. const confirmResult = await this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
  6. confirmButtonText: '确定',
  7. cancelButtonText: '取消',
  8. type: 'warning'
  9. }).catch(err=>err)
  10. if(confirmResult !=='confirm'){
  11. return this.$message.info('取消了删除!')
  12. }
  13. const{data:res} = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)
  14. if(res.meta.status !==200){
  15. return this.$message.error('删除权限失败!')
  16. }
  17. // 这个地方我们不推荐获取新页面,因为我们每次删除成功后,都会导致页面的刷新,然后就导致我们的列表关闭,然后我们还得再重新打开列表看看我们是否删除成功
  18. // this.getRolesList()
  19. // 所以这个地方我们重新赋值就可以了
  20. // role就是我们的对象
  21. role.children = res.data
  22. // 这样就不会出现页面刷新导致对应展开行关闭的选项
  23. }

2.7   分配权限功能实现

   2.7.1 获取所有权限列表

       我们要渲染成一个树形结构,所以在传入参数的时候我们应该传入tree

<el-button @click="showSetRigthDialog()" type="warning" icon="el-icon-setting">分配权限</el-button>
  1. <!-- 分配权限的对话框 -->
  2. <el-dialog
  3. title="提示"
  4. :visible.sync="setRightDialogVisible"
  5. width="50%">
  6. <span>分配权限</span>
  7. <span slot="footer" class="dialog-footer">
  8. <el-button @click="setRightDialogVisible = false">取 消</el-button>
  9. <el-button type="primary" @click="setRightDialogVisible = false">确 定</el-button>
  10. </span>
  11. </el-dialog>
  1. // 控制分配权限对话框的显示与隐藏
  2. setRightDialogVisible:false,
  3. // 所有权限的数据
  4. rightslist:[],

  1. // 展示分配权限功能的对话框
  2. async showSetRigthDialog(){
  3. // 获取所有权限的数据
  4. const{data:res} = await this.$http.get('rights/tree')
  5. if(res.meta.status !==200){
  6. return this.$message.error('获取权限数据失败')
  7. }
  8. // 获取到的权限数据
  9. this.rightslist=res.data
  10. this.setRightDialogVisible=true
  11. }

  2.7.2 使用ElementUI中Tree树形控件,并实现复选框功能

 我们在树形结构的基础上,在实现一个复选框供我们勾选,通过查阅ElementUI官方文档,我们发现有一个属性是show-checkbox,我们添加上就可以了

但是我们使用复选框的时候,如果我们勾选某一个选项,我们想要的是这个选项的id值并不是这个选项的文本值,通过查阅文档,我们又发现了下面这个属性  node-key属性,这个属性是说明每个树节点用来作为唯一标识的属性,整棵树应该是唯一的,所以在这个地方我们应该是绑定id值,毕竟我们设置的id值就是唯一的(为什么是id?因为我们服务器传过来的唯一标识就是id

默认展开所有结点

默认勾选结点 ,然后绑定一个数组

 @click="showSetRigthDialog(scope.row)",scope.row代表着这一整行的数据对象,也就是人物,我们为什么要传入这个呢?因为我们要实现默认勾选结点,利用递归的方式

  <el-button @click="showSetRigthDialog(scope.row)" type="warning" icon="el-icon-setting">分配权限</el-button>
  1. <!-- 分配权限的对话框 -->
  2. <el-dialog @close="setRightDialogClosed()"
  3. title="提示" :visible.sync="setRightDialogVisible" width="50%">
  4. <!-- 树形控件 :data="rightslist"表示数据源
  5. :props="treeProps"指定绑定哪个字段
  6. node-key="id" 你只要选中了我这个结点,就选中了我对应的id值而不是文本值-->
  7. <el-tree node-key="id" default-expand-all :default-checked-keys="defKeys"
  8. show-checkbox :data="rightslist" :props="treeProps"></el-tree>
  9. <!-- 底部按钮 -->
  10. <span slot="footer" class="dialog-footer">
  11. <el-button @click="setRightDialogVisible = false">取 消</el-button>
  12. <el-button type="primary" @click="setRightDialogVisible = false">确 定</el-button>
  13. </span>
  14. </el-dialog>
  1. // 树形控件的属性绑定对象
  2. treeProps:{
  3. // label属性指定我们看到的是哪一个属性,我们这里制定看到authName字段
  4. label:'authName',
  5. // children 第一个children表示哪个字段实现的父子嵌套
  6. children:'children'
  7. },
  8. // 默认选中的节点Id值数组
  9. defKeys:[],
  10. // 当前即将分配角色的id
  11. roleId:''
  1. // 展示分配权限功能的对话框
  2. async showSetRigthDialog(role){
  3. // 保存一下,后面会使用
  4. this.roleId = role.id
  5. // 获取所有权限的数据
  6. const{data:res} = await this.$http.get('rights/tree')
  7. if(res.meta.status !==200){
  8. return this.$message.error('获取权限数据失败')
  9. }
  10. // 获取到的权限数据
  11. this.rightslist=res.data
  12. // 递归获取三级节点的id值
  13. this.getLeafKeys(role,this.defKeys)
  14. this.setRightDialogVisible=true
  15. },
  16. // 定义一个递归函数,作用就是将所有的三级结点的id值保存到defKeys数组中
  17. // 我们要有一个结点node来判断是否是三级节点,同时还需要一个数组来进行保存
  18. getLeafKeys(node,arr){
  19. if(!node.children){
  20. //不包含children属性, 运行到这里说明node是一个三级节点
  21. // 当递归结束后,肯定都把三级节点的id值存放到了arr数组中
  22. return arr.push(node.id)
  23. }
  24. // 不是三级节点 遍历三级节点
  25. node.children.forEach(item=>this.getLeafKeys(item,arr))
  26. },
  27. // 当我们关闭分配权限对话框的时候,我们应该清空数组,要不然以后数组中的内容越来越多
  28. setRightDialogClosed(){
  29. this.defKeys=[]
  30. }

如图所示,我们初步渲染成了下面的这个样子

2.7.3 真正的实现分配权限功能

首先我们来了解一下这两个函数:当我们在对话框点击“确定按钮”之后,我们要调用下面这两个函数

第一个:获取已选中结点的id值,当做一个数组被返回回来

第二个 :获取半选中结点的id,当做一个数组被返回回来

  1. // 当前即将分配角色的id
  2. roleId:''

 我们在什么时候给上面的roleId赋值?但是是在展示对话框的时候

  1. // 展示分配权限功能的对话框
  2. async showSetRigthDialog(role){
  3. // 保存一下,后面会使用
  4. this.roleId = role.id
  5. // 获取所有权限的数据
  6. const{data:res} = await this.$http.get('rights/tree')
  7. if(res.meta.status !==200){
  8. return this.$message.error('获取权限数据失败')
  9. }
  10. // 获取到的权限数据
  11. this.rightslist=res.data
  12. // 递归获取三级节点的id值
  13. this.getLeafKeys(role,this.defKeys)
  14. this.setRightDialogVisible=true
  15. },
  1. // 点击为角色分配权限
  2. async allotRights(){
  3. // 把两个数组获取成为一个新数组 获取我们选中的和半选中的id值
  4. const keys =[
  5. // 获取选中的id值 ...三个点是展开运算符表示一项一项的放到keys这个数组中
  6. ...this.$refs.treeRef.getCheckedKeys(),
  7. ...this.$refs.treeRef.getHalfCheckedKeys()
  8. ]
  9. // console.log(keys)
  10. // 将我们的keys拼接成字符串
  11. const idStr= keys.join(',')
  12. // console.log(idStr)
  13. // {rids:idStr} 服务器需要传输一个rids的请求体
  14. const{data:res} = await this.$http.post(`roles/${this.roleId}/rights`,{rids:idStr})
  15. if(res.meta.status !==200){
  16. console.log(res)
  17. return this.$message.error('分配权限失败')
  18. }
  19. this.$message.success('分配权限成功!')
  20. this.getRolesList()
  21. this.setRightDialogVisible=false
  22. }

三、用户管理——用户列表中的分配角色

之前我们在做用户列表开发的时候,我们的分配角色功能还没有做完,我们现在把这个功能做完

 我们先给“分配角色”按钮绑定一个单击事件

  1. <!--effect 提示文字的背景颜色 :enterable="false" 默认自动隐藏 -->
  2. <el-tooltip :enterable="false" class="item" effect="dark" content="分配角色" placement="top">
  3. <!-- 分配角色按钮 -->
  4. <el-button @click="setRole(scope.row)" size="mini" type="warning" icon="el-icon-setting"></el-button>
  5. </el-tooltip>

 

  1. // 控制分配角色的对话框
  2. setRoleDialogVisible:false,
  3. // 需要被分配角色的用户信息
  4. userInfo:{},
  5. // 所有的角色的数据列表
  6. rolesList:[],
  7. // 已经选中的角色Id值 我们在下拉列表中选中的值,都会保存到这里面去
  8. selectedRoleId:''
  1. <!-- 分配角色的对话框 -->
  2. <el-dialog @close="setRoleDialogClosed()"
  3. title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" >
  4. <div>
  5. <p>当前的用户:{{userInfo.username}}</p>
  6. <p>当前的角色:{{userInfo.role_name}}</p>
  7. <p>分配新角色:
  8. <el-select v-model="selectedRoleId" placeholder="请选择">
  9. <!-- label指定显示的内容 value指定我们选中某个内容后真正选中的value值(一般是id) -->
  10. <el-option v-for="item in rolesList" :key="item.id"
  11. :label="item.roleName" :value="item.id"> </el-option>
  12. </el-select>
  13. </p>
  14. </div>

 

角色列表的获取方式如下图所示

  1. // 展示分配角色的对话框
  2. async setRole(userInfo){
  3. // 需要被分配角色的用户信息
  4. this.userInfo = userInfo
  5. // 在展示对话框之前获取所有的角色列表
  6. const{data:res} = await this.$http.get('roles')
  7. if(res.meta.status!==200){
  8. return this.$message.error('获取角色列表失败!')
  9. }
  10. this.rolesList = res.data
  11. // 展示分配角色的对话框
  12. this.setRoleDialogVisible=true
  13. }

 

 

分配角色的请求路径以及请求参数

 我们要在对话框的“确定”按钮上绑定一个单击事件”

<el-button type="primary" @click="saveRoleInfo()">确 定</el-button>

单击事件执行的方法 :

  1. // 点击按钮,分配角色
  2. async saveRoleInfo(){
  3. if(!this.selectedRoleId){
  4. return this.$message.error('请选择要分配的角色')
  5. }
  6. // 发送请求 分配角色
  7. const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{
  8. rid:this.selectedRoleId
  9. })
  10. console.log("userInfo",this.selectedRoleId)
  11. if(res.meta.status !==200){
  12. return this.$message.error('更新角色失败!')
  13. }
  14. this.$message.success('更新角色成功!')
  15. // 重新获取用户列表
  16. this.getUserList()
  17. this.setRoleDialogVisible=false
  18. },
  19. // 分配对话框关闭时我们要执行的函数
  20. setRoleDialogClosed(){
  21. this.selectenRoleId=''
  22. this.userInfo={}
  23. }
  1. // 分配对话框关闭时我们要执行的函数
  2. setRoleDialogClosed(){
  3. this.selectenRoleId=''
  4. this.userInfo={}
  5. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/903317
推荐阅读
相关标签
  

闽ICP备14008679号