登录页面

不同的路径,在显示不同的东西。不同的router路径在router/index.js配置,配置路径还有对应的页面(vue)

const routes = [{path: '/',name: 'Login',component: Login}
]

服务端登录接口制作

总体步骤:

1、逆向工程,创建model包和mapper包

2、Hr实体类,实现UserDetails接口,并重写七个方法

3、创建HrService,根据用户名,通过HrMapper获取hr对象。并返回该对象

4、创建SecurityConfig配置类,继承WebSecurityConfiguerAdapter类;

配置密码加密;

重写configure(AuthenticationManagerBuilder auth)方法,并将hrService传入

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(hrService);
}

6、写一个helloController来测试一下

7、测试成功,说明,Spring Security已经和数据库连接上了

8、重写configure(HttpSecurity http)方法,

写登录成功时的回调,登录失败时的回调,登出的回调

protected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().anyRequest().authenticated() // 剩余所有请求都需要登录才能访问.and().formLogin() // 表单登录.usernameParameter("username") // 携带参数 username的名称.passwordParameter("password") // 携带参数 passowrd的名称.loginProcessingUrl("/doLogin") // 如果使用PostMan,通过访问这个路径来登录.loginPage("/login").successHandler(new AuthenticationSuccessHandler() { // 登录成功后的操作@Overridepublic void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();// authentication 表示登录成功的用户信息Hr hr = (Hr) authentication.getPrincipal();// 返回RespBean对象RespBean ok = RespBean.ok("登录成功", hr);// 输出成字符串String s = new ObjectMapper().writeValueAsString(ok);out.write(s);out.flush();out.close();}}).failureHandler(new AuthenticationFailureHandler() { // 登录失败的操作@Overridepublic void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,AuthenticationException exception) throws IOException, ServletException {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();RespBean respBean = RespBean.error("登录失败");if(exception instanceof LockedException){respBean.setMsg("账户被锁定,请联系管理员");}else if(exception instanceof CredentialsExpiredException){respBean.setMsg("密码过期");}else if(exception instanceof AccountExpiredException){respBean.setMsg("账户过期,请联系管理员");}else if(exception instanceof DisabledException){respBean.setMsg("账户被禁用,请联系管理员");}else if(exception instanceof BadCredentialsException){respBean.setMsg("用户名或者密码错误,请联系管理员");}out.write(new ObjectMapper().writeValueAsString(respBean));out.flush();out.close();}}).permitAll() //.and().logout().logoutSuccessHandler(new LogoutSuccessHandler() { // 登出的操作@Overridepublic void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {}}).permitAll().and().csrf().disable();}

9、使用PostMan访问http://localhost:8080/doLogin测试一下,


1、在hr类实现UserDetails接口,实现方法

2、创建HrService,并实现UserDetailsService接口,实现loadUserByUsername方法

2.1、查询hr对象,使用MyBatis查询。如果等于null,抛出异常UsernameNotFoundException

2.2、查询

3、配置security配置类,继承WebSecurityConfiguerAdapter

3.1、注入hrService

@Autowired
HrService hrService;

3.2、配置Bean,密码加密的bean

@Bean
PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}

3.3、实现configure方法

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(hrService);
}

3.4、然后测试下

写一个helloController,get方式请求,返回一个hello就好

如果登录成功,说明已经介入到数据库了

3.5、因为是前后端分离,所以需要配置使用JSON的方式登录

登陆成功:返回一个登录成功的JSON

登录失败:返回一个登录失败的JSON

定义一个统一的返回Bean:RespBean,看源码里边有

实现configure(HttpSecurity http)接口:

.permitAll() 表示登录相关的页面/接口不要被拦截。

登录成功的回调:.successHandler

authentication:登录成功的用户信息

登录成功返回的是一个RespBean对象

.successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();Hr hr = (Hr) authentication.getPrincipal();RespBean ok = RespBean.ok("登录成功", hr);String s = new ObjectMapper().writeValueAsString(ok);out.write(s);out.flush();out.close();}})

登录成功后,不显示密码

hr.setPassword(null)

登录失败的回调:.failureHandler

