当前位置: 首页 > news >正文

实战10 角色管理

目录

1、角色后端接口

2、角色列表查询

2.1 效果图

2.2页面原型代码

2.3 角色api代码 role.js

2.4 查询角色列表代码

4、 新增和编辑角色

5、删除角色

6、分配权限

6.1 分配权限思路

6.2 分配权限回显接口

6.3 分配权限回显前端实现

6.4分配权限后端接口

6.4.1 RolePermissionDTO​

6.4.2 RoleMapper

6.4.3 RoleMapper.xml

6.4.4 RoleService

6.4.5 RoleServiceImp

 6.4.6 RoleController

6.5分配权限回显前端实现


1、角色后端接口

package com.cizhu.vo.query;import com.cizhu.entity.Permission;
import lombok.Data;@Data
public class RoleQueryVo extends Permission {private Long pageNo = 1L;//当前页码private Long pageSize = 10L;//每页显示数量private Long userId;//用户ID
}
package com.cizhu.mapper;import com.cizhu.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;import java.util.List;/*** <p>*  Mapper 接口* </p>** @author cizhu* @since 2023-12-14*/
public interface RoleMapper extends BaseMapper<Role> {@Select("select count(1) from sys_user_role where role_id = #{roleId}")int getRoleCountByRoleId(Long roleId);@Delete("delete from sys_role_permission where role_id = #{roleId}")void deleteRolePermissionByRoleId(Long roleId);/*** 保存角色权限关系* @param roleId* @param permissionIds* @return*/int saveRolePermission(Long roleId, List<Long> permissionIds);/*** 根据用户ID查询该用户拥有的角色ID* @param userId* @return*/@Select("select role_id from `sys_user_role` where user_id = #{userId}")List<Long> findRoleIdByUserId(Long userId);}
package com.cizhu.service;import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cizhu.entity.Role;
import com.baomidou.mybatisplus.extension.service.IService;
import com.cizhu.vo.query.RoleQueryVo;import java.util.List;/*** <p>*  服务类* </p>** @author cizhu* @since 2023-12-14*/
public interface IRoleService extends IService<Role> {/*** 根据用户查询角色列表* @param page* @param roleQueryVo* @return*/IPage<Role> findRoleListByUserId(IPage<Role> page, RoleQueryVo roleQueryVo);/**** 检查该角色是否已被使用* @param id:* @return boolean*/boolean hasRoleCount(Long id);/**** 删除角色* @param id:* @return boolean*/boolean deleteRoleById(Long id);/*** 保存角色权限关系* @param roleId* @param permissionIds* @return*/boolean saveRolePermission(Long roleId, List<Long> permissionIds);/*** 根据用户ID查询该用户拥有的角色ID* @param userId* @return*/List<Long> findRoleIdByUserId(Long userId);
}
package com.cizhu.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cizhu.entity.Role;
import com.cizhu.entity.User;
import com.cizhu.mapper.RoleMapper;
import com.cizhu.mapper.UserMapper;
import com.cizhu.service.IPermissionService;
import com.cizhu.service.IRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cizhu.utils.Result;
import com.cizhu.vo.query.RoleQueryVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;/*** <p>*  服务实现类* </p>** @author cizhu* @since 2023-12-14*/
@Service
@Transactional
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {@Resourceprivate UserMapper userMapper;/*** 根据用户查询角色列表** @param page* @param roleQueryVo* @return*/@Overridepublic IPage<Role> findRoleListByUserId(IPage<Role> page, RoleQueryVo roleQueryVo) {//创建条件构造器QueryWrapper<Role> queryWrapper = new QueryWrapper<Role>();//角色名称queryWrapper.like(!ObjectUtils.isEmpty(roleQueryVo.getRoleName()),"role_name",roleQueryVo.getRoleName());//排序queryWrapper.orderByAsc("id");//根据用户ID查询用户信息User user = userMapper.selectById(roleQueryVo.getUserId());//如果用户不为空、且不是管理员,则只能查询自己创建的角色if(user!=null && !ObjectUtils.isEmpty(user.getIsAdmin()) && user.getIsAdmin() !=1){queryWrapper.eq("create_user",roleQueryVo.getUserId());}return baseMapper.selectPage(page,queryWrapper);}/**** 检查该角色是否已被使用* @param id :* @return boolean*/@Overridepublic boolean hasRoleCount(Long id) {return baseMapper.getRoleCountByRoleId(id) > 0 ;}/**** 删除角色* @param id :* @return boolean*/@Overridepublic boolean deleteRoleById(Long id) {// 删除角色权限关系baseMapper.deleteRolePermissionByRoleId(id);// 删除角色return baseMapper.deleteById(id) > 0 ;}/*** 保存角色权限关系** @param roleId* @param permissionIds* @return*/@Overridepublic boolean saveRolePermission(Long roleId, List<Long> permissionIds) {//删除该角色对应的权限信息baseMapper.deleteRolePermissionByRoleId(roleId);//保存角色权限return baseMapper.saveRolePermission(roleId,permissionIds)>0;}/*** 根据用户ID查询该用户拥有的角色ID** @param userId* @return*/@Overridepublic List<Long> findRoleIdByUserId(Long userId) {return baseMapper.findRoleIdByUserId(userId);}
}
package com.cizhu.controller;import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cizhu.dto.RolePermissionDTO;
import com.cizhu.entity.Role;
import com.cizhu.service.IPermissionService;
import com.cizhu.service.IRoleService;
import com.cizhu.utils.Result;
import com.cizhu.vo.query.RolePermissionVo;
import com.cizhu.vo.query.RoleQueryVo;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;/*** <p>*  前端控制器* </p>** @author cizhu* @since 2023-12-14*/
@RestController
@RequestMapping("/api/role")
public class RoleController {@Resourceprivate IRoleService roleService;@Resourceprivate IPermissionService permissionService;/*** 分页查询角色列表* @param roleQueryVo* @return*/@GetMapping("/list")public Result list(RoleQueryVo roleQueryVo){//创建分页对象IPage<Role> page = new Page<Role>(roleQueryVo.getPageNo(),roleQueryVo.getPageSize());//调用分页查询方法roleService.findRoleListByUserId(page,roleQueryVo);//返回数据return Result.ok(page);}/*** 添加角色* @param role* @return*/@PostMapping("/add")public Result add(@RequestBody Role role){if(roleService.save(role)){return Result.ok().message("角色添加成功");}return Result.error().message("角色添加失败");}/*** 修改角色* @param role* @return*/@PutMapping("/update")public Result update(@RequestBody Role role){if(roleService.updateById(role)){return Result.ok().message("角色修改成功");}return Result.error().message("角色修改失败");}/*** 删除角色* @param id* @return*/@DeleteMapping("/delete/{id}")public Result delete(@PathVariable Long id){if(roleService.deleteRoleById(id)){return Result.ok().message("角色删除成功");}return Result.error().message("角色删除失败");}/*** 检查该角色是否已被使用* @param id* @return*/@GetMapping("/check/{id}")public Result check(@PathVariable Long id){//检查该角色是否已被使用if(roleService.hasRoleCount(id)){return Result.exist().message("该角色已分配给其他用户使用,无法删除");}return Result.ok();}/*** 分配权限-查询权限树数据* @param userId* @param roleId* @return*/@GetMapping("/getAssignPermissionTree")public Result getAssignPermissionTree(Long userId, Long roleId) {//调用查询权限树数据的方法RolePermissionVo permissionTree = permissionService.findPermissionTree(userId, roleId);//返回数据return Result.ok(permissionTree);}/*** 分配权限-保存权限数据** @param rolePermissionDTO* @return*/@PostMapping("/saveRoleAssign")public Result saveRoleAssign(@RequestBody RolePermissionDTO rolePermissionDTO) {if (roleService.saveRolePermission(rolePermissionDTO.getRoleId(),rolePermissionDTO.getList())) {return Result.ok().message("权限分配成功");} else {return Result.error().message("权限分配失败");}}}

2、角色列表查询

2.1 效果图

2.2页面原型代码

<template><el-main><!-- 查询条件 --><el-form :model="searchModel" ref="searchForm" label-width="80px" :inline="true" size="small"><el-form-item><el-input v-model="searchModel.roleName" placeholder="请输入角色名称"/></el-form-item><el-form-item><el-button type="primary" icon="el-icon-search" @click="search(pageNo,pageSize)">查询</el-button><el-button icon="el-icon-refresh-right" @click="resetValue()">重置</el-button><el-button type="success" icon="el-icon-plus" @click="openAddWindow()">新增</el-button></el-form-item></el-form><!-- 数据表格 --><el-table :data="roleList" :height="tableHeight" border stripe style="width: 100%; margin-bottom: 10px"><el-table-column prop="roleCode" label="角色编码" width="160" align="center"></el-table-column><el-table-column prop="roleName" label="角色名称"></el-table-column><el-table-column prop="remark" label="角色备注"></el-table-column><el-table-column label="操作" align="center" width="290"><template slot-scope="scope"><el-button icon="el-icon-edit" type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button><el-button icon="el-icon-delete" type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button><el-button icon="el-icon-setting" type="primary" size="small" @click="assignRole(scope.row)">分配权限</el-button></template></el-table-column></el-table><!-- 分页工具栏 --><el-paginationbackground@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="pageNo":page-sizes="[10, 20, 30, 40, 50]":page-size="10"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination><!-- 添加和修改角色窗口 --><system-dialog:title="roleDialog.title":visible="roleDialog.visible":width="roleDialog.width":height="roleDialog.height"@onClose="onClose"@onConfirm="onConfirm"><div slot="content"><el-form :model="role" ref="roleForm" :rules="rules" label-width="80px" :inline="false" size="small"><el-form-item label="角色编码" prop="roleCode"><el-input v-model="role.roleCode"></el-input></el-form-item><el-form-item label="角色名称" prop="roleName"><el-input v-model="role.roleName"></el-input></el-form-item><el-form-item label="角色描述"><el-input type="textarea" v-model="role.remark" :rows="5"></el-input></el-form-item></el-form></div></system-dialog><!-- 分配权限树窗口 --><system-dialog:title="assignDialog.title":visible="assignDialog.visible":width="assignDialog.width":height="assignDialog.height"@onClose="onAssignClose"@onConfirm="onAssignConfirm"><div slot="content"><el-tree ref="assignTree" :data="assignTreeData" node-key="id":props="defaultProps" empty-text="暂无数据" :show-checkbox="true":highlight-current="true" default-expand-all></el-tree></div></system-dialog></el-main>
</template>

2.3 角色api代码 role.js

import http from '@/utils/request'export function getRoutes() {// return request({//   url: '/vue-element-admin/routes',//   method: 'get'// })
}/**
* 查询角色列表
* @returns
*/
export async function getRoles(params) {return await http.get("/api/role/list", params)
}/*** 新增角色* @param {*} data * @returns */
export async function addRole(data) {return await http.post("/api/role/add", data)
}/*** 修改角色* @param {*} data * @returns */
export async function updateRole(data) {return await http.put("/api/role/update", data)
}/**
* 查询角色是否已被使用
* @returns
*/
export async function checkRole(params) {return await http.getRestApi("/api/role/check", params)
}/*** 删除角色* @param {*} id */
export async function deleteRole(params) {return await http.delete("/api/role/delete", params)
}/*** 查询分配权限树列表
* @returns*/
export async function getAssignTree(params) {return await http.get("/api/role/getAssignPermissionTree", params)
}/**
* 分配权限
* @returns
*/
export async function assignSave(params) {return await http.post("/api/role/saveRoleAssign", params)
}

2.4 查询角色列表代码

<script>
//导入role.js脚本
import {getRoles, addRole, updateRole, deleteRole, checkRole, getAssignTree, assignSave} from '@/api/role';
// 导入对话框组件
import SystemDialog from '@/components/system/SystemDialog.vue'
//导入末级节点脚本
import leafUtils from '@/utils/leaf'export default {name: 'roleList',//注册组件components:{ SystemDialog },data() {return {//查询条件searchModel: {roleName: '',pageNo:1,pageSize:10,userId:this.$store.getters.userId //用户ID},roleList: [], //数据列表tableHeight: 0, //表格高度pageNo: 1, //当前页码pageSize: 10, //每页显示数量total: 0, //总数量rules: {roleCode: [{ required: true, trigger: 'blur', message: '请输入角色编码' }],roleName: [{ required: true, trigger: 'blur', message: '请输入角色名称' }]},//添加和修改角色窗口属性roleDialog: {title: '',visible: false,height: 230,width: 500},//角色对象role: {id:"",roleCode:"",roleName:"",remark:"",createUser:this.$store.getters.userId},//分配权限窗口属性assignDialog: {title: '',visible: false,height: 450,width: 300},roleId: '', //角色IDassignTreeData: [], //树节点数据//树节点属性defaultProps: {children: 'children',label: 'label'}}},created() {//调用查询角色列表的方法this.search();},mounted() {this.$nextTick(() => {this.tableHeight = window.innerHeight - 220})},methods: {/*** 查询*/async search(pageNo=1,pageSize=10) {this.searchModel.pageNo = pageNothis.searchModel.pageSize = pageSizelet res = await getRoles(this.searchModel)if (res.success){//console.log(res)this.roleList = res.data.recordsthis.total = res.data.total}},/*** 每页显示数量发生变化*/handleSizeChange(size){this.pageSize = size// 查询数据this.search(this.pageNo, size)},/*** 显示第几页*/handleCurrentChange(page){this.pageNo = page// 查询数据this.search(page, this.pageSize)},/*** 重置查询条件*/resetValue(){//重置查询条件this.searchModel.roleName = ""this.search()},

获取当前用户ID store/user.js

import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken, setTokenTime } from '@/utils/auth'
import router, { resetRouter } from '@/router'const state = {token: getToken(),  // 获取token信息name: '',avatar: '',introduction: '',roles: []
}const mutations = {SET_TOKEN: (state, token) => {state.token = token},SET_INTRODUCTION: (state, introduction) => {state.introduction = introduction},SET_NAME: (state, name) => {state.name = name},SET_AVATAR: (state, avatar) => {state.avatar = avatar},SET_ROLES: (state, roles) => {state.roles = roles},SET_USERUID: (state, userId) => {state.userId = userId},
}const actions = {// user loginlogin({ commit }, userInfo) {const { username, password } = userInforeturn new Promise((resolve, reject) => {login({ username: username.trim(), password: password }).then(response => {const { token, expireTime } = responsecommit('SET_TOKEN', token)setToken(token)// 设置token过期时间setTokenTime(expireTime)resolve()}).catch(error => {reject(error)})})},// get user infogetInfo({ commit, state }) {return new Promise((resolve, reject) => {getInfo(state.token).then(response => {const { data } = responseif (!data) {reject('Verification failed, please Login again.')}// 从后端反馈的data数据中解构出用户相关信息const { roles, name, avatar, introduction, id } = data// roles must be a non-empty arrayif (!roles || roles.length <= 0) {reject('getInfo: roles must be a non-null array!')}commit('SET_ROLES', roles)commit('SET_NAME', name)commit('SET_AVATAR', avatar)commit('SET_INTRODUCTION', introduction)commit('SET_USERUID', id)//将权限字段保存到sessionStorage中sessionStorage.setItem("codeList",JSON.stringify(roles));resolve(data)}).catch(error => {reject(error)})})},// user logoutlogout({ commit, state, dispatch }) {return new Promise((resolve, reject) => {logout(state.token).then(() => {commit('SET_TOKEN', '')commit('SET_ROLES', [])removeToken()resetRouter()// reset visited views and cached views// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485dispatch('tagsView/delAllViews', null, { root: true })resolve()}).catch(error => {reject(error)})})},// remove tokenresetToken({ commit }) {return new Promise(resolve => {commit('SET_TOKEN', '')commit('SET_ROLES', [])removeToken()resolve()})},// dynamically modify permissionsasync changeRoles({ commit, dispatch }, role) {const token = role + '-token'commit('SET_TOKEN', token)setToken(token)const { roles } = await dispatch('getInfo')resetRouter()// generate accessible routes map based on rolesconst accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })// dynamically add accessible routesrouter.addRoutes(accessRoutes)// reset visited views and cached viewsdispatch('tagsView/delAllViews', null, { root: true })}
}export default {namespaced: true,state,mutations,actions
}

4、 新增和编辑角色

   /*** 打开添加窗口*/openAddWindow() {//清空表单数据this.$resetForm("roleForm",this.role);this.roleDialog.title = '新增角色'//设置窗口标题this.roleDialog.visible = true//显示窗口},/*** 窗口取消事件*/onClose() {this.roleDialog.visible = false},/*** 窗口确认事件*/onConfirm() {//表单验证this.$refs.roleForm.validate(async (valid)=>{// 验证通过if (valid) {let res = null// 判断是新增还是修改操作(role.id是否为空)if (this.role.id === ""){// 发送新增角色请求res = await addRole(this.role)}else{// 发送修改角色请求res = await updateRole(this.role)}if (res.success){// 提示成功this.$message.success(res.message)// 刷新数据//this.search(this.pageNo, this.pageSize)window.location.reload();//关闭窗口this.roleDialog.visible = false;}else{this.$message.error(res.message)}}});},/*** 编辑角色*/handleEdit(row){//数据回显this.$objCopy(row, this.role); //将当前编辑的数据复制到role对象中//设置窗口标题this.roleDialog.title = "编辑角色";//显示编辑角色窗口this.roleDialog.visible = true;}

 5、删除角色

 /*** 删除角色*/async handleDelete(row){//查询角色是否已被使用let result = await checkRole({ id: row.id })//判断是否可以删除if (!result.success) {//提示不能删除this.$message.warning(result.message);} else {//确认是否删除let confirm = await this.$myconfirm("确定要删除该数据吗?")if (confirm) {//发送删除请求let res = await deleteRole({ id: row.id })//判断是否成功if (res.success) {//成功提示this.$message.success(res.message)//刷新this.search(this.pageNo, this.pageSize)} else {//失败提示this.$message.error(res.message)}}}},

6、分配权限

6.1 分配权限思路

6.2 分配权限回显接口

 PermissionService

    /*** 查询分配权限树列表* @param userId* @param roleId* @return*/RolePermissionVo findPermissionTree(Long userId, Long roleId);

PermissionServiceImp

   /*** 查询分配权限树列表** @param userId* @param roleId* @return*/@Overridepublic RolePermissionVo findPermissionTree(Long userId, Long roleId) {// 1.查询当前用户信息User user = userService.getById(userId);List<Permission> list = null;//2.判断当前用户角色,如果是管理员,则查询所有权限;如果不是管理员,则只查询自己所拥有的的权限if (!ObjectUtils.isEmpty(user) && user.getIsAdmin() == 1){//查询所有权限list = baseMapper.selectList(null);}else{//根据用户ID查询list = baseMapper.findPermissionListByUserId(userId);}//3.组装成树数据List<Permission> permissionList = MenuTree.makeMenuTree(list, 0L);//4.查询要分配角色的原有权限List<Permission> rolePermissions = baseMapper.findPermissionListByRoleId(roleId);//5.找出该角色存在的数据List<Long> listIds = new ArrayList<Long>();Optional.ofNullable(list).orElse(new ArrayList<>()).stream().filter(item -> item != null).forEach(item -> {Optional.ofNullable(rolePermissions).orElse(new ArrayList<>()).stream().filter(Objects::nonNull).forEach(obj -> {// 判断两者的权限id是否一致if(item.getId().equals(obj.getId())){listIds.add(obj.getId());return;}});});//创建RolePermissionVo vo = new RolePermissionVo();vo.setPermissionList(permissionList);vo.setCheckList(listIds.toArray());return vo;}

 RoleController

 6.3 分配权限回显前端实现

/*** 分配权限*/async assignRole(row){this.roleId = row.id// 构建查询参数let params = {roleId: row.id,   // 角色iduserId: this.$store.getters.userId  // 用户id}// 发送查询分配权限菜单请求let res = await getAssignTree(params)if (res.success){//获取当前登录用户所拥有的所有菜单权限let {permissionList} = res.data//获取当前被分配角色的已经拥有的菜单权限let { checkList } = res.data//判断当前菜单是否是最后一级let { setLeaf } = leafUtils()//设置权限菜单列表let newPermissionList = setLeaf(permissionList)//设置树节点菜单数据this.assignTreeData = newPermissionList//将回调延迟到下次DOM更新循环之后执行,在修改数据之后立即使用它,然后等待DOM更新。this.$nextTick(() => {//获取树菜单的节点数据let nodes = this.$refs.assignTree.children//设置子节点this.setChild(nodes, checkList)})}//显示窗口this.assignDialog.visible = true//设置窗口标题this.assignDialog.title = `给【${row.roleName}】分配权限`},/*** 设置子节点*/setChild(childNodes, checkList) {//判断是否存在子节点if (childNodes && childNodes.length > 0){//循环所有权限for (let i=0; i< childNodes.length; i++) {//根据data或key获取树组件中的node节点let node = this.$refs.assignTree.getNode(childNodes[i])//判断使用已经拥有对应的角色权限数据if (checkList && checkList.length > 0) {//循环遍历已有的权限集合for (let j = 0; j < checkList.length; j++) {//找到已经存在的菜单权限节点if (checkList[j] == childNodes[i].id) {//如果节点是展开状态,则将树节点选中if (childNodes[i].open) {this.$refs.assignTree.setChecked(node, true)break}}}}//如果存在子节点,则递归选中if (childNodes[i].children) {this.setChild(childNodes[i].children, checkList)}}}},/*** 分配权限窗口取消事件*/onAssignClose() {this.assignDialog.visible = false},

6.4分配权限后端接口

6.4.1 RolePermissionDTO

6.4.2 RoleMapper

6.4.3 RoleMapper.xml

6.4.4 RoleService

    /*** 保存角色权限关系* @param roleId* @param permissionIds* @return*/boolean saveRolePermission(Long roleId, List<Long> permissionIds);

6.4.5 RoleServiceImp

    /*** 保存角色权限关系** @param roleId* @param permissionIds* @return*/@Overridepublic boolean saveRolePermission(Long roleId, List<Long> permissionIds) {//删除该角色对应的权限信息baseMapper.deleteRolePermissionByRoleId(roleId);//保存角色权限return baseMapper.saveRolePermission(roleId,permissionIds)>0;}

 6.4.6 RoleController

/*** 分配权限-保存权限数据** @param rolePermissionDTO* @return*/@PostMapping("/saveRoleAssign")public Result saveRoleAssign(@RequestBody RolePermissionDTO rolePermissionDTO) {if (roleService.saveRolePermission(rolePermissionDTO.getRoleId(),rolePermissionDTO.getList())) {return Result.ok().message("权限分配成功");} else {return Result.error().message("权限分配失败");}}

6.5分配权限回显前端实现

    /*** 分配权限窗口确认事件*/async onAssignConfirm() {//获取选中的节点keylet ids = this.$refs.assignTree.getCheckedKeys()//获取选中节点的父节点idlet pids = this.$refs.assignTree.getHalfCheckedKeys()//组装选中的节点ID数据let listId = ids.concat(pids)//组装参数let params = {roleId: this.roleId,list: listId}//发送请求let res = await assignSave(params)//判断是否成功if (res.success) {//关闭窗口this.assignDialog.visible = false//提示成功this.$message.success(res.message)} else {//提示失败this.$message.error(res.data)}}

相关文章:

  • redis 从0到1完整学习 (八):QuickList 数据结构
  • Android画布Canvas drawPath绘制跟随手指移动的圆,Kotlin
  • Springcloud Alibaba 使用Canal将MySql数据实时同步到Elasticsearch
  • Git三种方法从远程仓库拉取指定分支
  • Leetcode 2971. Find Polygon With the Largest Perimeter
  • C#实现串口通讯
  • Unity Shader 实现X光效果
  • 【Qt-Event-信号和槽】
  • go 语言程序设计第2章--程序结构
  • JS-图片预览
  • 图论 | 网络流的基本概念
  • elasticsearch系列四:集群常规运维
  • WEB 3D技术 three.js 色彩空间讲解
  • Vuex介绍2
  • 基于openGauss5.0.0全密态数据库等值查询小案例
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【EOS】Cleos基础
  • Create React App 使用
  • DataBase in Android
  • Flannel解读
  • HTTP那些事
  • Javascript Math对象和Date对象常用方法详解
  • Java应用性能调优
  • MYSQL 的 IF 函数
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • 大快搜索数据爬虫技术实例安装教学篇
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 在Mac OS X上安装 Ruby运行环境
  • 追踪解析 FutureTask 源码
  • 06-01 点餐小程序前台界面搭建
  • 阿里云ACE认证之理解CDN技术
  • 第二十章:异步和文件I/O.(二十三)
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #Z2294. 打印树的直径
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (译)2019年前端性能优化清单 — 下篇
  • *2 echo、printf、mkdir命令的应用
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net core webapi 大文件上传到wwwroot文件夹
  • .net 反编译_.net反编译的相关问题
  • .net 使用$.ajax实现从前台调用后台方法(包含静态方法和非静态方法调用)
  • .NET轻量级ORM组件Dapper葵花宝典
  • .NET中的Exception处理(C#)
  • @JsonFormat与@DateTimeFormat注解的使用
  • @RunWith注解作用
  • [3D游戏开发实践] Cocos Cyberpunk 源码解读-高中低端机性能适配策略
  • [AIGC] 开源流程引擎哪个好,如何选型?
  • [Android]使用Git将项目提交到GitHub
  • [bzoj1038][ZJOI2008]瞭望塔
  • [C#] 基于 yield 语句的迭代器逻辑懒执行
  • [C/C++]数据结构 栈和队列()
  • [CentOs7]iptables防火墙安装与设置
  • [CSS]浮动
  • [IE 技巧] 显示/隐藏IE 的菜单/工具栏