首页
友链
统计
留言
更多
直播
壁纸
推荐
我的毛线
哔哔点啥
院长科技
Search
1
本站官方群:894703859------|诚邀各位大佬的入驻!
580 阅读
2
pxe 自动化安装系统
570 阅读
3
软件安装
434 阅读
4
新款螺旋帽子编织#夏凉帽#合股线夏凉帽编织
379 阅读
5
10 个Linux Awk文本处理经典案例
372 阅读
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
登录
/
注册
Search
标签搜索
基础
js
Nginx
css
webapi
jQuery
面向对象
command
项目
ansible
用户权限
go
html
文件管理
命令
k8s
shell
pxe
awk
vscode
JustDoIt
累计撰写
114
篇文章
累计收到
4
条评论
首页
栏目
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
页面
友链
统计
留言
直播
壁纸
推荐
我的毛线
哔哔点啥
院长科技
搜索到
114
篇与
的结果
2023-08-24
Nginx中的break和last
两个指令用法相同,但含义不同,需要放到rewrite规则的末尾,用来控制重写后的链接是否继续被nginx配置执行(主要是rewrite、return指令)。 示例1(连续两条rewrite规则):server{ listen 80; server_name test.com; root /tmp/123.com; rewrite /1.html /2.html ; rewrite /2.html /3.html ; } 当我们请求1.html时,最终访问到的是3.html,两条rewrite规则先后执行。 break和last在location {}外部 格式:rewrite xxxxx break; 示例2(增加break):server{ listen 80; server_name test.com; root /tmp/123.com; rewrite /1.html /2.html break; rewrite /2.html /3.html; } 当我们请求1.html时,最终访问到的是2.html 说明break在此示例中,作用是不再执行break以下的rewrite规则。 但,当配置文件中有location时,它还会去执行location{}段的配置(请求要匹配该location)。 示例3(break后面还有location段):server{ listen 80; server_name test.com; root /tmp/123.com; rewrite /1.html /2.html break; rewrite /2.html /3.html; location /2.html { return 403; } } 当请求1.html时,最终会返回403状态码,说明它去匹配了break后面的location{}配置。 以上2个示例中,可以把break替换为last,它们两者起到的效果一模一样。 当break和last在location{}里面 示例4(什么都不加):server{ listen 80; server_name test.com; root /tmp/123.com; location / { rewrite /1.html /2.html; rewrite /2.html /3.html; } location /2.html { rewrite /2.html /a.html; } location /3.html { rewrite /3.html /b.html; } } 当请求/1.html,最终将会访问/b.html,连续执行location /下的两次rewrite,跳转到了/3.html,然后又匹配location /3.html 示例5(增加break):server{ listen 80; server_name test.com; root /tmp/123.com; location / { rewrite /1.html /2.html break; rewrite /2.html /3.html; } location /2.html { rewrite /2.html /a.html; } location /3.html { rewrite /3.html /b.html; } } 当请求/1.html,最终会访问/2.html 在location{}内部,遇到break,本location{}内以及后面的所有location{}内的所有指令都不再执行。 示例6(增加last):server{ listen 80; server_name test.com; root /tmp/123.com; location / { rewrite /1.html /2.html last; rewrite /2.html /3.html; } location /2.html { rewrite /2.html /a.html; } location /3.html { rewrite /3.html /b.html; } } 当请求/1.html,最终会访问/a.html 在location{}内部,遇到last,本location{}内后续指令不再执行,而重写后的url再次从头开始,从头到尾匹配一遍规则。 结论当rewrite规则在location{}外,break和last作用一样,遇到break或last后,其后续的rewrite/return语句不再执行。但后续有location{}的话,还会近一步执行location{}里面的语句,当然前提是请求必须要匹配该location。 当rewrite规则在location{}里,遇到break后,本location{}与其他location{}的所有rewrite/return规则都不再执行。 当rewrite规则在location{}里,遇到last后,本location{}里后续rewrite/return规则不执行,但重写后的url再次从头开始执行所有规则,哪个匹配执行哪个。
2023年08月24日
51 阅读
0 评论
2 点赞
2023-07-20
更换ssl证书报错
1.背景又到了一年一度的ssl证书替换的时间了, 这次替换证书并没有和往常一样预期的那么丝滑, 这不刚替换了非生产环境的证书业务就开始反馈报错了, 自此期间也度娘和谷歌了一番,有说改java代码的、有说证书问题的、有说服务器问题的 各抒己见 下面贴下报错信息, 给有和我一样的童鞋做个借鉴 2. 报错信息org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://labelservice-dev.wangmanyuan.com/rest/label/condition": sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:674) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:621) at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:415) at com.tal.file.business.client.LabelRestClient.condition(LabelRestClient.java:51) at com.tal.file.core.common.utils.CoursewareUtil.copyLabel(CoursewareUtil.java:2778) at com.tal.file.business.service.impl.CoursewareServiceImpl.packageOnCopy(CoursewareServiceImpl.java:1122) at com.tal.file.business.service.impl.CoursewareServiceImpl.copyCommon(CoursewareServiceImpl.java:329) at com.tal.file.business.service.impl.CoursewareServiceImpl.copyAsync(CoursewareServiceImpl.java:258) at com.tal.file.business.service.impl.CoursewareServiceImpl$$FastClassBySpringCGLIB$$b182efe0.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 3.思考证书报错的原因清晰可见他们的牌子和加密算法是一样的 可是切换过去就报错 3.1证书解析过程3.2 HTTPS在传输的过程中会涉及到三个密钥:服务器端的公钥和私钥,用来进行非对称加密 客户端生成的随机密钥,用来进行对称加密 一个HTTPS请求实际上包含了两次HTTP传输,可以细分为8步 客户端发起HTTPS请求用户在浏览器里输入一个https网址,然后连接到server的443端口。 服务端的配置采用HTTPS协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥。如果对公钥和私钥不太理解,可以想象成一把钥匙和一个锁头,只是全世界只有你一个人有这把钥匙,你可以把锁头给别人,别人可以用这个锁把重要的东西锁起来,然后发给你,因为只有你一个人有这把钥匙,所以只有你才能看到被这把锁锁起来的东西。 传送证书这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间等等。 客户端解析证书这部分工作是有客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随即值。然后用证书对该随机值进行加密。就像上面说的,把随机值用锁头锁起来,这样除非有钥匙,不然看不到被锁住的内容。 客户端收到服务器端的证书之后,会对证书进行检查,验证其合法性,如果发现发现证书有问题,那么HTTPS传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书的合法性。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS中的第一次HTTP请求结束。 传送加密信息这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。 客户端会发起HTTPS中的第二个HTTP请求,将加密之后的客户端密钥发送给服务器。 服务段解密信息服务端用私钥解密后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密。所谓对称加密就是,将信息和私钥通过某种算法混合在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全。 传输加密后的信息这部分信息是服务段用私钥加密后的信息,可以在客户端被还原 客户端解密信息客户端用之前生成的私钥解密服务段传过来的信息,于是获取了解密后的内容。整个过程第三方即使监听到了数据,也束手无策。 这样HTTPS中的第二个HTTP请求结束,整个HTTPS传输完成。SSL的位置SSL介于应用层和TCP层之间。应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层收到的数据进行加密,并增加自己的SSL头。3.3 根证书每15年更换https://help.aliyun.com/document_detail/179033.htm?spm=0.2020520163.help.dexternal.3cb237119U1VUdhttps://help.aliyun.com/document_detail/610069.html3.4 思考当前运行环境我们现在运行的服务环境go、nodejs、python、c、java 从报错日志来看目前出现问题的是java+jdk1.7的组合下发生的问题 那么就开始从这里下手 目前根据阿里提供的新的根证书进行导入到jdk1.7内, 原则上导入后恢复,从实际现象来看是需要重启服务的才能生效 3.5 具体操作流程3.5.1 上传G2根证书可根据阿里给的文件进行下载,要是不知道的童鞋可以加群和我要下 3.5.2 确认jdk1.7所在服务器目录执行导入命令 echo yes|keytool -importcert -file Digicert-OV-DV-root.cer -alias DigicertWangManYuan20240717 -keystore /usr/local/jdk1.7.0_79/jre/lib/security/cacerts -storepass changeit 3.5.2 导入后测试在导入后发现有的服务重启后生效 有的却不生效 具体排查方式有一个demo文件可以进行测试 3.5.3 demo文件[root@wangmanyuan abc]# cat SSLPoke.java import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import java.io.*; public class SSLPoke { public static void main(String[] args) { if (args.length != 2) { System.out.println("Usage: "+SSLPoke.class.getName()+" <host> <port>"); System.exit(1); } try { SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1])); InputStream in = sslsocket.getInputStream(); OutputStream out = sslsocket.getOutputStream(); out.write(1); while (in.available() > 0) { System.out.print(in.read()); } System.out.println("Successfully connected"); } catch (Exception exception) { exception.printStackTrace(); } } } 3.5.4 编译一下 执行编译 javac SSLPoke.java 编译后生成SSLPoke.class文件 执行命令测试 /usr/local/jdk1.7.0_79/bin/java -Djavax.net.debug=all SSLPoke confi.wangmanyuan.com 443 如下图展示 发现读取的并不是常规的cacerts这个文件而是jssecacerts文件 重新指定文件执行命令后解决 echo yes|keytool -importcert -file Digicert-OV-DV-root.cer -alias DigicertWangManYuan20240717 -keystore /usr/local/jdk1.7.0_79/jre/lib/security/jssecacerts -storepass changeit 3.5.5 查看导入G2根证书是否成功keytool -list -keystore /usr/local/jdk1.7.0_79/jre/lib/security/jssecacerts -storepass changeit|grep -i wangmanyuan 3.5.6开启搬砖模式由于非生产和生产环境数量非常多 那么接下来我就是去做这件事了 毕竟生产环境得重启 真是一个高危操作 好了,就写这么多吧,有遇到和我类似问题的童鞋可以进群交流~~~
2023年07月20日
66 阅读
0 评论
1 点赞
2023-02-17
clickhouse 系统表日志清理
今天出现了生产环境clickhouse 清理业务表数据磁盘并未下降的问题 搜索相关文档后得知system表内会继续各种操作日志 一、查询各个表占用空间信息select concat(database, '.', table) as table, formatReadableSize(sum(bytes)) as size, sum(rows) as rows, max(modification_time) as latest_modification, sum(bytes) as bytes_size, any(engine) as engine, formatReadableSize(sum(primary_key_bytes_in_memory)) as primary_keys_size from system.parts where active group by database, table order by bytes_size desc limit 10; 二、清理system 系统自带的日志表数据2.1 根据指定数据库清理SQLalter table system.query_thread_log delete where current_database ='xxx' ; 2.2 根据指定数据库和时间清理SQLalter table system.query_thread_log delete where current_database ='xxx' and event_date='2023-02-05';
2023年02月17日
64 阅读
0 评论
2 点赞
2022-10-29
商品分类
A.新建分支goods_cate新建分支goods_cate并推送到码云git checkout -b goods_categit push -u origin goods_cateB.创建子级路由创建categories子级路由组件并设置路由规则import Cate from './components/goods/Cate.vue' path: '/home', component: Home, redirect: '/welcome', children: [ { path: "/welcome", component: Welcome }, { path: "/users", component: Users }, { path: "/rights", component: Rights }, { path: "/roles", component: Roles }, { path: "/categories", component: Cate } ] C.添加组件基本布局在Cate.vue组件中添加面包屑导航以及卡片视图中的添加分类按钮<template> <div> <h3>商品分类</h3> <!-- 面包屑导航 --> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>商品管理</el-breadcrumb-item> <el-breadcrumb-item>商品分类</el-breadcrumb-item> </el-breadcrumb> <!-- 卡片视图区域 --> <el-card> <!-- 添加分类按钮区域 --> <el-row> <el-col> <el-button type="primary">添加分类</el-button> </el-col> </el-row> <!-- 分类表格 --> <!-- 分页 --> </el-card> </div> </template> D.请求分类数据请求分类数据并将数据保存在data中<script> export default { data() { return { // 商品分类数据列表 cateList: [], //查询分类数据的条件 queryInfo: { type: 3, pagenum: 1, pagesize: 5 }, //保存总数据条数 total: 0 } }, created() { this.getCateList() }, methods: { async getCateList() { //获取商品分类数据 const { data: res } = await this.$http.get('categories', { params: queryInfo }) if (res.meta.status !== 200) { return this.$message.error('获取商品列表数据失败') } //将数据列表赋值给cateList this.cateList = res.data.result //保存总数据条数 this.total = res.data.total // console.log(res.data); } } } </script> E.使用插件展示数据使用第三方插件vue-table-with-tree-grid展示分类数据1).在vue 控制台中点击依赖->安装依赖->运行依赖->输入vue-table-with-tree-gird->点击安装2).打开main.js,导入vue-table-with-tree-gridimport TreeTable from 'vue-table-with-tree-grid'.....Vue.config.productionTip = false//全局注册组件 Vue.component('tree-table', TreeTable) 3).使用组件展示分类数据 <!-- 分类表格 :data(设置数据源) :columns(设置表格中列配置信息) :selection-type(是否有复选框) :expand-type(是否展开数据) show-index(是否设置索引列) index-text(设置索引列头) border(是否添加纵向边框) :show-row-hover(是否鼠标悬停高亮) --> <tree-table :data="cateList" :columns="columns" :selection-type="false" :expand-type="false" show-index index-text="#" border :show-row-hover="false"> </tree-table> 在数据中添加columns: columns: [ {label:'分类名称',prop:'cat_name'} ] F.自定义数据列使用vue-table-with-tree-grid定义模板列并添加自定义列//先在columns中添加一个列 columns: [ {label:'分类名称',prop:'cat_name'}, //type:'template'(将该列设置为模板列),template:'isok'(设置该列模板的名称为isok) {label:'是否有效',prop:'',type:'template',template:'isok'}, {label:'排序',prop:'',type:'template',template:'order'}, {label:'操作',prop:'',type:'template',template:'opt'} ] <!-- 是否有效区域, 设置对应的模板列: slot="isok"(与columns中设置的template一致) --> <template slot="isok" slot-scope="scope"> <i class="el-icon-success" v-if="scope.row.cat_deleted === false" style="color:lightgreen"></i> <i class="el-icon-error" v-else style="color:red"></i> </template> <!-- 排序 --> <template slot="order" slot-scope="scope"> <el-tag size="mini" v-if="scope.row.cat_level===0">一级</el-tag> <el-tag size="mini" type="success" v-else-if="scope.row.cat_level===1">二级</el-tag> <el-tag size="mini" type="warning" v-else>三级</el-tag> </template> <!-- 操作 --> <template slot="opt" slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> </template> G.完成分页功能<!-- 分页 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[3, 5, 10, 15]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> //添加对应的事件函数 methods:{ ....... handleSizeChange(newSize){ //当pagesize发生改变时触发 this.queryInfo.pagesize = newSize; this.getCateList(); }, handleCurrentChange(newPage){ //当pagenum发生改变时触发 this.queryInfo.pagenum = newPage; this.getCateList(); } } H.完成添加分类...... <!-- 添加分类按钮区域 --> <el-row> <el-col> <el-button type="primary" @click="showAddCateDialog">添加分类</el-button> </el-col> </el-row> ...... <!-- 添加分类对话框 --> <el-dialog title="添加分类" :visible.sync="addCateDialogVisible" width="50%" @close="addCateDialogClosed"> <!-- 添加分类表单 --> <el-form :model="addCateForm" :rules="addCateFormRules" ref="addCateFormRuleForm" label-width="100px"> <el-form-item label="分类名称" prop="cat_name"> <el-input v-model="addCateForm.cat_name"></el-input> </el-form-item> <el-form-item label="父级分类" prop="cat_pid"> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="addCateDialogVisible = false">取 消</el-button> <el-button type="primary" @click="addCate">确 定</el-button> </span> </el-dialog> //用来显示或隐藏添加分类对话框 addCateDialogVisible: false, //添加分类的表单数据对象 addCateForm:{ //分类名称 cat_name:'', //添加分类的父级id,0则表示父级为0.添加一级分类 cat_pid:0, //添加分类的等级,0则表示添加一级分类 cat_level:0 }, //添加分类校验规则 addCateFormRules:{ //验证规则 cat_name:[ {required:true , message:'请输入分类名称',trigger:'blur'} ] }, //保存1,2级父级分类的列表 parentCateList:[] ....... showAddCateDialog() { //调用getParentCateList获取分类列表 this.getParentCateList() //显示添加分类对话框 this.addCateDialogVisible = true }, async getParentCateList(){ //获取父级分类数据列表 const { data: res } = await this.$http.get('categories', { params: {type:2} }) if (res.meta.status !== 200) { return this.$message.error('获取商品分类列表数据失败') } this.parentCateList = res.data } 添加级联菜单显示父级分类先导入Cascader组件,并注册然后添加使用级联菜单组件:<el-form-item label="父级分类" prop="cat_pid"> <!-- expandTrigger='hover'(鼠标悬停触发级联) v-model(设置级联菜单绑定数据) :options(指定级联菜单数据源) :props(用来配置数据显示的规则) clearable(提供“X”号完成删除文本功能) change-on-select(是否可以选中任意一级的菜单) --> <el-cascader expandTrigger='hover' v-model="selectedKeys" :options="parentCateList" :props="cascaderProps" @change="parentCateChange" clearable change-on-select></el-cascader> </el-form-item> 添加数据 //配置级联菜单中数据如何展示 cascaderProps:{ value:'cat_id', label:'cat_name', children:'children', expandTrigger:'hover' }, //绑定用户选择的分类值 selectedKeys:[] ..... methods:{ ..... parentCateChange(){ //级联菜单中选择项发生变化时触发 console.log(this.selectedKeys) //如果用户选择了父级分类 if(this.selectedKeys.length > 0){ //则将数组中的最后一项设置为父级分类 this.addCateForm.cat_pid = this.selectedKeys[this.selectedKeys.length - 1] //level也要跟着发生变化 this.addCateForm.cat_level = this.selectedKeys.length return }else{ this.addCateForm.cat_pid = 0 this.addCateForm.cat_level = 0 return } }, addCateDialogClosed(){ //当关闭添加分类对话框时,重置表单 this.$refs.addCateFormRef.resetFields() this.selectedKeys = []; this.addCateForm.cat_pid = 0 this.addCateForm.cat_level = 0 }, addCate() { //点击确定,完成添加分类 console.log(this.addCateForm) this.$refs.addCateFormRef.validate(async valid => { if (!valid) return //发送请求完成添加分类 const { data: res } = await this.$http.post( 'categories', this.addCateForm ) if (res.meta.status !== 201) { return this.$message.error('添加分类失败') } this.$message.success('添加分类成功') this.getCateList() this.addCateDialogVisible = false }) } } I.推送代码制作完添加分类之后,将代码提交到仓库,推送到码云,将goods_cate分支合并到mastergit add .git commit -m '完成商品分类'git pushgit checkout mastergit merge goods_cate2.参数管理只允许给三级分类内容设置参数,参数分为动态参数和静态参数属性A.添加子级组件添加Params.vue子组件,并在router.js中引入该组件并设置路由规则import Params from './components/goods/Params.vue' ...... path: '/home', component: Home, redirect: '/welcome', children: [ { path: "/welcome", component: Welcome }, { path: "/users", component: Users }, { path: "/rights", component: Rights }, { path: "/roles", component: Roles }, { path: "/categories", component: Cate }, { path: "/params", component: Params } ] B.完成组件基本布局完成Params.vue组件的基本布局其中警告提示信息使用了el-alert,在element.js引入该组件并注册<template> <div> <h3>分类参数</h3> <!-- 面包屑导航 --> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>商品管理</el-breadcrumb-item> <el-breadcrumb-item>分类参数</el-breadcrumb-item> </el-breadcrumb> <!-- 卡片视图区域 --> <el-card> <!-- 警告区域 :closable="false"(是否展示“X”号) show-icon(显示图标) --> <el-alert title="注意:只允许为第三级分类设置相关参数" type="warning" :closable="false" show-icon> </el-alert> <!-- 选择商品分类区域 --> <el-row class="cat_opt"> <el-col> <span>选择商品分类:</span> <!-- 选择商品分类的级联选择框 --> </el-col> <el-col></el-col> </el-row> </el-card> </div> </template> C.完成级联选择框完成商品分类级联选择框<!-- 选择商品分类区域 --> <el-row class="cat_opt"> <el-col> <span>选择商品分类:</span> <!-- 选择商品分类的级联选择框 --> <el-cascader expandTrigger='hover' v-model="selectedCateKeys" :options="cateList" :props="cateProps" @change="handleChange" clearable></el-cascader> </el-col> <el-col></el-col> </el-row> ...... <script> export default { data() { return { //分类列表 cateList:[], //用户在级联下拉菜单中选中的分类id selectedCateKeys:[], //配置级联菜单中数据如何展示 cateProps: { value: 'cat_id', label: 'cat_name', children: 'children' } } }, created() { this.getCateList() }, methods: { async getCateList(){ //获取所有的商品分类列表 const { data: res } = await this.$http.get('categories') if (res.meta.status !== 200) { return this.$message.error('获取分类数据失败') } //将数据列表赋值给cateList this.cateList = res.data // //保存总数据条数 // this.total = res.data.total // console.log(res.data); }, handleChange(){ //当用户在级联菜单中选择内容改变时触发 console.log(this.selectedCateKeys); } } } </script> D.展示参数展示动态参数数据以及静态属性数据<!-- tab页签区域 --> <el-tabs v-model="activeName" @tab-click="handleTabClick"> <!-- 添加动态参数的面板 将标签页改为many --> <el-tab-pane label="动态参数" name="many"> <el-button size="mini" type="primary" :disabled="isButtonDisabled">添加参数</el-button> <!-- 动态参数表格 --> <el-table :data="manyTableData" border stripe> <!-- 展开行 --> <el-table-column type="expand"></el-table-column> <!-- 索引列 --> <el-table-column type="index"></el-table-column> <el-table-column label="参数名称" prop="attr_name"></el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> </template> </el-table-column> </el-table> </el-tab-pane> <!-- 添加静态属性的面板 将标签页改为only --> <el-tab-pane label="静态属性" name="only"> <el-button size="mini" type="primary" :disabled="isButtonDisabled">添加属性</el-button> <!-- 静态属性表格 --> <el-table :data="onlyTableData" border stripe> <!-- 展开行 --> <el-table-column type="expand"></el-table-column> <!-- 索引列 --> <el-table-column type="index"></el-table-column> <el-table-column label="属性名称" prop="attr_name"></el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> </template> </el-table-column> </el-table> </el-tab-pane> </el-tabs> <script> export default { data() { return { ...... //tab页签激活显示的页签项 activeName: 'many', //用来保存动态参数数据 manyTableData: [], //用来保存静态属性数据 onlyTableData: [] } methods: { ....... async handleChange() { //当用户在级联菜单中选择内容改变时触发 console.log(this.selectedCateKeys) //发送请求,根据用户选择的三级分类和面板获取参数数据 const { data: res } = await this.$http.get( `categories/${this.cateId}/attributes`, { params: { sel: this.activeName } } ) if (res.meta.status !== 200) { return this.$message.error('获取参数列表数据失败') } console.log(res.data) if (this.activeName === 'many') { //获取的是动态参数 this.manyTableData = res.data } else if (this.activeName === 'only') { //获取的是静态属性 this.onlyTableData = res.data } }, handleTabClick() { console.log(this.activeName) this.handleChange() } }, computed: { //添加计算属性用来获取按钮禁用与否 isButtonDisabled() { return this.selectedCateKeys.length !== 3 }, //获取选中的三级分类id cateId() { if (this.selectedCateKeys.length === 3) { return this.selectedCateKeys[this.selectedCateKeys.length - 1] } return null } } E.添加参数完成添加参数或属性<!-- 添加参数或属性对话框 --> <el-dialog :title="'添加'+titleText" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed"> <!-- 添加表单 --> <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px"> <el-form-item :label="titleText" prop="attr_name"> <el-input v-model="addForm.attr_name"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="addDialogVisible = false">取 消</el-button> <el-button type="primary" @click="addParams">确 定</el-button> </span> </el-dialog> export default { data() { return { ....... //控制添加参数.属性对话框的显示或隐藏 addDialogVisible: false, //添加参数的表单数据对象 addForm: { attr_name: '' }, //添加表单验证规则 addFormRules: { attr_name: [{ required: true, message: '请输入名称', trigger: 'blur' }] } } },methods: { ....... addParams() { //当用户点击对话框中的确定时,校验表单 this.$refs.addFormRef.validate(async valid => { //校验不通过,return if (!valid) return //校验通过,发送请求完成添加参数或者属性 const { data: res } = this.$http.post(`categories/${this.cateId}/attributes`, { attr_name: this.addForm.attr_name, attr_sel: this.activeName, attr_vals: "a,b,c" } ) console.log(res) if (res.meta.status !== 201) { return this.$message.error('添加' + this.titleText + '数据失败') } this.$message.success('添加' + this.titleText + '数据成功') this.addDialogVisible = false this.getCateList() }) } } F.编辑参数完成编辑参数或属性<!-- 修改参数或属性对话框 --> <el-dialog :title="'修改'+titleText" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"> <!-- 添加表单 --> <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px"> <el-form-item :label="titleText" prop="attr_name"> <el-input v-model="editForm.attr_name"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="editDialogVisible = false">取 消</el-button> <el-button type="primary" @click="editParams">确 定</el-button> </span> </el-dialog> export default { data() { return { ....... //控制修改参数.属性对话框的显示或隐藏 editDialogVisible:false, //修改参数.属性对话框中的表单 editForm:{ attr_name:'' }, //修改表单的验证规则 editFormRules:{ attr_name:[ { required: true, message: '请输入名称', trigger: 'blur' } ] } } },methods: { ....... async showEditDialog(attr_id){ //发起请求获取需要修改的那个参数数据 const {data:res} = await this.$http.get(`categories/${this.cateId}/attributes/${attr_id}`, {params:{ attr_sel:this.activeName }}) if (res.meta.status !== 200) { return this.$message.error('获取参数数据失败') } this.editForm = res.data; //显示修改参数.属性对话框 this.editDialogVisible = true; }, editDialogClosed(){ //当关闭修改参数.属性对话框时 this.$refs.editFormRef.resetFields() }, editParams(){ //验证表单 this.$refs.editFormRef.validate(async valid => { if(!valid) return; //发送请求完成修改 const {data:res} = await this.$http.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`, {attr_name:this.editForm.attr_name,attr_sel:this.activeName}) if (res.meta.status !== 200) { return this.$message.error('获取参数数据失败') } this.$message.success('修改' + this.titleText + '数据成功') this.editDialogVisible = false this.handleChange(); }) } } G.删除参数删除参数或属性给两个删除按钮添加事件 <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button> <el-button size="mini" type="danger" icon="el-icon-delete" @click="removeParams(scope.row.attr_id)">删除</el-button> 添加对应的事件处理函数 async removeParams(attr_id){ //根据id删除对应的参数或属性 //弹窗提示用户是否要删除 const confirmResult = await this.$confirm( '请问是否要删除该'+this.titleText, '删除提示', { confirmButtonText: '确认删除', cancelButtonText: '取消', type: 'warning' } ).catch(err => err) //如果用户点击确认,则confirmResult 为'confirm' //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel' if (confirmResult != 'confirm') { return this.$message.info('已经取消删除') } //没有取消就是要删除,发送请求完成删除 const {data:res} = await this.$http.delete(`categories/${this.cateId}/attributes/${attr_id}`) if (res.meta.status !== 200) { return this.$message.error('删除参数数据失败') } this.$message.success('删除' + this.titleText + '数据成功') this.handleChange() }
2022年10月29日
89 阅读
0 评论
0 点赞
2022-10-26
Ansible--Ansible之Roles
Ansible之RolesRoles介绍ansible自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令引入即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷的include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。主要使用场景代码复用度较高的情况下。Roles目录结构各目录含义解释roles: <--所有的角色必须放在roles目录下,这个目录可以自定义位置,默认的位置在/etc/ansible/roles project: <---具体的角色项目名称,比如nginx、tomcat、php files: <--用来存放由copy模块或script模块调用的文件。 templates: <--用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件。 tasks: <--此目录应当包含一个main.yml文件,用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的task文件。 main.yml handlers: <--此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作。 main.yml vars: <--此目录应当包含一个main.yml文件,用于定义此角色用到的变量。 main.yml defaults: <--此目录应当包含一个main.yml文件,用于为当前角色设定默认变量。 main.yml meta: <--此目录应当包含一个main.yml文件,用于定义此角色的特殊设定及其依赖关系。 main.yml Roles示例通过ansible roles安装配置httpd服务,此处的roles使用默认的路径/etc/ansible/roles1)创建目录[root@ansible ~]# cd /etc/ansible/roles/ # 创建需要用到的目录 [root@ansible roles]# mkdir -p httpd/{handlers,tasks,templates,vars} [root@ansible roles]# cd httpd/ [root@ansible httpd]# tree . . ├── handlers ├── tasks ├── templates └── vars 4 directories, 0 file 2)变量文件准备vars/main.yml[root@ansible httpd]# vim vars/main.yml PORT: 8088 #指定httpd监听的端口 USERNAME: www #指定httpd运行用户 GROUPNAME: www #指定httpd运行组 3)配置文件模板准备templates/httpd.conf.j2# copy一个本地的配置文件放在templates/下并已j2为后缀 [root@ansible httpd]# cp /etc/httpd/conf/httpd.conf templates/httpd.conf.j2 # 进行一些修改,调用上面定义的变量 [root@ansible httpd]# vim templates/httpd.conf.j2 Listen {{ PORT }} User {{ USERNAME }} Group {{ GROUPNAME }} 4)任务剧本编写,创建用户、创建组、安装软件、配置、启动等# 创建组的task [root@ansible httpd]# vim tasks/group.yml - name: Create a Startup Group group: name=www gid=60 system=yes # 创建用户的task [root@ansible httpd]# vim tasks/user.yml - name: Create Startup Users user: name=www uid=60 system=yes shell=/sbin/nologin # 安装软件的task [root@ansible httpd]# vim tasks/install.yml - name: Install Package Httpd yum: name=httpd state=installed # 配置软件的task [root@ansible httpd]# vim tasks/config.yml - name: Copy Httpd Template File template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf notify: Restart Httpd # 启动软件的task [root@ansible httpd]# vim tasks/start.yml - name: Start Httpd Service service: name=httpd state=started enabled=yes # 编写main.yml,将上面的这些task引入进来 [root@ansible httpd]# vim tasks/main.yml - include: group.yml - include: user.yml - include: install.yml - include: config.yml - include: start.ym 5)编写重启httpd的handlers,handlers/main.yml[root@ansible httpd]# vim handlers/main.yml # 这里的名字需要和task中的notify保持一致 - name: Restart Httpd service: name=httpd state=restarted 6)编写主的httpd_roles.yml文件调用httpd角色[root@ansible httpd]# cd .. [root@ansible roles]# vim httpd_roles.yml --- - hosts: all remote_user: root roles: - role: httpd #指定角色名称 7)整体的一个目录结构查看[root@ansible roles]# tree . . ├── httpd │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── config.yml │ │ ├── group.yml │ │ ├── install.yml │ │ ├── main.yml │ │ ├── start.yml │ │ └── user.yml │ ├── templates │ │ └── httpd.conf.j2 │ └── vars │ └── main.yml └── httpd_roles.yml 5 directories, 10 files 8)测试playbook语法是否正确[root@ansible roles]# ansible-playbook -C httpd_roles.yml PLAY [all] ************************************************************************************************** TASK [Gathering Facts] ************************************************************************************** ok: [192.168.1.33] ok: [192.168.1.32] ok: [192.168.1.31] ok: [192.168.1.36] TASK [httpd : Create a Startup Group] *********************************************************************** changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.36] changed: [192.168.1.32] TASK [httpd : Create Startup Users] ************************************************************************* changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.36] TASK [httpd : Install Package Httpd] ************************************************************************ changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.36] TASK [httpd : Copy Httpd Template File] ********************************************************************* changed: [192.168.1.33] changed: [192.168.1.36] changed: [192.168.1.32] changed: [192.168.1.31] TASK [httpd : Start Httpd Service] ************************************************************************** changed: [192.168.1.36] changed: [192.168.1.31] changed: [192.168.1.32] changed: [192.168.1.33] RUNNING HANDLER [httpd : Restart Httpd] ********************************************************************* changed: [192.168.1.36] changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] PLAY RECAP ************************************************************************************************** 192.168.1.31 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.32 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.33 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.36 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 9)上面的测试没有问题,正式执行playbook[root@ansible roles]# ansible-playbook -C httpd_roles.yml PLAY [all] ************************************************************************************************** TASK [Gathering Facts] ************************************************************************************** ok: [192.168.1.33] ok: [192.168.1.32] ok: [192.168.1.31] ok: [192.168.1.36] TASK [httpd : Create a Startup Group] *********************************************************************** changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.36] changed: [192.168.1.32] TASK [httpd : Create Startup Users] ************************************************************************* changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.36] TASK [httpd : Install Package Httpd] ************************************************************************ changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.36] TASK [httpd : Copy Httpd Template File] ********************************************************************* changed: [192.168.1.33] changed: [192.168.1.36] changed: [192.168.1.32] changed: [192.168.1.31] TASK [httpd : Start Httpd Service] ************************************************************************** changed: [192.168.1.36] changed: [192.168.1.31] changed: [192.168.1.32] changed: [192.168.1.33] RUNNING HANDLER [httpd : Restart Httpd] ********************************************************************* changed: [192.168.1.36] changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] PLAY RECAP ************************************************************************************************** 192.168.1.31 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.32 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.33 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.36 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@ansible roles]# ansible-playbook httpd_roles.yml PLAY [all] ************************************************************************************************** TASK [Gathering Facts] ************************************************************************************** ok: [192.168.1.32] ok: [192.168.1.33] ok: [192.168.1.31] ok: [192.168.1.36] TASK [httpd : Create a Startup Group] *********************************************************************** changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.36] TASK [httpd : Create Startup Users] ************************************************************************* changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.36] TASK [httpd : Install Package Httpd] ************************************************************************ changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.36] TASK [httpd : Copy Httpd Template File] ********************************************************************* changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] changed: [192.168.1.36] TASK [httpd : Start Httpd Service] ************************************************************************** fatal: [192.168.1.36]: FAILED! => {"changed": false, "msg": "httpd: Syntax error on line 56 of /etc/httpd/conf/httpd.conf: Include directory '/etc/httpd/conf.modules.d' not found\n"} changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] RUNNING HANDLER [httpd : Restart Httpd] ********************************************************************* changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.31] PLAY RECAP ************************************************************************************************** 192.168.1.31 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.32 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.33 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.36 : ok=5 changed=4 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 这里有看到报错,其原因是因为192.168.1.36这台机器是centos6系统,别的都是centos7系统,两个系统安装的httpd默认版本是不一样的,所以报错。如果需要优化。参考这里ansible roles总结1、编写任务(task)的时候,里面不需要写需要执行的主机,单纯的写某个任务是干什么的即可,装软件的就是装软件的,启动的就是启动的。单独做某一件事即可,最后通过main.yml将这些单独的任务安装执行顺序include进来即可,这样方便维护且一目了然。2、定义变量时候直接安装k:v格式将变量写在vars/main.yml文件即可,然后task或者template直接调用即可,会自动去vars/main.yml文件里面去找。3、定义handlers时候,直接在handlers/main.yml文件中写需要做什么事情即可,多可的话可以全部写在该文件里面,也可以像task那样分开来写,通过include引入一样的可以。在task调用notify时直接写与handlers名字对应即可(二者必须高度一直)。4、模板文件一样放在templates目录下即可,task调用的时后直接写文件名字即可,会自动去到templates里面找。注意:如果是一个角色调用另外一个角色的单个task时后,那么task中如果有些模板或者文件,就得写绝对路径了。出处:https://www.cnblogs.com/yanjieli/p/10971862.html版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
2022年10月26日
75 阅读
0 评论
1 点赞
2022-10-26
Ansible--Module
Ansible Ad-hoc模式常用模块ansible-doc 常用命令# ansible-doc -h Usage: ansible-doc [-l|-F|-s] [options] [-t <plugin type> ] [plugin] -j 以json格式显示所有模块信息 -l 列出所有的模块 -s 查看模块常用参数 # 直接跟模块名,显示模块所有信息 [root@ansible ~]# ansible-doc -j [root@ansible ~]# ansible-doc -l [root@ansible ~]# ansible-doc -l |wc -l #统计所有模块个数,ansible2.8共计2834个模块 2834 命令相关的模块commandansible默认的模块,执行命令,注意:shell中的"<", ">", "|", ";", "&","$"等特殊字符不能在command模块中使用,如果需要使用,则用shell模块# 查看模块参数 [root@ansible ~]# ansible-doc -s command # 在192.168.1.31服务器上面执行ls命令,默认是在当前用户的家目录/root [root@ansible ~]# ansible 192.168.1.31 -a 'ls' # chdir 先切换工作目录,再执行后面的命令,一般情况下在编译时候使用 [root@ansible ~]# ansible 192.168.1.31 -a 'chdir=/tmp pwd' 192.168.1.31 | CHANGED | rc=0 >> /tmp # creates 如果creates的文件存在,则执行后面的操作 [root@ansible ~]# ansible 192.168.1.31 -a 'creates=/tmp ls /etc/passwd' #tmp目录存在,则不执行后面的ls命令 192.168.1.31 | SUCCESS | rc=0 >> skipped, since /tmp exists [root@ansible ~]# ansible 192.168.1.31 -a 'creates=/tmp11 ls /etc/passwd' # tmp11文件不存在,则执行后面的ls命令 192.168.1.31 | CHANGED | rc=0 >> /etc/passwd # removes 和creates相反,如果removes的文件存在,才执行后面的操作 [root@ansible ~]# ansible 192.168.1.31 -a 'removes=/tmp ls /etc/passwd' #tmp文件存在,则执行了后面的ls命令 192.168.1.31 | CHANGED | rc=0 >> /etc/passwd [root@ansible ~]# ansible 192.168.1.31 -a 'removes=/tmp11 ls /etc/passwd' #tmp11文件不存在,则没有执行后面的ls命令 192.168.1.31 | SUCCESS | rc=0 >> skipped, since /tmp11 does not exist shell专门用来执行shell命令的模块,和command模块一样,参数基本一样,都有chdir,creates,removes等参数# 查看模块参数 [root@ansible ~]# ansible-doc -s shell [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'mkdir /tmp/test' [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'ls /tmp' #执行下面这条命令,每次执行都会更新文件的时间戳 [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'cd /tmp/test && touch 1.txt && ls' 192.168.1.31 | CHANGED | rc=0 >> 1.txt # 由于有时候不想更新文件的创建时间戳,则如果存在就不执行creates [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'creates=/tmp/test/1.txt cd /tmp/test && touch 1.txt && ls' 192.168.1.31 | SUCCESS | rc=0 >> skipped, since /tmp/test/1.txt exists script用于在被管理机器上面执行shell脚本的模块,脚本无需在被管理机器上面存在# 查看模块参数 [root@ansible ~]# ansible-doc -s script # 编写shell脚本 [root@ansible ~]# vim ansible_test.sh #!/bin/bash echo `hostname` # 在所有被管理机器上执行该脚本 [root@ansible ~]# ansible all -m script -a '/root/ansible_test.sh' 192.168.1.32 | CHANGED => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.1.32 closed.\r\n", "stderr_lines": [ "Shared connection to 192.168.1.32 closed." ], "stdout": "linux.node02.com\r\n", "stdout_lines": [ "linux.node02.com" ] } ...... 文件相关的模块file用于对文件的处理,创建,删除,权限控制等# 查看模块参数 [root@ansible ~]# ansible-doc -s file path #要管理的文件路径 recurse #递归 state: directory #创建目录,如果目标不存在则创建目录及其子目录 touch #创建文件,如果文件存在,则修改文件 属性 absent #删除文件或目录 mode #设置文件或目录权限 owner #设置文件或目录属主信息 group #设置文件或目录属组信息 link #创建软连接,需要和src配合使用 hard #创建硬连接,需要和src配合使用 # 创建目录 [root@ansible ~]# ansible 192.168.1.31 -m file -a 'path=/tmp/test1 state=directory' # 创建文件 [root@ansible ~]# ansible 192.168.1.31 -m file -a 'path=/tmp/test2 state=touch' # 建立软链接(src表示源文件,path表示目标文件) [root@ansible ~]# ansible 192.168.1.31 -m file -a 'src=/tmp/test1 path=/tmp/test3 state=link' # 删除文件 [root@ansible ~]# ansible 192.168.1.31 -m file -a 'path=/tmp/test2 state=absent' # 创建文件时同时设置权限等信息 [root@ansible ~]# ansible 192.168.1.31 -m file -a 'path=/tmp/test4 state=directory mode=775 owner=root group=root' copy用于管理端复制文件到远程主机,并可以设置权限,属组,属主等# 查看模块参数 [root@ansible ~]# ansible-doc -s copy src #需要copy的文件的源路径 dest #需要copy的文件的目标路径 backup #对copy的文件进行备份 content #直接在远程主机被管理文件中添加内容,会覆盖原文件内容 mode #对copy到远端的文件设置权限 owner #对copy到远端的文件设置属主 group #对copy到远端文件设置属组 # 复制文件到远程主机并改名 [root@ansible ~]# ansible 192.168.1.31 -m copy -a 'dest=/tmp/a.sh src=/root/ansible_test.sh' # 复制文件到远程主机,并备份远程文件,安装时间信息备份文件(当更新文件内容后,重新copy时用到) [root@ansible ~]# ansible 192.168.1.31 -m copy -a 'dest=/tmp/a.sh src=/root/ansible_test.sh backup=yes' # 直接在远程主机a.sh中添加内容 [root@ansible ~]# ansible 192.168.1.31 -m copy -a 'dest=/tmp/a.sh content="#!/bin/bash\n echo `uptime`"' # 复制文件到远程主机,并设置权限及属主与属组 [root@ansible ~]# ansible 192.168.1.31 -m copy -a 'dest=/tmp/passwd src=/etc/passwd mode=700 owner=root group=root' fetch用于从被管理机器上面拉取文件,拉取下来的内容会保留目录结构,一般情况用在收集被管理机器的日志文件等# 查看模块参数 [root@ansible ~]# ansible-doc -s fetch src #指定需要从远端机器拉取的文件路径 dest #指定从远端机器拉取下来的文件存放路径 # 从被管理机器上拉取cron日志文件,默认会已管理节点地址创建一个目录,并存放在内 [root@ansible ~]# ansible 192.168.1.31 -m fetch -a 'dest=/tmp src=/var/log/cron' [root@ansible ~]# tree /tmp/192.168.1.31/ /tmp/192.168.1.31/ └── var └── log └── cron 2 directories, 1 file 用户相关的模块user用于对系统用户的管理,用户的创建、删除、家目录、属组等设置# 查看模块参数 [root@ansible ~]# ansible-doc -s user name #指定用户的名字 home #指定用户的家目录 uid #指定用户的uid group #指定用户的用户组 groups #指定用户的附加组 password #指定用户的密码 shell #指定用户的登录shell create_home #是否创建用户家目录,默认是yes remove #删除用户时,指定是否删除家目录 state: absent #删除用户 # 创建用户名指定家目录,指定uid及组 [root@ansible ~]# ansible 192.168.1.31 -m user -a 'name=mysql home=/opt/mysql uid=1002 group=root' [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'id mysql && ls -l /opt' 192.168.1.31 | CHANGED | rc=0 >> uid=1002(mysql) gid=0(root) 组=0(root) 总用量 0 drwx------ 3 mysql root 78 5月 27 18:13 mysql # 创建用户,不创建家目录,并且不能登录 [root@ansible ~]# ansible 192.168.1.31 -m user -a 'name=apache shell=/bin/nologin uid=1003 create_home=no' [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'id apache && tail -1 /etc/passwd' 192.168.1.31 | CHANGED | rc=0 >> uid=1003(apache) gid=1003(apache) 组=1003(apache) apache:x:1003:1003::/home/apache:/bin/nologin # 删除用户 [root@ansible ~]# ansible 192.168.1.31 -m user -a 'name=apache state=absent' # 删除用户并删除家目录 [root@ansible ~]# ansible 192.168.1.31 -m user -a 'name=mysql state=absent remove=yes' group用于创建组,当创建用户时如果需要指定组,组不存在的话就可以通过group先创建组# 查看模块参数 [root@ansible ~]# ansible-doc -s group name #指定组的名字 gid #指定组的gid state: absent #删除组 present #创建组(默认的状态) # 创建组 [root@ansible ~]# ansible 192.168.1.31 -m group -a 'name=www' # 创建组并指定gid [root@ansible ~]# ansible 192.168.1.31 -m group -a 'name=www1 gid=1005' # 删除组 [root@ansible ~]# ansible 192.168.1.31 -m group -a 'name=www1 state=absent' 软件包相关的模块yum用于对软件包的管理,下载、安装、卸载、升级等操作# 查看模块参数 [root@ansible ~]# ansible-doc -s yum name #指定要操作的软件包名字 download_dir #指定下载软件包的存放路径,需要配合download_only一起使用 download_only #只下载软件包,而不进行安装,和yum --downloadonly一样 list: installed #列出所有已安装的软件包 updates #列出所有可以更新的软件包 repos #列出所有的yum仓库 state: installed, present #安装软件包(两者任选其一都可以) removed, absent #卸载软件包 latest #安装最新软件包 # 列出所有已安装的软件包 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'list=installed' # 列出所有可更新的软件包 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'list=updates' #列出所有的yum仓库 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'list=repos' #只下载软件包并到指定目录下 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'name=httpd download_only=yes download_dir=/tmp' #安装软件包 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'name=httpd state=installed' #卸载软件包 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'name=httpd state=removed' #安装包组,类似yum groupinstall 'Development Tools' [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'name="@Development Tools" state=installed' pip用于安装python中的包# 查看模块参数 [root@ansible ~]# ansible-doc -s pip # 使用pip时,需要保证被管理机器上有python-pip软件包 [root@ansible ~]# ansible 192.168.1.31 -m yum -a 'name=python-pip' # 安装pip包 [root@ansible ~]# ansible 192.168.1.31 -m pip -a 'name=flask' service服务模块,用于对服务进行管理,服务的启动、关闭、开机自启等# 查看模块参数 [root@ansible ~]# ansible-doc -s service name #指定需要管理的服务名 enabled #指定是否开机自启动 state: #指定服务状态 started #启动服务 stopped #停止服务 restarted #重启服务 reloaded #重载服务 # 启动服务,并设置开机自启动 [root@ansible ~]# ansible 192.168.1.31 -m service -a 'name=crond state=started enabled=yes' 计划任务相关的模块cron用于指定计划任务,和crontab -e一样# 查看模块参数 [root@ansible ~]# ansible-doc -s cron job #指定需要执行的任务 minute #分钟 hour #小时 day #天 month #月 weekday #周 name #对计划任务进行描述 state: absetn #删除计划任务 # 创建一个计划任务,并描述是干嘛用的 [root@ansible ~]# ansible 192.168.1.31 -m cron -a "name='这是一个测试的计划任务' minute=* hour=* day=* month=* weekday=* job='/bin/bash /root/test.sh'" [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'crontab -l' 192.168.1.31 | CHANGED | rc=0 >> #Ansible: 这是一个测试的计划任务 * * * * * /bin/bash /root/test.sh # 创建一个没有带描述的计划任务 [root@ansible ~]# ansible 192.168.1.31 -m cron -a "job='/bin/sh /root/test.sh'" # 删除计划任务 [root@ansible ~]# ansible 192.168.1.31 -m cron -a "name='None' job='/bin/sh /root/test.sh' state=absent" 系统信息相关的模块setup用于获取系统信息的一个模块# 查看模块参数 [root@ansible ~]# ansible-doc -s setup # 查看系统所有信息 [root@ansible ~]# ansible 192.168.1.31 -m setup # filter 对系统信息进行过滤 [root@ansible ~]# ansible 192.168.1.31 -m setup -a 'filter=ansible_all_ipv4_addresses' # 常用的过滤选项 ansible_all_ipv4_addresses 所有的ipv4地址 ansible_all_ipv6_addresses 所有的ipv6地址 ansible_architecture 系统的架构 ansible_date_time 系统时间 ansible_default_ipv4 系统的默认ipv4地址 ansible_distribution 系统名称 ansible_distribution_file_variety 系统的家族 ansible_distribution_major_version 系统的版本 ansible_domain 系统所在的域 ansible_fqdn 系统的主机名 ansible_hostname 系统的主机名,简写 ansible_os_family 系统的家族 ansible_processor_cores cpu的核数 ansible_processor_count cpu的颗数 ansible_processor_vcpus cpu的个数 出处:https://www.cnblogs.com/yanjieli/p/10969143.html版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
2022年10月26日
70 阅读
0 评论
1 点赞
2022-10-26
Ansible--快速入门
Ansible快速入门介绍Ansible是一款简单的运维自动化工具,只需要使用ssh协议连接就可以来进行系统管理,自动化执行命令,部署等任务。Ansible的特点1、ansible不需要单独安装客户端,也不需要启动任何服务2、ansible是python中的一套完整的自动化执行任务模块3、ansible playbook 采用yaml配置,对于自动化任务执行过一目了然Ansible组成结构 Ansible是Ansible的命令工具,核心执行工具;一次性或临时执行的操作都是通过该命令执行。 Ansible Playbook任务剧本(又称任务集),编排定义Ansible任务集的配置文件,由Ansible顺序依次执行,yaml格式。 InventoryAnsible管理主机的清单,默认是/etc/ansible/hosts文件。 ModulesAnsible执行命令的功能模块,Ansible2.3版本为止,共有1039个模块。还可以自定义模块。 Plugins插件,模块功能的补充,常有连接类型插件,循环插件,变量插件,过滤插件,插件功能用的较少。 API提供给第三方程序调用的应用程序编程接口。 环境准备 IP 系统 主机名 描述 192.168.1.30 CentOS7 ansible ansible管理节点 192.168.1.31 CentOS7 linux.node01.com 被管理节点1 192.168.1.32 CentOS7 linux.node02.com 被管理节点2 192.168.1.33 CentOS7 linux.node03.com 被管理节点3 192.168.1.36 CentOS6 linux.node06.com 被管理节点6 Ansible安装1)配置epel源[root@ansible ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo [root@ansible ~]# yum clean all [root@ansible ~]# yum makecache 2)安装ansible[root@ansible ~]# yum -y install ansible # 查看ansible版本 [root@ansible ~]# ansible --version ansible 2.8.0 config file = /etc/ansible/ansible.cfg configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.5 (default, Aug 4 2017, 00:39:18) [GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] Ansible Inventory文件Inventory文件通常用于定义要管理的主机的认证信息,例如ssh登录用户名、密码以及key相关信息。可以同时操作一个组的多台主机,组与主机组之间的关系都是通过inventory文件配置。配置文件路径为:/etc/ansible/hosts基于密码连接[root@ansible ~]# vim /etc/ansible/hosts # 方法一 主机+端口+密码 [webserver] 192.168.1.31 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" 192.168.1.32 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" 192.168.1.33 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" 192.168.1.36 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass="123456" # 方法二 主机+端口+密码 [webserver] 192.168.1.3[1:3] ansible_ssh_user=root ansible_ssh_pass="123456" # 方法二 主机+端口+密码 [webserver] 192.168.1.3[1:3] [webserver:vars] ansible_ssh_pass="123456" 基于秘钥连接基于秘钥连接需要先创建公钥和私钥,并发送给被管理机器1)生成公私钥[root@ansible ~]# ssh-keygen [root@ansible ~]# for i in {1,2,3,6}; do ssh-copy-id -i 192.168.1.3$i ; done 2)配置连接[root@ansible ~]# vim /etc/ansible/hosts # 方法一 主机+端口+密钥 [webserver] 192.168.1.31:22 192.168.1.32 192.168.1.33 192.168.1.36 # 方法一 别名主机+端口+密钥 [webserver] node1 ansible_ssh_host=192.168.1.31 ansible_ssh_port=22 node2 ansible_ssh_host=192.168.1.32 ansible_ssh_port=22 node3 ansible_ssh_host=192.168.1.33 ansible_ssh_port=22 node6 ansible_ssh_host=192.168.1.36 ansible_ssh_port=22 主机组的使用# 主机组变量名+主机+密码 [apache] 192.168.1.36 192.168.1.33 [apache.vars] ansible_ssh_pass='123456' # 主机组变量名+主机+密钥 [nginx] 192.168.1.3[1:2] # 定义多个组,把一个组当另外一个组的组员 [webserver:children] #webserver组包括两个子组:apache nginx apache nginx 临时指定inventory1)先编辑一个主机定义清单2)在执行命令是指定inventory[root@ansible ~]# ansible dockers -m ping -i /etc/dockers -o 192.168.1.33 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.32 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.31 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} Inventory内置参数Ansible Ad-Hocad-hoc —— 临时的,在ansible中是指需要快速执行,并且不需要保存的命令。说白了就是执行简单的命令——一条命令。对于复杂的命令则为playbook,类似于saltstack的state sls状态文件。ansible命令格式1)常用命令参数[root@ansible ~]# ansible -h Usage: ansible <host-pattern> [options] -a MODULE_ARGS #模块参数 -C, --check #检查语法 -f FORKS #并发 --list-hosts #列出主机列表 -m MODULE_NAME #模块名字 -o 使用精简的输出 2)示例[root@ansible ~]# ansible webserver -m shell -a 'uptime' -o 192.168.1.36 | CHANGED | rc=0 | (stdout) 13:46:14 up 1 day, 9:20, 4 users, load average: 0.00, 0.00, 0.00 192.168.1.33 | CHANGED | rc=0 | (stdout) 21:26:33 up 1 day, 8:51, 3 users, load average: 0.00, 0.01, 0.05 192.168.1.31 | CHANGED | rc=0 | (stdout) 21:26:33 up 1 day, 8:50, 3 users, load average: 0.00, 0.01, 0.05 192.168.1.32 | CHANGED | rc=0 | (stdout) 21:26:33 up 1 day, 8:59, 3 users, load average: 0.00, 0.01, 0.05 3)命令说明host-pattern格式目标target主机,主机组匹配方式主机的匹配# 一台目标主机 [root@ansible ~]# ansible 192.168.1.31 -m ping # 多台目标主机 [root@ansible ~]# ansible 192.168.1.31,192.168.1.32 -m ping # 所有目标主机 [root@ansible ~]# ansible all -m ping 组的匹配# 组的配置信息如下:这里定义了一个nginx组和一个apache组 [root@ansible ~]# ansible nginx --list hosts (2): 192.168.1.31 192.168.1.32 [root@ansible ~]# ansible apache --list hosts (3): 192.168.1.36 192.168.1.33 192.168.1.32 # 一个组的所有主机匹配 [root@ansible ~]# ansible apache -m ping # 匹配apache组中有,但是nginx组中没有的所有主机 [root@ansible ~]# ansible 'apache:!nginx' -m ping -o 192.168.1.36 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.33 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} # 匹配apache组和nginx组中都有的机器(并集) [root@ansible ~]# ansible 'apache:&nginx' -m ping -o 192.168.1.32 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} # 匹配apache组nginx组两个组所有的机器(并集);等于ansible apache,nginx -m ping [root@ansible ~]# ansible 'apache:nginx' -m ping -o 192.168.1.32 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.31 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.33 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 192.168.1.36 | SUCCESS => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "ping": "pong"} 出处:https://www.cnblogs.com/yanjieli/p/10969089.html版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
2022年10月26日
59 阅读
0 评论
0 点赞
2022-10-26
Ansible--Ansible之Playbook
Tags: ansibleURL: https://www.cnblogs.com/yanjieli/p/10969299.htmlAnsible之PlaybookPlaybook介绍Playbook与ad-hoc相比,是一种完全不同的运用ansible的方式,类似与saltstack的state状态文件。ad-hoc无法持久使用,playbook可以持久使用。playbook是由一个或多个play组成的列表,play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓的task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联合起来按事先编排的机制完成某一任务Playbook核心元素 Hosts 执行的远程主机列表 Tasks 任务集 Varniables 内置变量或自定义变量在playbook中调用 Templates 模板,即使用模板语法的文件,比如配置文件等 Handlers 和notity结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行 tags 标签,指定某条任务执行,用于选择运行playbook中的部分代码。 Playbook语法playbook使用yaml语法格式,后缀可以是yaml,也可以是yml。 在单一一个playbook文件中,可以连续三个连子号(--)区分多个play。还有选择性的连续三个点好(...)用来表示play的结尾,也可省略。 次行开始正常写playbook的内容,一般都会写上描述该playbook的功能。 使用#号注释代码。 缩进必须统一,不能空格和tab混用。 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的。 YAML文件内容和Linux系统大小写判断方式保持一致,是区分大小写的,k/v的值均需大小写敏感 k/v的值可同行写也可以换行写。同行使用:分隔。 v可以是个字符串,也可以是一个列表 一个完整的代码块功能需要最少元素包括 name: task 一个简单的示例# 创建playbook文件 [root@ansible ~]# cat playbook01.yml --- #固定格式 - hosts: 192.168.1.31 #定义需要执行主机 remote_user: root #远程用户 vars: #定义变量 http_port: 8088 #变量 tasks: #定义一个任务的开始 - name: create new file #定义任务的名称 file: name=/tmp/playtest.txt state=touch #调用模块,具体要做的事情 - name: create new user user: name=test02 system=yes shell=/sbin/nologin - name: install package yum: name=httpd - name: config httpd template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf notify: #定义执行一个动作(action)让handlers来引用执行,与handlers配合使用 - restart apache #notify要执行的动作,这里必须与handlers中的name定义内容一致 - name: copy index.html copy: src=/var/www/html/index.html dest=/var/www/html/index.html - name: start httpd service: name=httpd state=started handlers: #处理器:更加tasks中notify定义的action触发执行相应的处理动作 - name: restart apache #要与notify定义的内容相同 service: name=httpd state=restarted #触发要执行的动作 #测试页面准备 [root@ansible ~]# echo "<h1>playbook test file</h1>" >>/var/www/html/index.html #配置文件准备 [root@ansible ~]# cat httpd.conf |grep ^Listen Listen {{ http_port }} #执行playbook, 第一次执行可以加-C选项,检查写的playbook是否ok [root@ansible ~]# ansible-playbook playbook01.yml PLAY [192.168.1.31] ********************************************************************************************* TASK [Gathering Facts] ****************************************************************************************** ok: [192.168.1.31] TASK [create new file] ****************************************************************************************** changed: [192.168.1.31] TASK [create new user] ****************************************************************************************** changed: [192.168.1.31] TASK [install package] ****************************************************************************************** changed: [192.168.1.31] TASK [config httpd] ********************************************************************************************* changed: [192.168.1.31] TASK [copy index.html] ****************************************************************************************** changed: [192.168.1.31] TASK [start httpd] ********************************************************************************************** changed: [192.168.1.31] PLAY RECAP ****************************************************************************************************** 192.168.1.31 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 # 验证上面playbook执行的结果 [root@ansible ~]# ansible 192.168.1.31 -m shell -a 'ls /tmp/playtest.txt && id test02' 192.168.1.31 | CHANGED | rc=0 >> /tmp/playtest.txt uid=990(test02) gid=985(test02) 组=985(test02) [root@ansible ~]# curl 192.168.1.31:8088 <h1>playbook test file</h1> Playbook的运行方式通过ansible-playbook命令运行格式:ansible-playbook <filename.yml> ... [options][root@ansible PlayBook]# ansible-playbook -h #ansible-playbook常用选项: --check or -C #只检测可能会发生的改变,但不真正执行操作 --list-hosts #列出运行任务的主机 --list-tags #列出playbook文件中定义所有的tags --list-tasks #列出playbook文件中定义的所以任务集 --limit #主机列表 只针对主机列表中的某个主机或者某个组执行 -f #指定并发数,默认为5个 -t #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags) -v #显示过程 -vv -vvv更详细 Playbook中元素属性主机与用户在一个playbook开始时,最先定义的是要操作的主机和用户--- - hosts: 192.168.1.31 remote_user: root 除了上面的定义外,还可以在某一个tasks中定义要执行该任务的远程用户tasks: - name: run df -h remote_user: test shell: name=df -h 还可以定义使用sudo授权用户执行该任务tasks任务列表每一个task必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中可以很清楚的辨别是属于哪一个task的,如果没有定义 name,action的值将会用作输出信息中标记特定的task。每一个playbook中可以包含一个或者多个tasks任务列表,每一个tasks完成具体的一件事,(任务模块)比如创建一个用户或者安装一个软件等,在hosts中定义的主机或者主机组都将会执行这个被定义的tasks。Handlers与Notify很多时候当我们某一个配置发生改变,我们需要重启服务,(比如httpd配置文件文件发生改变了)这时候就可以用到handlers和notify了;(当发生改动时)notify actions会在playbook的每一个task结束时被触发,而且即使有多个不同task通知改动的发生,notify actions知会被触发一次;比如多个resources指出因为一个配置文件被改动,所以apache需要重启,但是重新启动的操作知会被执行一次。[root@ansible ~]# cat httpd.yml #用于安装httpd并配置启动 --- - hosts: 192.168.1.31 remote_user: root tasks: - name: install httpd yum: name=httpd state=installed - name: config httpd template: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf notify: - restart httpd - name: start httpd service: name=httpd state=started handlers: - name: restart httpd service: name=httpd state=restarted #这里只要对httpd.conf配置文件作出了修改,修改后需要重启生效,在tasks中定义了restart httpd这个action,然后在handlers中引用上面tasks中定义的notify。 Playbook中变量的使用环境说明:这里配置了两个组,一个apache组和一个nginx组[root@ansible PlayBook]# cat /etc/ansible/hosts [apache] 192.168.1.36 192.168.1.33 [nginx] 192.168.1.3[1:2] 命令行指定变量执行playbook时候通过参数-e传入变量,这样传入的变量在整个playbook中都可以被调用,属于全局变量[root@ansible PlayBook]# cat variables.yml --- - hosts: all remote_user: root tasks: - name: install pkg yum: name={{ pkg }} #执行playbook 指定pkg [root@ansible PlayBook]# ansible-playbook -e "pkg=httpd" variables.yml hosts文件中定义变量在/etc/ansible/hosts文件中定义变量,可以针对每个主机定义不同的变量,也可以定义一个组的变量,然后直接在playbook中直接调用。注意,组中定义的变量没有单个主机中的优先级高。# 编辑hosts文件定义变量 [root@ansible PlayBook]# vim /etc/ansible/hosts [apache] 192.168.1.36 webdir=/opt/test #定义单个主机的变量 192.168.1.33 [apache:vars] #定义整个组的统一变量 webdir=/web/test [nginx] 192.168.1.3[1:2] [nginx:vars] webdir=/opt/web # 编辑playbook文件 [root@ansible PlayBook]# cat variables.yml --- - hosts: all remote_user: root tasks: - name: create webdir file: name={{ webdir }} state=directory #引用变量 # 执行playbook [root@ansible PlayBook]# ansible-playbook variables.yml playbook文件中定义变量编写playbook时,直接在里面定义变量,然后直接引用,可以定义多个变量;注意:如果在执行playbook时,又通过-e参数指定变量的值,那么会以-e参数指定的为准。# 编辑playbook [root@ansible PlayBook]# cat variables.yml --- - hosts: all remote_user: root vars: #定义变量 pkg: nginx #变量1 dir: /tmp/test1 #变量2 tasks: - name: install pkg yum: name={{ pkg }} state=installed #引用变量 - name: create new dir file: name={{ dir }} state=directory #引用变量 # 执行playbook [root@ansible PlayBook]# ansible-playbook variables.yml # 如果执行时候又重新指定了变量的值,那么会已重新指定的为准 [root@ansible PlayBook]# ansible-playbook -e "dir=/tmp/test2" variables.yml 调用setup模块获取变量setup模块默认是获取主机信息的,有时候在playbook中需要用到,所以可以直接调用。常用的参数参考# 编辑playbook文件 [root@ansible PlayBook]# cat variables.yml --- - hosts: all remote_user: root tasks: - name: create file file: name={{ ansible_fqdn }}.log state=touch #引用setup中的ansible_fqdn # 执行playbook [root@ansible PlayBook]# ansible-playbook variables.yml 独立的变量YAML文件中定义为了方便管理将所有的变量统一放在一个独立的变量YAML文件中,laybook文件直接引用文件调用变量即可。# 定义存放变量的文件 [root@ansible PlayBook]# cat var.yml var1: vsftpd var2: httpd # 编写playbook [root@ansible PlayBook]# cat variables.yml --- - hosts: all remote_user: root vars_files: #引用变量文件 - ./var.yml #指定变量文件的path(这里可以是绝对路径,也可以是相对路径) tasks: - name: install package yum: name={{ var1 }} #引用变量 - name: create file file: name=/tmp/{{ var2 }}.log state=touch #引用变量 # 执行playbook [root@ansible PlayBook]# ansible-playbook variables.yml Playbook中标签的使用一个playbook文件中,执行时如果想执行某一个任务,那么可以给每个任务集进行打标签,这样在执行的时候可以通过-t选择指定标签执行,还可以通过--skip-tags选择除了某个标签外全部执行等。# 编辑playbook [root@ansible PlayBook]# cat httpd.yml --- - hosts: 192.168.1.31 remote_user: root tasks: - name: install httpd yum: name=httpd state=installed tags: inhttpd - name: start httpd service: name=httpd state=started tags: sthttpd - name: restart httpd service: name=httpd state=restarted tags: - rshttpd - rs_httpd # 正常执行的结果 [root@ansible PlayBook]# ansible-playbook httpd.yml PLAY [192.168.1.31] ************************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************************** ok: [192.168.1.31] TASK [install httpd] ************************************************************************************************************************* ok: [192.168.1.31] TASK [start httpd] *************************************************************************************************************************** ok: [192.168.1.31] TASK [restart httpd] ************************************************************************************************************************* changed: [192.168.1.31] PLAY RECAP *********************************************************************************************************************************** 192.168.1.31 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 1)通过-t选项指定tags进行执行# 通过-t指定tags名称,多个tags用逗号隔开 [root@ansible PlayBook]# ansible-playbook -t rshttpd httpd.yml PLAY [192.168.1.31] ************************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************************** ok: [192.168.1.31] TASK [restart httpd] ************************************************************************************************************************* changed: [192.168.1.31] PLAY RECAP *********************************************************************************************************************************** 192.168.1.31 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 2)通过--skip-tags选项排除不执行的tags[root@ansible PlayBook]# ansible-playbook --skip-tags inhttpd httpd.yml PLAY [192.168.1.31] ************************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************************** ok: [192.168.1.31] TASK [start httpd] *************************************************************************************************************************** ok: [192.168.1.31] TASK [restart httpd] ************************************************************************************************************************* changed: [192.168.1.31] PLAY RECAP *********************************************************************************************************************************** 192.168.1.31 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Playbook中模板的使用template模板为我们提供了动态配置服务,使用jinja2语言,里面支持多种条件判断、循环、逻辑运算、比较操作等。其实说白了也就是一个文件,和之前配置文件使用copy一样,只是使用copy,不能根据服务器配置不一样进行不同动态的配置。这样就不利于管理。说明:1、多数情况下都将template文件放在和playbook文件同级的templates目录下(手动创建),这样playbook文件中可以直接引用,会自动去找这个文件。如果放在别的地方,也可以通过绝对路径去指定。2、模板文件后缀名为.j2。循环参考示例:通过template安装httpd1)playbook文件编写[root@ansible PlayBook]# cat testtmp.yml #模板示例 --- - hosts: all remote_user: root vars: - listen_port: 88 #定义变量 tasks: - name: Install Httpd yum: name=httpd state=installed - name: Config Httpd template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf #使用模板 notify: Restart Httpd - name: Start Httpd service: name=httpd state=started handlers: - name: Restart Httpd service: name=httpd state=restarted 2)模板文件准备,httpd配置文件准备,这里配置文件端口使用了变量[root@ansible PlayBook]# cat templates/httpd.conf.j2 |grep ^Listen Listen {{ listen_port }} 3)查看目录结构# 目录结构 [root@ansible PlayBook]# tree . . ├── templates │ └── httpd.conf.j2 └── testtmp.yml 1 directory, 2 files 4)执行playbook,由于192.168.1.36那台机器是6的系统,模板文件里面的配置文件是7上面默认的httpd配置文件,httpd版本不一样(6默认版本为2.2.15,7默认版本为2.4.6),所以拷贝过去后启动报错。下面使用playbook中的判断语句进行处理;此处先略过[root@ansible PlayBook]# ansible-playbook testtmp.yml PLAY [all] ****************************************************************************************** TASK [Gathering Facts] ****************************************************************************** ok: [192.168.1.36] ok: [192.168.1.32] ok: [192.168.1.33] ok: [192.168.1.31] TASK [Install Httpd] ******************************************************************************** ok: [192.168.1.36] ok: [192.168.1.33] ok: [192.168.1.32] ok: [192.168.1.31] TASK [Config Httpd] ********************************************************************************* changed: [192.168.1.31] changed: [192.168.1.33] changed: [192.168.1.32] changed: [192.168.1.36] TASK [Start Httpd] ********************************************************************************** fatal: [192.168.1.36]: FAILED! => {"changed": false, "msg": "httpd: Syntax error on line 56 of /etc/httpd/conf/httpd.conf: Include directory '/etc/httpd/conf.modules.d' not found\n"} changed: [192.168.1.32] changed: [192.168.1.33] changed: [192.168.1.31] RUNNING HANDLER [Restart Httpd] ********************************************************************* changed: [192.168.1.31] changed: [192.168.1.32] changed: [192.168.1.33] PLAY RECAP ****************************************************************************************** 192.168.1.31 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.32 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.33 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 192.168.1.36 : ok=3 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 template之whenwhen语句参考条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过when语句执行,在task中使用jinja2的语法格式、when语句:在task后添加when子句即可使用条件测试;when语句支持jinja2表达式语法。类似这样:tasks: - command: /bin/false register: result ignore_errors: True - command: /bin/something when: result|failed - command: /bin/something_else when: result|success - command: /bin/still/something_else when: result|skipped 示例:通过when语句完善上面的httpd配置1)准备两个配置文件,一个centos6系统httpd配置文件,一个centos7系统httpd配置文件。[root@ansible PlayBook]# tree templates/ templates/ ├── httpd6.conf.j2 #6系统2.2.15版本httpd配置文件 └── httpd7.conf.j2 #7系统2.4.6版本httpd配置文件 0 directories, 2 files 2)修改playbook文件,通过setup模块获取系统版本去判断。setup常用模块[root@ansible PlayBook]# cat testtmp.yml #when示例 --- - hosts: all remote_user: root vars: - listen_port: 88 tasks: - name: Install Httpd yum: name=httpd state=installed - name: Config System6 Httpd template: src=httpd6.conf.j2 dest=/etc/httpd/conf/httpd.conf when: ansible_distribution_major_version == "6" #判断系统版本,为6便执行上面的template配置6的配置文件 notify: Restart Httpd - name: Config System7 Httpd template: src=httpd7.conf.j2 dest=/etc/httpd/conf/httpd.conf when: ansible_distribution_major_version == "7" #判断系统版本,为7便执行上面的template配置7的配置文件 notify: Restart Httpd - name: Start Httpd service: name=httpd state=started handlers: - name: Restart Httpd service: name=httpd state=restarted 3)执行playbook[root@ansible PlayBook]# ansible-playbook testtmp.yml PLAY [all] ****************************************************************************************** TASK [Gathering Facts] ****************************************************************************** ok: [192.168.1.31] ok: [192.168.1.32] ok: [192.168.1.33] ok: [192.168.1.36] TASK [Install Httpd] ******************************************************************************** ok: [192.168.1.32] ok: [192.168.1.33] ok: [192.168.1.31] ok: [192.168.1.36] TASK [Config System6 Httpd] ************************************************************************* skipping: [192.168.1.33] skipping: [192.168.1.31] skipping: [192.168.1.32] changed: [192.168.1.36] TASK [Config System7 Httpd] ************************************************************************* skipping: [192.168.1.36] changed: [192.168.1.33] changed: [192.168.1.31] changed: [192.168.1.32] TASK [Start Httpd] ********************************************************************************** ok: [192.168.1.36] ok: [192.168.1.31] ok: [192.168.1.32] ok: [192.168.1.33] RUNNING HANDLER [Restart Httpd] ********************************************************************* changed: [192.168.1.33] changed: [192.168.1.31] changed: [192.168.1.32] changed: [192.168.1.36] PLAY RECAP ****************************************************************************************** 192.168.1.31 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 192.168.1.32 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 192.168.1.33 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 192.168.1.36 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 template之with_itemswith_items迭代,当有需要重复性执行的任务时,可以使用迭代机制。对迭代项的引用,固定变量名为“item”,要在task中使用with_items给定要迭代的元素列表。列表格式:字符串字典示例1:通过with_items安装多个不同软件编写playbook[root@ansible PlayBook]# cat testwith.yml # 示例with_items --- - hosts: all remote_user: root tasks: - name: Install Package yum: name={{ item }} state=installed #引用item获取值 with_items: #定义with_items - httpd - vsftpd - nginx 上面tasks写法等同于:--- - hosts: all remote_user: root tasks: - name: Install Httpd yum: name=httpd state=installed - name: Install Vsftpd yum: name=vsftpd state=installed - name: Install Nginx yum: name=nginx state=installed 示例2:通过嵌套子变量创建用户并加入不同的组1)编写playbook[root@ansible PlayBook]# cat testwith01.yml # 示例with_items嵌套子变量 --- - hosts: all remote_user: root tasks: - name: Create New Group group: name={{ item }} state=present with_items: - group1 - group2 - group3 - name: Create New User user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user1', group: 'group1' } - { name: 'user2', group: 'group2' } - { name: 'user3', group: 'group3' } 2)执行playbook并验证# 执行playbook [root@ansible PlayBook]# ansible-playbook testwith01.yml # 验证是否成功创建用户及组 [root@ansible PlayBook]# ansible all -m shell -a 'tail -3 /etc/passwd' 192.168.1.36 | CHANGED | rc=0 >> user1:x:500:500::/home/user1:/bin/bash user2:x:501:501::/home/user2:/bin/bash user3:x:502:502::/home/user3:/bin/bash 192.168.1.32 | CHANGED | rc=0 >> user1:x:1001:1001::/home/user1:/bin/bash user2:x:1002:1002::/home/user2:/bin/bash user3:x:1003:1003::/home/user3:/bin/bash 192.168.1.31 | CHANGED | rc=0 >> user1:x:1002:1003::/home/user1:/bin/bash user2:x:1003:1004::/home/user2:/bin/bash user3:x:1004:1005::/home/user3:/bin/bash 192.168.1.33 | CHANGED | rc=0 >> user1:x:1001:1001::/home/user1:/bin/bash user2:x:1002:1002::/home/user2:/bin/bash user3:x:1003:1003::/home/user3:/bin/bash template之for if通过使用for,if可以更加灵活的生成配置文件等需求,还可以在里面根据各种条件进行判断,然后生成不同的配置文件、或者服务器配置相关等。示例11)编写playbook[root@ansible PlayBook]# cat testfor01.yml # template for 示例 --- - hosts: all remote_user: root vars: nginx_vhost_port: - 81 - 82 - 83 tasks: - name: Templage Nginx Config template: src=nginx.conf.j2 dest=/tmp/nginx_test.conf 2)模板文件编写# 循环playbook文件中定义的变量,依次赋值给port [root@ansible PlayBook]# cat templates/nginx.conf.j2 {% for port in nginx_vhost_port %} server{ listen: {{ port }}; server_name: localhost; } {% endfor %} 3)执行playbook并查看生成结果[root@ansible PlayBook]# ansible-playbook testfor01.yml # 去到一个节点看下生成的结果发现自动生成了三个虚拟主机 [root@linux ~]# cat /tmp/nginx_test.conf server{ listen: 81; server_name: localhost; } server{ listen: 82; server_name: localhost; } server{ listen: 83; server_name: localhost; } 示例21)编写playbook[root@ansible PlayBook]# cat testfor02.yml # template for 示例 --- - hosts: all remote_user: root vars: nginx_vhosts: - web1: listen: 8081 server_name: "web1.example.com" root: "/var/www/nginx/web1" - web2: listen: 8082 server_name: "web2.example.com" root: "/var/www/nginx/web2" - web3: listen: 8083 server_name: "web3.example.com" root: "/var/www/nginx/web3" tasks: - name: Templage Nginx Config template: src=nginx.conf.j2 dest=/tmp/nginx_vhost.conf 2)模板文件编写[root@ansible PlayBook]# cat templates/nginx.conf.j2 {% for vhost in nginx_vhosts %} server{ listen: {{ vhost.listen }}; server_name: {{ vhost.server_name }}; root: {{ vhost.root }}; } {% endfor %} 3)执行playbook并查看生成结果[root@ansible PlayBook]# ansible-playbook testfor02.yml # 去到一个节点看下生成的结果发现自动生成了三个虚拟主机 [root@linux ~]# cat /tmp/nginx_vhost.conf server{ listen: 8081; server_name: web1.example.com; root: /var/www/nginx/web1; } server{ listen: 8082; server_name: web2.example.com; root: /var/www/nginx/web2; } server{ listen: 8083; server_name: web3.example.com; root: /var/www/nginx/web3; } 示例3在for循环中再嵌套if判断,让生成的配置文件更加灵活1)编写playbook[root@ansible PlayBook]# cat testfor03.yml # template for 示例 --- - hosts: all remote_user: root vars: nginx_vhosts: - web1: listen: 8081 root: "/var/www/nginx/web1" - web2: server_name: "web2.example.com" root: "/var/www/nginx/web2" - web3: listen: 8083 server_name: "web3.example.com" root: "/var/www/nginx/web3" tasks: - name: Templage Nginx Config template: src=nginx.conf.j2 dest=/tmp/nginx_vhost.conf 2)模板文件编写# 说明:这里添加了判断,如果listen没有定义的话,默认端口使用8888,如果server_name有定义,那么生成的配置文件中才有这一项。 [root@ansible PlayBook]# cat templates/nginx.conf.j2 {% for vhost in nginx_vhosts %} server{ {% if vhost.listen is defined %} listen: {{ vhost.listen }}; {% else %} listen: 8888; {% endif %} {% if vhost.server_name is defined %} server_name: {{ vhost.server_name }}; {% endif %} root: {{ vhost.root }}; } {% endfor %} 3)执行playbook并查看生成结果[root@ansible PlayBook]# ansible-playbook testfor03.yml # 去到一个节点看下生成的结果发现自动生成了三个虚拟主机 [root@linux ~]# cat /tmp/nginx_vhost.conf server{ listen: 8081; root: /var/www/nginx/web1; } server{ listen: 8888; server_name: web2.example.com; root: /var/www/nginx/web2; } server{ listen: 8083; server_name: web3.example.com; root: /var/www/nginx/web3; } 上面三个示例的图片展示效果例一例二例三作者:别来无恙-出处:https://www.cnblogs.com/yanjieli/p/10969299.html版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
2022年10月26日
103 阅读
0 评论
0 点赞
2022-10-23
管理后台 API 接口文档
管理接口
2022年10月23日
63 阅读
0 评论
1 点赞
2022-10-23
用户信息
1.修改用户信息A.为用户列表中的修改按钮绑定点击事件B.在页面中添加修改用户对话框,并修改对话框的属性C.根据id查询需要修改的用户数据//展示编辑用户的对话框 async showEditDialog(id) { //发送请求根据id获取用户信息 const { data: res } = await this.$http.get('users/' + id) //判断如果添加失败,就做提示 if (res.meta.status !== 200) return this.$message.error('获取用户信息失败') //将获取到的数据保存到数据editForm中 this.editForm = res.data //显示弹出窗 this.editDialogVisible = true } D.在弹出窗中添加修改用户信息的表单并做响应的数据绑定以及数据验证<!-- 对话框主体区域 --> <el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="70px"> <el-form-item label="用户名"> <el-input v-model="editForm.username" disabled></el-input> </el-form-item> <el-form-item label="邮箱" prop="email"> <el-input v-model="editForm.email"></el-input> </el-form-item> <el-form-item label="电话" prop="mobile"> <el-input v-model="editForm.mobile"></el-input> </el-form-item> </el-form> 数据绑定以及验证://控制修改用户对话框的显示与否 editDialogVisible: false, //修改用户的表单数据 editForm: { username: '', email: '', mobile: '' }, //修改表单的验证规则对象 editFormRules: { email: [ { required: true, message: '请输入邮箱', trigger: 'blur' }, { validator: checkEmail, message: '邮箱格式不正确,请重新输入', trigger: 'blur' } ], mobile: [ { required: true, message: '请输入手机号码', trigger: 'blur' }, { validator: checkMobile, message: '手机号码不正确,请重新输入', trigger: 'blur' } ] } E.监听对话框关闭事件,在对话框关闭之后,重置表单<el-dialog title="修改用户" :visible.sync="editDialogVisible" width="50%" @close="editDialogClosed"> editDialogClosed(){ //对话框关闭之后,重置表达 this.$refs.editFormRef.resetFields() } F.在用户点击确定按钮的时候,验证数据成功之后发送请求完成修改editUser() { //用户点击修改表单中的确定按钮之后,验证表单数据 this.$refs.editFormRef.validate(async valid => { if (!valid) return this.$message.error('请填写完整用户信息') //发送请求完成修改用户的操作 const { data: res } = await this.$http.put( 'users/' + this.editForm.id, this.editForm ) //判断如果修改失败,就做提示 if (res.meta.status !== 200) return this.$message.error('修改用户失败') //修改成功的提示 this.$message.success('修改用户成功') //关闭对话框 this.editDialogVisible = false //重新请求最新的数据 this.getUserList() }) } 2.删除用户在点击删除按钮的时候,我们应该跳出提示信息框,让用户确认要进行删除操作。如果想要使用确认取消提示框,我们需要先将提示信息框挂载到vue中。A.导入MessageBox组件,并将MessageBox组件挂载到实例。Vue.prototype.$confirm = MessageBox.confirmB.给用户列表中的删除按钮添加事件,并在事件处理函数中弹出确定取消窗,最后再根据id发送删除用户的请求async removeUserById(id){ //弹出确定取消框,是否删除用户 const confirmResult = await this.$confirm('请问是否要永久删除该用户','删除提示',{ confirmButtonText:'确认删除', cancelButtonText:'取消', type:'warning' }).catch(err=>err) //如果用户点击确认,则confirmResult 为'confirm' //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel' if(confirmResult != "confirm"){ return this.$message.info("已经取消删除") } //发送请求根据id完成删除操作 const {data:res} = await this.$http.delete('users/'+id) //判断如果删除失败,就做提示 if (res.meta.status !== 200) return this.$message.error('删除用户失败') //修改成功的提示 this.$message.success('删除用户成功') //重新请求最新的数据 this.getUserList() } 3.推送代码创建user子分支,并将代码推送到码云A.创建user子分支 git checkout -b userB.将代码添加到暂存区 git add .C.将代码提交并注释 git commit -m '添加完成用户列表功能'D.将本地的user分支推送到码云 git push -u origin userE.将user分支代码合并到master:切换到master git checkout master合并user git merge userF.将本地master分支的代码推送到码云 git push创建rights子分支A.创建rights子分支 git checkout -b rightsB.将本地的rights分支推送到码云 git push -u origin rights4.权限列表A.添加权限列表路由创建权限管理组件(Rights.vue),并在router.js添加对应的路由规则import Rights from './components/power/Rights.vue' ...... path: '/home', component: Home, redirect: '/welcome', children: [ { path: "/welcome", component: Welcome }, { path: "/users", component: Users }, { path: "/rights", component: Rights } ] ...... B.添加面包屑导航在Rights.vue中添加面包屑组件展示导航路径C.显示数据在data中添加一个rightsList数据,在methods中提供一个getRightsList方法发送请求获取权限列表数据,在created中调用这个方法获取数据<el-table :data="rightsList" stripe> <el-table-column type="index"></el-table-column> <el-table-column label="权限名称" prop="authName"></el-table-column> <el-table-column label="路径" prop="path"></el-table-column> <el-table-column label="权限等级" prop="level"> <template slot-scope="scope"> <el-tag v-if="scope.row.level === 0">一级权限</el-tag> <el-tag v-if="scope.row.level === 1" type="success">二级权限</el-tag> <el-tag v-if="scope.row.level === 2" type="warning">三级权限</el-tag> </template> </el-table-column> </el-table> <script> export default { data(){ return { //列表形式的权限 rightsList:[] } }, created(){ this.getRightsList(); }, methods:{ async getRightsList(){ const {data:res} = await this.$http.get('rights/list') //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) return this.$message.error('获取权限列表失败') //如果返回状态正常,将请求的数据保存在data中 this.rightsList = res.data } } } </script> 5.角色列表A.添加角色列表路由添加角色列表子组件(power/Roles.vue),并添加对应的规则path: '/home', component: Home, redirect: '/welcome', children: [ { path: "/welcome", component: Welcome }, { path: "/users", component: Users }, { path: "/rights", component: Rights }, { path: "/roles", component: Roles } ] B.添加面包屑导航在Roles.vue中添加面包屑组件展示导航路径C.显示数据在data中添加一个roleList数据,在methods中提供一个getRoleList方法发送请求获取权限列表数据,在created中调用这个方法获取数据<!-- 角色列表区域 --> <!-- row-key="id" 是2019年3月提供的新特性, if there's nested data, rowKey is required. 如果这是一个嵌套的数据,rowkey 是必须添加的属性 --> <el-table row-key="id" :data="roleList" border> <!-- 添加展开列 --> <el-table-column type="expand"></el-table-column> <el-table-column type="index"></el-table-column> <el-table-column label="角色名称" prop="roleName"></el-table-column> <el-table-column label="角色描述" prop="roleDesc"></el-table-column> <el-table-column label="操作" width="300px"> <template slot-scope="scope"> <el-button size="mini" type="primary" icon="el-icon-edit">编辑</el-button> <el-button size="mini" type="danger" icon="el-icon-delete">删除</el-button> <el-button size="mini" type="warning" icon="el-icon-setting">分配权限</el-button> </template> </el-table-column> </el-table> <script> export default { data(){ return { roleList:[] } },created(){ this.getRoleList(); },methods:{ async getRoleList(){ const {data:res} = await this.$http.get('roles') //如果返回状态为异常状态则报错并返回 // if (res.meta.status !== 200) // return this.$message.error('获取角色列表失败') // //如果返回状态正常,将请求的数据保存在data中 // this.roleList = res.data console.log(res.data) this.roleList = res.data; } } } </script> D.补充说明之前学习过类似的添加角色,删除角色,编辑角色请参照之前编写过的代码还有接口文档完成效果。E.生成权限列表使用三重嵌套for循环生成权限下拉列表<!-- 添加展开列 --> <el-table-column type="expand"> <template slot-scope="scope"> <el-row :class="['bdbottom',i1===0?'bdtop':'']" v-for="(item1,i1) in scope.row.children" :key="item1.id"> <!-- 渲染一级权限 --> <el-col :span="5"> <el-tag> {{item1.authName}} </el-tag> <i class="el-icon-caret-right"></i> </el-col> <!-- 渲染二,三级权限 --> <el-col :span="19"> <!-- 通过for循环嵌套渲染二级权限 --> <el-row :class="[i2===0?'':'bdtop' ]" v-for="(item2,i2) in item1.children" :key="item2.id"> <el-col :span="6"> <el-tag type="success">{{item2.authName}}</el-tag> <i class="el-icon-caret-right"></i> </el-col> <el-col :span="18"> <el-tag type="warning" v-for="(item3) in item2.children" :key="item3.id"> {{item3.authName}} </el-tag> </el-col> </el-row> </el-col> </el-row> </template> </el-table-column> F.美化样式通过设置global.css中的#app样式min-width:1366px 解决三级权限换行的问题,通过给一级权限el-row添加display:flex,align-items:center的方式解决一级权限垂直居中的问题,二级权限也类似添加,因为需要给多个内容添加,可以将这个样式设置为一个.vcenter{display:flex;align-items:center}G.添加权限删除功能给每一个权限的el-tag添加closable属性,是的权限右侧出现“X”图标再给el-tag添加绑定close事件处理函数removeRightById(scope.row,item1.id)removeRightById(scope.row,item2.id)removeRightById(scope.row,item3.id)async removeRightById(role,rightId){ //弹窗提示用户是否要删除 const confirmResult = await this.$confirm('请问是否要删除该权限','删除提示',{ confirmButtonText:'确认删除', cancelButtonText:'取消', type:'warning' }).catch(err=>err) //如果用户点击确认,则confirmResult 为'confirm' //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel' if(confirmResult != "confirm"){ return this.$message.info("已经取消删除") } //用户点击了确定表示真的要删除 //当发送delete请求之后,返回的数据就是最新的角色权限信息 const {data:res} = await this.$http.delete(`roles/${role.id}/rights/${rightId}`) if (res.meta.status !== 200) return this.$message.error('删除角色权限失败') //无需再重新加载所有权限 //只需要对现有的角色权限进行更新即可 role.children = res.data // this.getRoleList(); } H.完成权限分配功能先给分配权限按钮添加事件<el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog">分配权限在showSetRightDialog函数中请求权限树数据并显示对话框async showSetRightDialog() { //当点击分配权限按钮时,展示对应的对话框 this.setRightDialogVisible = true; //获取所有权限的数据 const {data:res} = await this.$http.get('rights/tree') //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) return this.$message.error('获取权限树失败') //如果返回状态正常,将请求的数据保存在data中 this.rightsList = res.data } 添加分配权限对话框,并添加绑定数据setRightDialogVisible 这是一段信息 取 消 确 定 I.完成树形结构弹窗在element.js中引入Tree,注册Tree<!-- 分配权限对话框 --> <el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClose"> <!-- 树形组件 show-checkbox:显示复选框 node-key:设置选中节点对应的值 default-expand-all:是否默认展开所有节点 :default-checked-keys 设置默认选中项的数组 ref:设置引用 --> <el-tree :data="rightsList" :props="treeProps" show-checkbox node-key="id" default-expand-all :default-checked-keys="defKeys" ref="treeRef"></el-tree> <span slot="footer" class="dialog-footer"> <el-button @click="setRightDialogVisible = false">取 消</el-button> <el-button type="primary" @click="allotRights">确 定</el-button> </span> </el-dialog> <script> export default { data() { return { //角色列表数据 roleList: [], //控制分配权限对话框的显示 setRightDialogVisible: false, //权限树数据 rightsList: [], //树形控件的属性绑定对象 treeProps: { //通过label设置树形节点文本展示authName label: 'authName', //设置通过children属性展示子节点信息 children: 'children' }, //设置树形控件中默认选中的内容 defKeys: [], //保存正在操作的角色id roleId:'' } }, created() { this.getRoleList() }, methods: { async getRoleList() { const { data: res } = await this.$http.get('roles') //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) return this.$message.error('获取角色列表失败') //如果返回状态正常,将请求的数据保存在data中 // this.roleList = res.data console.log(res.data) this.roleList = res.data }, async removeRightById(role, rightId) { //弹窗提示用户是否要删除 const confirmResult = await this.$confirm( '请问是否要删除该权限', '删除提示', { confirmButtonText: '确认删除', cancelButtonText: '取消', type: 'warning' } ).catch(err => err) //如果用户点击确认,则confirmResult 为'confirm' //如果用户点击取消, 则confirmResult获取的就是catch的错误消息'cancel' if (confirmResult != 'confirm') { return this.$message.info('已经取消删除') } //用户点击了确定表示真的要删除 //当发送delete请求之后,返回的数据就是最新的角色权限信息 const { data: res } = await this.$http.delete( `roles/${role.id}/rights/${rightId}` ) if (res.meta.status !== 200) return this.$message.error('删除角色权限失败') //无需再重新加载所有权限 //只需要对现有的角色权限进行更新即可 role.children = res.data // this.getRoleList(); }, async showSetRightDialog(role) { //将role.id保存起来以供保存权限时使用 this.roleId = role.id; //获取所有权限的数据 const { data: res } = await this.$http.get('rights/tree') //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) return this.$message.error('获取权限树失败') //如果返回状态正常,将请求的数据保存在data中 this.rightsList = res.data //调用getLeafKeys进行递归,将三级权限添加到数组中 this.getLeafKeys(role, this.defKeys) //当点击分配权限按钮时,展示对应的对话框 this.setRightDialogVisible = true console.log(this.defKeys) }, getLeafKeys(node, arr) { //该函数会获取到当前角色的所有三级权限id并添加到defKeys中 //如果当前节点不包含children属性,则表示node为三级权限 if (!node.children) { return arr.push(node.id) } //递归调用 node.children.forEach(item => this.getLeafKeys(item, arr)) }, setRightDialogClose() { //当用户关闭树形权限对话框的时候,清除掉所有选中状态 this.defKeys = [] }, async allotRights() { //当用户在树形权限对话框中点击确定,将用户选择的 //权限发送请求进行更新 //获取所有选中及半选的内容 const keys = [ ...this.$refs.treeRef.getCheckedKeys(), ...this.$refs.treeRef.getHalfCheckedKeys() ] //将数组转换为 , 拼接的字符串 const idStr = keys.join(',') //发送请求完成更新 const { data: res } = await this.$http.post( `roles/${this.roleId}/rights`, { rids:idStr } ) if (res.meta.status !== 200) return this.$message.error('分配权限失败') this.$message.success("分配权限成功") this.getRoleList(); //关闭对话框 this.setRightDialogVisible = false; } } } </script> 6.分配角色打开Users.vue,完成分配角色的功能A.添加分配角色对话框<!-- 分配角色对话框 --> <el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%"> <div> <p>当前的用户:{{userInfo.username}}</p> <p>当前的角色:{{userInfo.role_name}}</p> <p>分配新角色:</p> </div> <span slot="footer" class="dialog-footer"> <el-button @click="setRoleDialogVisible = false">取 消</el-button> <el-button type="primary" @click="setRoleDialogVisible = false">确 定</el-button> </span> </el-dialog> B.给分配角色按钮添加点击事件,点击之后弹出一个对话框进行角色分配<!-- 分配角色 --> <el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false"> <el-button type="warning" icon="el-icon-setting" size='mini' @click="setRole(scope.row)"></el-button> </el-tooltip> data(){ ...... //控制显示分配角色对话框 setRoleDialogVisible:false, //保存正在操作的那个用户信息 userInfo:{}, //保存所有的角色信息 rolesList:[], //保存用户选中的角色id selectedRoleId:'' }, methods:{ ...... async setRole( userInfo ){ //保存起来以供后续使用 this.userInfo = userInfo; //获取所有的角色信息,以备下拉列表使用 //发送请求根据id完成删除操作 const { data: res } = await this.$http.get('roles') //判断如果删除失败,就做提示 if (res.meta.status !== 200) return this.$message.error('获取角色列表失败') this.rolesList = res.data; //展示分配角色对话框 this.setRoleDialogVisible = true; } } C.在element.js中引入Select,Option,注册Select,Option<!-- 角色选择下拉框 v-model:设置用户选中角色之后的id绑定数据 --> <el-select v-model="selectedRoleId" placeholder="请选择角色"> <!-- :label 显示文本,:value 选中值 --> <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id"> </el-option> </el-select> D.当用户点击对话框中的确定之后,完成分配角色的操作<!-- 分配角色对话框 --> <el-dialog title="分配角色" :visible.sync="setRoleDialogVisible" width="50%" @close="setRoleDialogClosed"> <div> <p>当前的用户:{{userInfo.username}}</p> <p>当前的角色:{{userInfo.role_name}}</p> <p>分配新角色: <!-- 角色选择下拉框 v-model:设置用户选中角色之后的id绑定数据 --> <el-select v-model="selectedRoleId" placeholder="请选择角色"> <!-- :label 显示文本,:value 选中值 --> <el-option v-for="item in rolesList" :key="item.id" :label="item.roleName" :value="item.id"> </el-option> </el-select> </p> </div> <span slot="footer" class="dialog-footer"> <el-button @click="setRoleDialogVisible = false">取 消</el-button> <el-button type="primary" @click="saveRoleInfo">确 定</el-button> </span> </el-dialog> methods:{ ....... async saveRoleInfo(){ //当用户点击确定按钮之后 //判断用户是否选择了需要分配的角色 if(!this.selectedRoleId){ return this.$message.error('请选择需要分配的角色') } //发送请求完成分配角色的操作 const {data:res} = await this.$http.put(`users/${this.userInfo.id}/role`,{rid:this.selectedRoleId}) //判断如果删除失败,就做提示 if (res.meta.status !== 200) return this.$message.error('分配角色失败') this.$message.success('分配角色成功') this.getUserList(); //关闭对话框 this.setRoleDialogVisible = false }, setRoleDialogClosed(){ //当关闭对话框的时候,重置下拉框中的内容 this.selectedRoleId = '' this.userInfo = {} } } 7.将代码推送到码云A.将代码推送到暂存区 git add .B.将代码提交到仓库 git commit -m '完成了权限功能开发'C.将rights分支代码推送到码云 git pushD.将代码合并到mastergit checkout mastergit merge rightsE.将master代码推送到码云git push
2022年10月23日
57 阅读
0 评论
0 点赞
2022-10-23
后台基本布局
打开Home.vue组件,进行布局:<el-container class="home-container"> <!-- 头部区域 --> <el-header>Header<el-button type="info" @click="logout"> 退出 </el-button></el-header> <!-- 页面主体区域 --> <el-container> <!-- 侧边栏 --> <el-aside width="200px">Aside</el-aside> <!-- 主体结构 --> <el-main>Main</el-main> </el-container> </el-container> 默认情况下,跟element-ui组件同名的类名可以帮助我们快速的给对应的组件添加样式,如:.home-container { height: 100%; } .el-header{ background-color:#373D41; } .el-aside{ background-color:#333744; } .el-main{ background-color:#eaedf1; } 2.顶部布局,侧边栏布局<template> <el-container class="home-container"> <!-- 头部区域 --> <el-header> <div> <!-- 黑马logo --> <img src="../assets/heima.png" alt=""> <!-- 顶部标题 --> <span>电商后台管理系统</span> </div> <el-button type="info" @click="logout"> 退出 </el-button> </el-header> <!-- 页面主体区域 --> <el-container> <!-- 侧边栏 --> <el-aside width="200px"> <!-- 侧边栏菜单 --> <el-menu background-color="#333744" text-color="#fff" active-text-color="#ffd04b"> <!-- 一级菜单 --> <el-submenu index="1"> <!-- 一级菜单模板 --> <template slot="title"> <!-- 图标 --> <i class="el-icon-location"></i> <!-- 文本 --> <span>导航一</span> </template> <!-- 二级子菜单 --> <el-menu-item index="1-4-1"> <!-- 二级菜单模板 --> <template slot="title"> <!-- 图标 --> <i class="el-icon-location"></i> <!-- 文本 --> <span>子菜单一</span> </template> </el-menu-item> </el-submenu> </el-menu> </el-aside> <!-- 主体结构 --> <el-main>Main</el-main> </el-container> </el-container> </template> 3.axios请求拦截器后台除了登录接口之外,都需要token权限验证,我们可以通过添加axios请求拦截器来添加token,以保证拥有获取数据的权限在main.js中添加代码,在将axios挂载到vue原型之前添加下面的代码//请求在到达服务器之前,先会调用use中的这个回调函数来添加请求头信息 axios.interceptors.request.use(config=>{ //为请求头对象,添加token验证的Authorization字段 config.headers.Authorization = window.sessionStorage.getItem("token") return config }) 4.请求侧边栏数据<script> export default { data() { return { // 左侧菜单数据 menuList: null } }, created() { // 在created阶段请求左侧菜单数据 this.getMenuList() }, methods: { logout() { window.sessionStorage.clear() this.$router.push('/login') }, async getMenuList() { // 发送请求获取左侧菜单数据 const { data: res } = await this.$http.get('menus') if (res.meta.status !== 200) return this.$message.error(res.meta.msg) this.menuList = res.data console.log(res) } } } </script> 通过v-for双重循环渲染左侧菜单<el-menu background-color="#333744" text-color="#fff" active-text-color="#ffd04b"> <!-- 一级菜单 --> <el-submenu :index="item.id+''" v-for="item in menuList" :key="item.id"> <!-- 一级菜单模板 --> <template slot="title"> <!-- 图标 --> <i class="el-icon-location"></i> <!-- 文本 --> <span>{{item.authName}}</span> </template> <!-- 二级子菜单 --> <el-menu-item :index="subItem.id+''" v-for="subItem in item.children" :key="subItem.id"> <!-- 二级菜单模板 --> <template slot="title"> <!-- 图标 --> <i class="el-icon-location"></i> <!-- 文本 --> <span>{{subItem.authName}}</span> </template> </el-menu-item> </el-submenu> </el-menu> 5.设置激活子菜单样式通过更改el-menu的active-text-color属性可以设置侧边栏菜单中点击的激活项的文字颜色通过更改菜单项模板(template)中的i标签的类名,可以将左侧菜单栏的图标进行设置,我们需要在项目中使用第三方字体图标在数据中添加一个iconsObj:iconsObj: { '125':'iconfont icon-user', '103':'iconfont icon-tijikongjian', '101':'iconfont icon-shangpin', '102':'iconfont icon-danju', '145':'iconfont icon-baobiao' } 然后将图标类名进行数据绑定,绑定iconsObj中的数据:为了保持左侧菜单每次只能打开一个,显示其中的子菜单,我们可以在el-menu中添加一个属性unique-opened或者也可以数据绑定进行设置(此时true认为是一个bool值,而不是字符串) :unique-opened="true"6.制作侧边菜单栏的伸缩功能在菜单栏上方添加一个div <!-- 侧边栏,宽度根据是否折叠进行设置 --> <el-aside :width="isCollapse ? '64px':'200px'"> <!-- 伸缩侧边栏按钮 --> <div class="toggle-button" @click="toggleCollapse">|||</div> <!-- 侧边栏菜单,:collapse="isCollapse"(设置折叠菜单为绑定的 isCollapse 值),:collapse-transition="false"(关闭默认的折叠动画) --> <el-menu :collapse="isCollapse" :collapse-transition="false" ...... 然后给div添加样式,给div添加事件:|||7.在后台首页添加子级路由新增子级路由组件Welcome.vue在router.js中导入子级路由组件,并设置路由规则以及子级路由的默认重定向打开Home.vue,在main的主体结构中添加一个路由占位符制作好了Welcome子级路由之后,我们需要将所有的侧边栏二级菜单都改造成子级路由链接我们只需要将el-menu的router属性设置为true就可以了,此时当我们点击二级菜单的时候,就会根据菜单的index属性进行路由跳转,如: /110,使用index id来作为跳转的路径不合适,我们可以重新绑定index的值为 :index="'/'+subItem.path"8.完成用户列表主体区域新建用户列表组件 user/Users.vue在router.js中导入子级路由组件Users.vue,并设置路由规则当点击二级菜单的时候,被点击的二级子菜单并没有高亮,我们需要正在被使用的功能高亮显示我们可以通过设置el-menu的default-active属性来设置当前激活菜单的index但是default-active属性也不能写死,固定为某个菜单值所以我们可以先给所有的二级菜单添加点击事件,并将path值作为方法的参数@click="saveNavState('/'+subItem.path)"在saveNavState方法中将path保存到sessionStorage中saveNavState( path ){//点击二级菜单的时候保存被点击的二级菜单信息window.sessionStorage.setItem("activePath",path);this.activePath = path;}然后在数据中添加一个activePath绑定数据,并将el-menu的default-active属性设置为activePath最后在created中将sessionStorage中的数据赋值给activePaththis.activePath = window.sessionStorage.getItem("activePath")9.绘制用户列表基本结构A.使用element-ui面包屑组件完成顶部导航路径(复制面包屑代码,在element.js中导入组件Breadcrumb,BreadcrumbItem)B.使用element-ui卡片组件完成主体表格(复制卡片组件代码,在element.js中导入组件Card),再使用element-ui输入框完成搜索框及搜索按钮,此时我们需要使用栅格布局来划分结构(复制卡片组件代码,在element.js中导入组件Row,Col),然后再使用el-button制作添加用户按钮<div> <h3>用户列表组件</h3> <!-- 面包屑导航 --> <el-breadcrumb separator="/"> <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item> <el-breadcrumb-item>用户列表</el-breadcrumb-item> </el-breadcrumb> <!-- 卡片视图区域 --> <el-card> <!-- 搜索与添加区域 --> <el-row :gutter="20"> <el-col :span="7"> <el-input placeholder="请输入内容"> <el-button slot="append" icon="el-icon-search"></el-button> </el-input> </el-col> <el-col :span="4"> <el-button type="primary">添加用户</el-button> </el-col> </el-row> </el-card> </div> 10.请求用户列表数据<script> export default { data() { return { //获取查询用户信息的参数 queryInfo: { query: '', pagenum: 1, pagesize: 2 }, //保存请求回来的用户列表数据 userList:[], total:0 } }, created() { this.getUserList() }, methods: { async getUserList() { //发送请求获取用户列表数据 const { res: data } = await this.$http.get('users', { params: this.queryInfo }) //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) return this.$message.error('获取用户列表失败') //如果返回状态正常,将请求的数据保存在data中 this.userList = res.data.users; this.total = res.data.total; } } } </script> 11.将用户列表数据展示使用表格来展示用户列表数据,使用element-ui表格组件完成列表展示数据(复制表格代码,在element.js中导入组件Table,TableColumn)在渲染展示状态时,会使用作用域插槽获取每一行的数据再使用switch开关组件展示状态信息(复制开关组件代码,在element.js中导入组件Switch)而渲染操作列时,也是使用作用域插槽来进行渲染的,在操作列中包含了修改,删除,分配角色按钮,当我们把鼠标放到分配角色按钮上时希望能有一些文字提示,此时我们需要使用文字提示组件(复制文字提示组件代码,在element.js中导入组件Tooltip),将分配角色按钮包含代码结构如下:<!-- 用户列表(表格)区域 --> <el-table :data="userList" border stripe> <el-table-column type="index"></el-table-column> <el-table-column label="姓名" prop="username"></el-table-column> <el-table-column label="邮箱" prop="email"></el-table-column> <el-table-column label="电话" prop="mobile"></el-table-column> <el-table-column label="角色" prop="role_name"></el-table-column> <el-table-column label="状态"> <template slot-scope="scope"> <el-switch v-model="scope.row.mg_state"></el-switch> </template> </el-table-column> <el-table-column label="操作" width="180px"> <template slot-scope="scope"> <!-- 修改 --> <el-button type="primary" icon="el-icon-edit" size='mini'></el-button> <!-- 删除 --> <el-button type="danger" icon="el-icon-delete" size='mini'></el-button> <!-- 分配角色 --> <el-tooltip class="item" effect="dark" content="分配角色" placement="top" :enterable="false"> <el-button type="warning" icon="el-icon-setting" size='mini'></el-button> </el-tooltip> </template> </el-table-column> </el-table> 12.实现用户列表分页A.使用表格来展示用户列表数据,可以使用分页组件完成列表分页展示数据(复制分页组件代码,在element.js中导入组件Pagination)B.更改组件中的绑定数据<!-- 分页导航区域 @size-change(pagesize改变时触发) @current-change(页码发生改变时触发) :current-page(设置当前页码) :page-size(设置每页的数据条数) :total(设置总页数) --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryInfo.pagenum" :page-sizes="[1, 2, 5, 10]" :page-size="queryInfo.pagesize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> C.添加两个事件的事件处理函数@size-change,@current-changehandleSizeChange(newSize) { //pagesize改变时触发,当pagesize发生改变的时候,我们应该 //以最新的pagesize来请求数据并展示数据 // console.log(newSize) this.queryInfo.pagesize = newSize; //重新按照pagesize发送请求,请求最新的数据 this.getUserList(); }, handleCurrentChange( current ) { //页码发生改变时触发当current发生改变的时候,我们应该 //以最新的current页码来请求数据并展示数据 // console.log(current) this.queryInfo.pagenum = current; //重新按照pagenum发送请求,请求最新的数据 this.getUserList(); } 13.实现更新用户状态当用户点击列表中的switch组件时,用户的状态应该跟随发生改变。A.首先监听用户点击switch组件的事件,并将作用域插槽的数据当做事件参数进行传递<el-switch v-model="scope.row.mg_state" @change="userStateChanged(scope.row)"></el-switch> B.在事件中发送请求完成状态的更改async userStateChanged(row) { //发送请求进行状态修改 const { data: res } = await this.$http.put( `users/${row.id}/state/${row.mg_state}` ) //如果返回状态为异常状态则报错并返回 if (res.meta.status !== 200) { row.mg_state = !row.mg_state return this.$message.error('修改状态失败') } this.$message.success('更新状态成功') }, 14.实现搜索功能添加数据绑定,添加搜索按钮的点击事件(当用户点击搜索按钮的时候,调用getUserList方法根据文本框内容重新请求用户列表数据)当我们在输入框中输入内容并点击搜索之后,会按照搜索关键字搜索,我们希望能够提供一个X删除搜索关键字并重新获取所有的用户列表数据,只需要给文本框添加clearable属性并添加clear事件,在clear事件中重新请求数据即可<el-col :span="7"> <el-input placeholder="请输入内容" v-model="queryInfo.query" clearable @clear="getUserList"> <el-button slot="append" icon="el-icon-search" @click="getUserList"></el-button> </el-input> </el-col> 15.实现添加用户A.当我们点击添加用户按钮的时候,弹出一个对话框来实现添加用户的功能,首先我们需要复制对话框组件的代码并在element.js文件中引入Dialog组件B.接下来我们要为“添加用户”按钮添加点击事件,在事件中将addDialogVisible设置为true,即显示对话框C.更改Dialog组件中的内容<!-- 对话框组件 :visible.sync(设置是否显示对话框) width(设置对话框的宽度) :before-close(在对话框关闭前触发的事件) --> <el-dialog title="添加用户" :visible.sync="addDialogVisible" width="50%"> <!-- 对话框主体区域 --> <el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="70px"> <el-form-item label="用户名" prop="username"> <el-input v-model="addForm.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="addForm.password"></el-input> </el-form-item> <el-form-item label="邮箱" prop="email"> <el-input v-model="addForm.email"></el-input> </el-form-item> <el-form-item label="电话" prop="mobile"> <el-input v-model="addForm.mobile"></el-input> </el-form-item> </el-form> <!-- 对话框底部区域 --> <span slot="footer" class="dialog-footer"> <el-button @click="addDialogVisible = false">取 消</el-button> <el-button type="primary" @click="addDialogVisible = false">确 定</el-button> </span> </el-dialog> D.添加数据绑定和校验规则:data() { //验证邮箱的规则 var checkEmail = (rule, value, cb) => { const regEmail = /^\w+@\w+(\.\w+)+$/ if (regEmail.test(value)) { return cb() } //返回一个错误提示 cb(new Error('请输入合法的邮箱')) } //验证手机号码的规则 var checkMobile = (rule, value, cb) => { const regMobile = /^1[34578]\d{9}$/ if (regMobile.test(value)) { return cb() } //返回一个错误提示 cb(new Error('请输入合法的手机号码')) } return { //获取查询用户信息的参数 queryInfo: { // 查询的条件 query: '', // 当前的页数,即页码 pagenum: 1, // 每页显示的数据条数 pagesize: 2 }, //保存请求回来的用户列表数据 userList: [], total: 0, //是否显示添加用户弹出窗 addDialogVisible: false, // 添加用户的表单数据 addForm: { username: '', password: '', email: '', mobile: '' }, // 添加表单的验证规则对象 addFormRules: { username: [ { required: true, message: '请输入用户名称', trigger: 'blur' }, { min: 3, max: 10, message: '用户名在3~10个字符之间', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 15, message: '用户名在6~15个字符之间', trigger: 'blur' } ], email: [ { required: true, message: '请输入邮箱', trigger: 'blur' }, { validator:checkEmail, message: '邮箱格式不正确,请重新输入', trigger: 'blur'} ], mobile: [ { required: true, message: '请输入手机号码', trigger: 'blur' }, { validator:checkMobile, message: '手机号码不正确,请重新输入', trigger: 'blur'} ] } } } E.当关闭对话框时,重置表单给el-dialog添加@close事件,在事件中添加重置表单的代码methods:{ .... addDialogClosed(){ //对话框关闭之后,重置表达 this.$refs.addFormRef.resetFields(); } } F.点击对话框中的确定按钮,发送请求完成添加用户的操作首先给确定按钮添加点击事件,在点击事件中完成业务逻辑代码methods:{ .... addUser(){ //点击确定按钮,添加新用户 //调用validate进行表单验证 this.$refs.addFormRef.validate( async valid => { if(!valid) return this.$message.error("请填写完整用户信息"); //发送请求完成添加用户的操作 const {data:res} = await this.$http.post("users",this.addForm) //判断如果添加失败,就做提示 if (res.meta.status !== 200) return this.$message.error('添加用户失败') //添加成功的提示 this.$message.success("添加用户成功") //关闭对话框 this.addDialogVisible = false //重新请求最新的数据 this.getUserList() }) } }
2022年10月23日
67 阅读
0 评论
2 点赞
2022-10-19
Shell中整数自增的几种方式
在Shell脚本中,用于while或for循环中经常要涉及到整数自增的情况,下面罗列下可能的方式【方式一】declare -i来声明整数变量root@localhost:~# declare -i x=1 root@localhost:~# x+=1 root@localhost:~# echo $x 2 【 方式二 】使用let命令root@localhost:~# i=1 root@localhost:~# let i+=1 root@localhost:~# echo $i 2 root@localhost:~# i=1 root@localhost:~# let i=$i+1 root@localhost:~# echo $i 2 root@localhost:~# i=1 root@localhost:~# let i++ root@localhost:~# echo $i 2 root@localhost:~# i=1 root@localhost:~# let ++i root@localhost:~# echo $i 2 【 方式三 】使用(())root@localhost:~# i=1 root@localhost:~# ((++i)) root@localhost:~# echo $i 2 root@localhost:~# i=1 root@localhost:~# ((i++)) root@localhost:~# echo $i 2 【 方式四 】使用expr命令root@localhost:~# i=1 root@localhost:~# i=`expr $i + 1` root@localhost:~# echo $i 2 root@localhost:~# i=1 root@localhost:~# i=$(expr $i + 1) root@localhost:~# echo $i 2 【 方式五 】使用$(())root@localhost:~# i=1 root@localhost:~# i=$(($i + 1)) root@localhost:~# echo $i 2 【 方式六 】使用$[]root@localhost:~# i=1 root@localhost:~# i=$[$i + 1] root@localhost:~# echo $i 2 备注1)使用i=$(expr $i + 1)比i=`expr $i + 1`要好些 2)使用(())或者$(())速度要比expr快 3)如果不考虑速度问题,涉及到不同平台的兼容,最好使用expr 4)Bash(sh)上使用比较多的情形:let,expr,(())
2022年10月19日
54 阅读
0 评论
2 点赞
1
...
3
4
5
...
10