.failureHandler(new AuthenticationFailureHandler() {@Overridepublic void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {resp.setContentType("application/json;charset=utf-8");PrintWriter out = resp.getWriter();RespBean error = RespBean.error("登录失败");if (e instanceof LockedException) {error.setMsg("账户被锁定,请联系管理员");} else if (e instanceof CredentialsExpiredException) {error.setMsg("密码过期,请联系管理员");} else if (e instanceof AccountExpiredException) {error.setMsg("账户过期,请联系管理员");}else if (e instanceof DisabledException) {error.setMsg("账户被禁用,请联系管理员");}else if(e instanceof BadCredentialsException){error.setMsg("用户名或者密码输入错误,请联系管理员");}String s = new ObjectMapper().writeValueAsString(error);out.write(s);out.flush();out.close();}})

定义一个LoginController来处理登录页的问题

@RestController
public class LoginController {@GetMapping("/login")public RespBean login(){return RespBean.error("尚未登录,请登录");}
}

登出的回调:.logoutSuccessHandler

authentication:表示需要注销的用户

直接返回一个RespBean对象

前后端接口对接

安装axios,npm install axios

1、封装网络请求,创建utils包,再创建api.js。

  • 在里边写一个响应拦截器。success.status这是http的响应码,success.data.status这是我们返回的JSON的响应码
import axios from 'axios'
import {Message} from 'element-ui'/*响应的拦截器*/
axios.interceptors.response.use(success=>{if(success.status && success.status ==200 && success.data.status==500){Message.error({message:success.data.msg})return;}return success.data;},error => {if(error.response.status == 504 ||error.response.status == 404 ){Message.error({message:'没找到服务器o(╯□╰)o'})}else if(error.response.status == 403 ){Message.error({message: '权限不足,请联系管理员'})}else if(error.response.status == 401 ){Message.error({message: '没有登录,请登录'})}else{if(error.response.data.msg){Message.error({message:error.response.data.nsg})}else{Message.error({message: '未知错误!'})}}return;
})
  • 再写一个POST请求的封装,(新增 封装多个请求
let base = '';// 传递key value的请求
export const postKeyValueRequest=(url,params)=>{return axios({method:'post',url:`${base}${url}`,data:params,transformRequest:[function(data){let ret = '';for(let i in data){ret +=encodeURIComponent(i) +'=' + encodeURIComponent(data[i])+'&'}console.log(ret);return ret;}],headers:{'Content-Type':'application/x-www-form-urlencoded'}});
}// 传递JSON的请求
export const postRequest=(url,params)=>{return axios({method:'post',url:`${base}${url}`,data:params})
}export const putRequest=(url,params)=>{return axios({method:'put',url:`${base}${url}`,data:params})
}export const getRequest=(url,params)=>{return axios({method:'get',url:`${base}${url}`,data:params})
}export const deleteRequest=(url,params)=>{return axios({method:'delete',url:`${base}${url}`,data:params})
}

将封装的请求,做成vue插件

在main.js把封装的请求都导入进来
import {postRequest} from "./utils/api";
import {postKeyValueRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";
import {getRequest} from "./utils/api";Vue.prototype.postRequest = postRequest;
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;使用:
this.封装的请求.
如:this.postRequest()
  • 使用封装的请求

    • 1、先导入:import {postKeyValueRequest} from "../utils/api"
    • 2、使用:
    • 另外一种使用方法:this.postRequest()前提是已经做成了一个vue插件
postKeyValueRequest('/doLogin', this.loginForm).then(resp=>{if(resp) {alert(JSON.stringify(resp))}
})

​ 通过if(resp)判断请求成功还是失败,失败不用管,因为在拦截器已经处理过了

  • 跨域问题处理:配置 nodejs 请求转发代理vue.config.js
let proxyObj = {};
proxyObj['/']={ws:false,target:'http://localhost:8081', // 把拦截的请求转发到8081端口区changeOrigin: true,pathRewrite:{'^/':''}
} module.exports={devServer:{host:'localhost',port:8080,proxy:proxyObj}
}

2、保存请求回来的数据到sessionStorage

3、保存后再进行页面跳转,跳转到Home.vue页面

3.1、创建Home.vue页面

3.2、在Router.js中引入,并设置

3.3、跳转this.$router.replace('/home')

postKeyValueRequest('/doLogin', this.loginForm).then(resp=>{if(resp) {window.sessionStorage.setItem("user", JSON.stringify(resp.obj));this.$router.replace('/home')}
})

Home页面制作

sessionStorage读取用户信息

注销登录要把sessionStorage的信息清空

api.js加上一个登录成功的提示

if(success.data.msg) {Message.success({message:success.data.msg})
}
左边导航菜单制作

1、在右边显示窗口加一个

2、在router包下index.js添加

{path: '/home',name: '导航一',component: Home,children:[{path: '/test1', // 要跳转的页面,这里为vuename: '选项1',component: Test1}, {path: '/test2',name: '选项2',component: Test2}]}

3、统一Home.vue和index.js的选项页面

渲染index.js的routes到Home.vue中,添加hidden给不需要渲染的routes,用来区分。

<el-menu  router><el-submenu index="1" v-for="(item,index) in this.$router.options.routes" v-if="!item.hidden" :key="index"><template slot="title"><i class="el-icon-location"></i><span>{{item.name}}</span></template><el-menu-item :index="child.path" v-for="(child,indexj) in item.children " :key="indexj">{{child.name}}</el-menu-item></el-submenu></el-menu>

4、通过数据库动态修改左边导航菜单

  • menu表里存有导航菜单,分有一级菜单,二级菜单。enabled表示菜单是否启用,requireAuth表示需要登录才能访问。

  • 根据用户id获取角色id,根据角色id获取menu id,再通过menu id获取用户可以操作的菜单。

目的:通过服务端根据用户id返回一个menu菜单。

1、修改menu实体类,改成驼峰法(看个人)

2、将额外的字段放到一个meta类里边,

这里将keepAlive,requireAuth定义成meta类的成员变量

在menu实体类增加一个meta成员变量,

在menu实体类增加一个children成员变量,List

类型,

在MenuMapper的resultMap修改:

<association property="meta" javaType="org.javaboy.vhr.model.Meta"><result column="keepAlive" property="keepAlive" jdbcType="BIT" /><result column="requireAuth" property="requireAuth" jdbcType="BIT" />
</association>

3、写一个controller查询需要的数据,系统配置的controller(SystemConfigController),创建MenuService。

通过系统保存的用户对象的id来查询,(Hr)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId()

mapper进行查询:

sql语句:

select distinct m1.*,m2.id as id2,m2.component as component2,
m2.enabled as enabled2,
m2.iconCls as iconCls2,m2.keepAlive as keepAlive2,m2.name as name2,
m2.parentId as parentId2,m2.requireAuth as requireAuth2,m2.path as path2
fromhr_role hrr,menu_role mr,menu m2,menu m1
wherehrr.hrid=10
and hrr.rid=mr.rid
and m2.id = mr.mid
and m1.id = m2.parentId
and m1.enabled=true// 目的:查询该用户所拥有的菜单信息
// 步骤:
1、查询用户为10的角色id
2、根据角色id查询该角色拥有的菜单id
3、根据菜单id查询具体的菜单信息
4、根据菜单id链接菜单的父类菜单信息
5、给子菜单的字段取个别名

重新定义一个resultMap:

<resultMap id="Menus2" type="org.javaboy.vhr.model.Menu" extends="BaseResultMap"><collection property="children" ofType="org.javaboy.vhr.model.Menu"><id column="id2" property="id" jdbcType="INTEGER" /><result column="url2" property="url" jdbcType="VARCHAR" /><result column="path2" property="path" jdbcType="VARCHAR" /><result column="component2" property="component" jdbcType="VARCHAR" /><result column="name2" property="name" jdbcType="VARCHAR" /><result column="iconCls2" property="iconCls" jdbcType="VARCHAR" /><result column="parentId2" property="parentId" jdbcType="INTEGER" /><result column="enabled2" property="enabled" jdbcType="BIT" /><association property="meta" javaType="org.javaboy.vhr.model.Meta"><result column="keepAlive2" property="keepAlive" jdbcType="BIT" /><result column="requireAuth2" property="requireAuth" jdbcType="BIT" /></association></collection></resultMap>

5、菜单项数据加载成功之后,在前端有几个可以存放的地方:

sessionStorage localStorage vuex

需要把加载下来的菜单项数据,需要放在一个公共的地方,让大家都能访问

vuex:状态管理。把数据放在一个公共的地方。可以用于数据共享,互相调用。

vuex的安装:npm install vuex

vuex的用法:

  • 创建一个store包,创建index.js文件
  • main.js引入。 在new Vue里加入
import Vue from 'vue'
import Vuex from 'vuex'Vue.user(Vuex)export default new Vuex.Store({state:{routes:[] // 菜单项都放在这个数据里},mutations:{initRoutes(state,data){ // 这里写怎么放state.routes = data;}},actions:{}
})// 引入
import store from './store'
new Vue({// 加上一个store,
})

6、写一个专门的工具类,功能:1、从服务端请求数据。2、把服务端返回的字符串转成前端的对象;3、把服务端返回的数据放到router.js中去

在utiles包中创建,menus.js

在views包中创建组件的vue:有五个包:emp、per、sal、sta、sys。也拷贝一份到components

emp:EmpBasic.vue(基本资料)、EmpAdv.vue(高级资料)
per:PerEmp(员工资料)、PerEc(员工奖惩)、PerTrain(员工培训)、PerSalary(员工调薪)、PerMv(员工调动)
sal:SalSob(工资账套管理)、SalSobCfg(员工账套设置)、SalTable(工资表管理)、SalMonth(月末处理)、SalSearch(工资表查询)
sta:StaAll(综合信息统计)、StaScore(员工积分统计)、StaPers(人事信息统计)、StaRecord(人事记录统计)
sys:SysBasic(基础信息设置)、SysCfg(系统管理)、SysLog(操作日志管理)、SysHr(操作员管理)、SysData(备份恢复数据库)、SysInit(初始化数据库)
import {getRequest} from "../../utils/api";export const initMenu=(router,store) =>{ // 要存到router,要保存到storeif(store.state.routes.length > 0){// 说明有菜单数据,直接返回return;}getRequest("/system/config/menu").then(data =>{if(data){let fmtRoutes = formatRoutes(data);router.addRoutes(fmtRoutes);store.commit('initRoutes', fmtRoutes); // 调用initRoutes这个方法}})
}// 把一些字符串字段变成前端对象
export const formatRoutes=(routes)=>{let fmRoutes = [];routes.forEach(router =>{let{path,component,name,meta,iconCls,children} = router // router.path, router.name, router.component....if(children && children instanceof Array){// 说明是一级菜单// 递归调用children = formatRoutes(children);}let fmRouter={path:path,name:name,iconCls:iconCls,meta:meta,children:children,component(resolve){ // 主要是处理这个component,componen就是返回的字符串: component:"。。是这个。。"// 动态导入(用的时候再导入这个文件,类似于import...),导入这个vue。if(component.startsWith("Home")){require(['../views/' + component+'.vue'],resolve);}else if (component.startsWith("Emp")){require(['../views/emp/' + component+'.vue'],resolve);}else if(component.startsWith("Per")) {require(['../views/per/' + component+'.vue'],resolve);}else if(component.startsWith("Sal")) {require(['../views/sal/' + component+'.vue'],resolve);}else if(component.startsWith("Sta")) {require(['../views/sta/' + component+'.vue'],resolve);}else if(component.startsWith("Sys")) {require(['../views/sys/' + component+'.vue'],resolve);}}}fmRoutes.push(fmRouter);})return fmRoutes;
}

7、左边导航栏菜单加载

通过路由导航守卫调用initMenu()方法来加载菜单项

路由导航守卫 :路由页面在跳转过程中,可以对页面的跳转进行监听,可以过滤。https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

在main.js加载路由导航守卫

router.beforeEach(to, from, next) => {// from:从哪来// to:去哪里if (to.path == '/') { // 如果要去的页面是登录页,直接放行next(); }else {// 调用initMenu方法加载菜单项 ,记得导入 import {initMenu} from "@/store/menus"initMenu(router, store);next();}
}

替换菜单项,把菜单项换成store里存的数据。使用computed

在Home.vue中添加

computed: {routes(){return this.$store.state.routes;}
}
然后导航页的数据源代码换成:routes,再加一个unique-opened..看别人的那个代码注销登录时,要清空store里的数据
this.$store.commit('initRoutes',[])

安装图标库:看网页

完善首页:看网页

前后端分离权限管理思路探讨

目的:前端做菜单显示只是为了提高用户体验,而不是进行权限控制

权限:后端会提供很多接口,每个接口都需要用户或者角色才能访问。没有权限就访问不了接口。

后端处理的权限才是安全的,前端处理的不是安全的。

权限管理:后端接口设计

1、前端先发起一个http请求,拿到请求后,先分析地址和menu表的url字段哪一个是匹配的。

2、然后去menu_role去查看,哪些角色能访问这个资源。

3、查看当前登录的用户是否具备所需要的角色,如果具备那就没问题,不具备就是越权访问。


1、写一个类,来实现:通过请求地址来获取请求地址所需要的角色

2、写一个类,来判断:当前用户是否具备有上边类返回结果所需要的角色

3、在Security配置类中,设置这两个类

4、给登录用户设置角色信息

接口设计

1、定义一个Fileter实现FilterInvocationSecurityMetadataSource接口(这个类的功能就是启动权限访问)(根据用户传来的请求地址,分析出请求需要的角色)

2、主要写getAttributes这个方法

3、Menu类加一个 List<Role> Roles

MenuService写一个getAllMenusWithRole方法。Mapper写一个sql:

在定义一个ResultMap

SELECT m.*,r.id as rid,r.name as rname,r.nameZh as rnameZh
FROM menu m,menu_role mr, role r
WHERE m.id=mr.mid and mr.rid=r.id
ORDER BY m.id
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException
//
Collection<ConfigAttribute>:当前请求需要的角色

第一步:通过分析当前请求地址,需要哪些角色才能访问

// 最终目的:通过分析当前请求地址,需要哪些角色才能访问
@Overridepublic Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {String requestUrl = ((FilterInvocation) o).getRequestUrl(); // 当前请求的地址List<Menu> menus = menuService.getAllMenusWithRole(); // 获取所有的菜单所需要的角色。需要开缓存,不然每次都会去查数据库 // 遍历menus比较当前请求路径// antPathMatcher需要newfor (Menu menu : menus) {if(antPathMatcher.match(menu.getUrl(),requestUrl)){// 获取当前资源需要的角色List<Role> roles = menu.getRoles();String[] str = new String[roles.size()];for (int i = 0; i < roles.size(); i++) {str[i] = roles.get(i).getName();}return SecurityConfig.createList(str);}}// 表示没有匹配上的,都是登录后访问return SecurityConfig.createList("ROLE_LOGIN");
}

第二步:分析当前用户是否具备有所访问资源的角色

写一个类,实现AccessDecisionManager接口,用来判断当前用户是否具备所访问资源的角色,如果具有就过。

主要重写:

public void decide(Authentication authentication, Object o,Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException
// authentication:登录成功的用户信息。Collection<ConfigAttribute> collection:是上边Fileter的返回值,就是menu需要的角色信息
@Overridepublic void decide(Authentication authentication, Object o,Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {for (ConfigAttribute configAttribute : collection) {String needRole = configAttribute.getAttribute(); // 需要的角色// 判断有没有登录。如果当前用户是匿名实例的话,就说明没登录if("ROLE_LOGIN".equals(needRole)){if(authentication instanceof AnonymousAuthenticationToken){throw new AccessDeniedException("尚未登录,请登录");}else{return;}}// 获取当前登录用户的角色Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();// 如果资源需要的角色有多个,只需要判断当前用户的角色有一个在里边就可以for (GrantedAuthority authority : authorities) {if(authority.getAuthority().equals(needRole)){return;}}}throw new AccessDeniedException("权限不足,请联系管理员");
}

第三步:在SecurityConfig引进这两个类就可以了

.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {@Overridepublic <O extends FilterSecurityInterceptor> O postProcess(O object) {object.setAccessDecisionManager(customUrlDecisionManager);object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);return object;}
})

第四步:给用户给角色

Hr类添加 List roles 属性

这个操作就是给用户赋角色的值

@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());for (Role role : roles) {authorities.add(new SimpleGrantedAuthority(role.getName()));}return authorities;
}

在登录成功后,获取登录用户的角色,并给Hr的roles赋值,在HrService里:

添加:
hr.setRoles(hrMapper.getHrRolesById(hr.getId()));

写sql:根据用户id查询用户用拥有的角色

第四步:测试

如果访问/login不用经过Spring Security

SecurityConfig加上

@Override
public void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/login");
}

完善权限管理:看网页!

基础信息设置页面

前端:复制别人的

vue的组件化开发,由小的页面(组件)拼接成一个完整的页面(组件)

1、在components包下创建sys包创建basic包,创建5个组件:DepMana、PosMana、JobLevelMana、EcMana、PermissMana

2、在SysBasic.vue引入这五个组件

import DepMana from '../../components/sys.......'
自己写吧。。。。
并注册组件
components:{// 'DepMana':DepMana,前边跟后边一样的可以简写成下边这样。DepMana
}在<template></template>内写<DepMana></DepMana>
如:
<el-tab-pane label="部门管理" name="a"><DepMana></DepMana></el-tab-pane>

微人事学习笔记(持续更新)相关推荐

  1. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  2. Admin.NET管理系统(vue3等前后端分离)学习笔记--持续更新

    我的学习笔记 - 9iAdmin.NET 欢迎学习交流 (一)前端笔记 1.1 关于.env的设置 1.2 关于路由模式问题 1.3 关于 vue.config.ts 1.4 关于 打包(pnpm r ...

  3. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  4. 专升本 计算机 公共课学习笔记(持续更新中...)

    计算机公共课学习笔记 第一章 计算机基础知识(30分) 1.计算机概述 计算机(Computer)的起源与发展 计算机(Computer)也称"电脑",是一种具有计算功能.记忆功能 ...

  5. CSS(3)学习笔记——持续更新

    本篇皆是本人长期记录并整理出来的笔记,如有记录得不对的地方,欢迎探讨.记录的很少,将不断学习不断补充. 1 选择器 CSS(3)中提供的选择器手册(w3school):http://www.w3sch ...

  6. typescript-----javascript的超集,typescript学习笔记持续更新中......

    Typescript,冲! Typescript 不是一门全新的语言,Typescript是 JavaScript 的超集,它对 JavaScript进行了一些规范和补充.使代码更加严谨. 一个特别好 ...

  7. 计算机网络:学习笔记(持续更新)

    文章目录 前言 1.1 计算机网络基本概念 什么是计算机网络? 什么是网络协议? 1.2 计算机网络结构 计算机网络结构 网络边缘 接入网络(物理介质) 网络核心(核心网络) Internet结构 1 ...

  8. Docker快速入门学习笔记-持续更新中

    Docker安装 #1.卸载旧的版本 yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker ...

  9. 最全Linux系统学习笔记--持续更新

    1.shell语法 命令 选项 参数 命令:整条shell命令的主体 选项:会影响会微调整个命令的行为 参数:命令作用的对象 1.自动补全 2.快捷键 ctrl+C:终止前台运行的程序 ctrl+D: ...

最新文章

  1. 书评 | 如何让开发中的各种文档变活?《活文档》阅读总结
  2. WinForm经典窗体皮肤[重绘]
  3. Boghe连接FreeSwitch的配置
  4. 使用Spring 3.1和基于Java的配置构建RESTful Web服务,第2部分
  5. c加加中print是什么意思_砖家财经:基金名字后面的A、B和C,分别代表什么意思?...
  6. 海康威视第一季度营收165亿元 净利润22.84亿元
  7. 双系统安装和ros安装踩坑
  8. 苹果iPhone一键解锁破解流程(新机篇)
  9. 小米 note3 android,小米Note3将更新MIUI10:基于安卓8.1 速度提升明显手感更加丝滑...
  10. 编程英语单词1.编程常用英语词汇大全
  11. JeeWx捷微管家系统
  12. 《客户反映访问公司网站服务器很慢或打不开》排错思想
  13. 【Python3之面向对象的程序设计】
  14. ubuntu壁纸1080p
  15. LeetCode数据库题目汇总一(附答案)
  16. 阿里云部署Tiny Tiny RSS踩坑笔记
  17. python连接teradata_使用Teradata模块将Python与Teradata连接
  18. 使用simulink模拟六个位移传感器数据,并实现数据的实时采集与处理
  19. 【互联网+“创新创业大赛”】智慧课堂项目计划书
  20. ruoyi 富文本编辑器Quill-取消自动聚焦

热门文章

  1. C语言-蓝桥杯-算法训练 印章
  2. 头号音频对齐插件 VocAlign Project 5 发布
  3. Linux vlan
  4. Mysql快速备份_sql备份
  5. wordpress插件_5个最佳WordPress插件来处理您网站上的字体
  6. ubuntu20.04运行网易云音乐出现无法播放、下载的情况
  7. linux cc攻击
  8. STEM图像模拟软件QSTEM入门教程-测试狗
  9. java中流转pdf_Java中的PDX到PDF转换器
  10. 5款替代微软Visio的开源免费软件