基础语法
启动项目
解决node版本问题
export NODE_OPTIONS=--openssl-legacy-provider
npm run serve
启动报错node_modules/.bin/vue-cli-service: Permission denied
在项目目录下执行:chmod 777 node_modules/.bin/vue-cli-service
进入页面自动调接口
安装VUE
入门Demo
定义的Vue只有id是app的才能使用他的变量值name,age,gender。
+ - 方法非id是app的也不可以调用。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>{{name}}</div>
<div>{{age}}</div>
<div>{{gender}}</div>
<button type="button" @click="add()">+</button>
<button type="button" @click="sub()">-</button>
</div>
<div id="web">
<button type="button" @click="add()">+</button>
<button type="button" @click="sub()">-</button>
</div>
<script type="text/javascript">
/*
{{}}:插值表达式
*/
var vue = new Vue({
// el 是vue绑定的页面元素
el: "#app",
// data: 是绑定到页面中的数据
data:{
name:'musen',
age:18,
gender:'男'
},
methods:{
// vue方法中的this代表的vue这个对象
add:function(){
this.age++
},
sub(){
this.age--
}
},
})
</script>
</body>
</html>
指令
常见指令:
v-text:往标签中插入文本值
v-html:往标签中插入html元素
v-pre:在标签中显示原始文本内容,这里还是显示{{info}}
v-show:控制元素的显示和隐藏(根据v-show指定的布尔值例来决定)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
<span>{{user}}</span>
<span v-text="user"></span>
<div v-html="info"></div>
<div v-pre>{{info}}</div>
<input type="text" v-show="status">
</div>
<script type="text/javascript">
/*
常见指令:
v-text:往标签中插入文本值
v-html:往标签中插入html元素
v-pre:在标签中显示原始文本内容,这里还是显示{{info}}
v-show:控制元素的显示和隐藏(根据v-show指定的布尔值例来决定)
*/
var vue = new Vue({
el: '#box',
data: {
user: '小柠檬',
info: '<h1>python</h1>',
status:false
}
})
</script>
</body>
</html>
属性绑定
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style type="text/css">
.hide {
width: 100px;
height: 200px;
background: red;
}
</style>
</head>
<body>
<div id="box">
<div class="hide">1</div>
<hr>
<div :class="cls">2</div>
<hr>
<div :class="[cls]">3</div>
<hr>
/* 这个hide使用的头部hide,并不是对象里的hide*/
<div :class="{hide:status}">4 </div>
<a :href='addr'>淘宝</a>
<a :href="addr2">百度</a>
</div>
<script type="text/javascript">
/**
* v-bind 可以简写为 :
* v-bind:属性值 = 绑定的数据
*/
var vue = new Vue({
el: '#box',
data: {
cls: 'hide',
status: true,
addr:'http://www.taobao.com',
addr2:'http://www.baidu.com',
}
})
</script>
</body>
</html>
事件绑定
点击事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
<button type="button" @click="sub()">-</button>
<input type="text" :value="number">
<button type="button" v-on:click="add()">+</button>
</div>
<script type="text/javascript">
/**
* v-on:可以简写为 @
* v-on:事件名 = 执行的函数
*/
var vue = new Vue({
el: '#box',
data: {
number:0
},
methods:{
add:function(){
this.number++
},
// 定义方法时:将function省略,和上面定义的add方法没有区别
sub(){
this.number--
}
}
})
</script>;
</body>
</html>
条件判断
if
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<span v-text="user"></span>
<span v-text="score"></span>
<span v-if="score>80">优秀</span>
<span v-else-if="score>60">良好</span>
<span v-else=>不及格</span>
</div>
</div>
<script type="text/javascript">
/*
0-60 不及格
60--80 良好
80-100 优秀
vue中的条件判断语句:根据不同的条件显示不同的内容
v-if
v-else-if
v-else:
*/
var vue = new Vue({
el:"#app",
data:{
stus:[
{name:'木森',score:100},
{name:'盼盼达',score:78},
{name:'好好先生',score:89},
{name:'枫',score:99},
],
score:88,
user:'musen'
}
})
</script>
</body>
</html>
for
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<table border="" cellspacing="" cellpadding="">
<tr>
<th>姓名</th>
<th>成绩</th>
<th>等级</th>
</tr>
<!-- // key 需要指定一个数据中唯一的值 -->
<tr v-for='item in stus' :key='item.score'>
<td>{{item.name}}</td>
<td>{{item.score}}</td>
<td v-if="item.score>80">优秀</td>
<td v-else-if="item.score>60">良好</td>
<td v-else>不及格</td>
</tr>
</table>
</div>
<script type="text/javascript">
/*
0-60 不及格
60--80 良好
80-100 优秀
v-for:循环语句,记得加:key='item.唯一的属性'
*/
var vue = new Vue({
el: "#app",
data: {
stus: [{
name: '木森',
score: 100
},
{
name: '盼盼达',
score: 100
},
{
name: '好好先生',
score: 89
},
{
name: '枫',
score: 99
},
],
score: 88,
user: 'musen'
}
})
</script>
</body>
</html>
数据双向绑定v-model
前端改了,代码的值也改了。代码的值改了,前端也改了。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="info">
<div>{{info}}</div>
</div>
<script type="text/javascript">
var vue = new Vue({
el:'#app',
data:{
info: 'python'
}
})
</script>
</body>
</html>
作业
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<table border="" cellspacing="" cellpadding="">
<tr>
<th>ID</th>
<th>接口名</th>
<th>测试人员</th>
<th>项目名</th>
<th>项目ID</th>
<th>描述信息</th>
<th>创建时间</th>
<th>用例数</th>
<th>操作</th>
</tr>
<tr :key='item.id' v-for="item in lists">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.tester}}</td>
<td>{{item.project}}</td>
<td>{{item.project_id}}</td>
<td>{{item.desc}}</td>
<td>{{item.create_time}}</td>
<td>{{item.testcases}}</td>
<td><button @click="del(item.id)">删除</button></td>
</tr>
</table>
<hr>
</div>
<script type="text/javascript">
/*
给绑定的事件传递参数
*/
var vue = new Vue({
el: '#app',
data: {
lists: [{
"id": 1,
"name": "登录接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "登录接口",
"create_time": "2019-11-06 14:50:30",
"testcases": 9,
},
{
"id": 2,
"name": "注册接口_自动化测试平台项目",
"tester": "可可",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "自动化测试平台项目, 注册接口",
"create_time": "2019-11-06 14:51:00",
"testcases": 0,
},
{
"id": 3,
"name": "创建项目接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "这是自动化测试平台创建项目接口",
"create_time": "2019-11-06 14:51:45",
"testcases": 1,
},
{
"id": 4,
"name": "注册接口_前程贷P2P金融项目",
"tester": "小可可",
"project": "前程贷P2P金融项目",
"project_id": 2,
"desc": "",
"create_time": "2019-11-06 14:52:22",
"testcases": 0,
},
{
"id": 5,
"name": "登录接口_前程贷P2P金融项目",
"tester": "柠檬小姐姐",
"project": "前程贷P2P金融项目",
"project_id": 2,
"desc": "",
"create_time": "2019-11-06 14:52:40",
"testcases": 0,
},
{
"id": 6,
"name": "查看项目列表接口_前程贷P2P金融项目",
"tester": "柠檬小姐姐",
"project": "前程贷P2P金融项目",
"project_id": 2,
"desc": "",
"create_time": "2019-11-06 17:22:50",
"testcases": 1,
}
],
},
methods:{
del:function(id){
// 删除数据的方法
console.log('删除一行数据',id)
// 从lists中删除对应的数据
// 通过过滤的方式删除数组中的数据
// this.lists= this.lists.filter(function(item,index){
// return item.id !=id
// })
// 查找要删除的数据索引值
let index = this.lists.findIndex(function(item){
return item.id==id
})
// 根据数组的索引去删除数组中对应的数据
this.lists.splice(index,1)
}
}
})
</script>
</body>
</html>
高级特性
修饰符
事件修饰符
事件冒泡就是指父元素和子元素有相同的事件,当触发子元素事件时,会向上冒泡,同时也会触发父元素事件。
vue事件修饰符: stop: 阻止事件冒泡 prevent:阻止元素默认的事件行为可以两个一起用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div @click="work3">
<div @click="work2()">
<button type="button" @click.stop="work1()">按钮1</button>
<a href="http://www.baidu.com" @click.prevent.stop="handleA()">百度</a>
</div>
</div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {},
methods: {
work1: function() {
console.log('work1-----')
},
work2: function() {
console.log('work2-----')
},
work3: function() {
console.log('---work30000')
},
handleA:function(){
console.log('点击了a标签')
}
}
})
</script>
</body>
</html>
按键修饰符
监听操作键盘的按键内容来触发某些函数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<form action="">
账号:<input type="text" v-model="loginForm.user"> <br>
密码:<input type="password" v-model="loginForm.pwd" @keyup.enter="login_btn()"><br>
<button type="button" @click="login_btn()">点击登录</button>
</form>
</div>
<script type="text/javascript">
Vue.config.keyCodes.a = 65
var vm = new Vue({
el: '#app',
data: {
loginForm:{
user:'',
pwd:''
}
},
methods: {
login_btn:function(){
alert('点击了登录')
}
}
})
</script>
</body>
</html>
表单输入绑定,配合数据双向绑定使用
.lazy - 取代input监听change事件不会是输入一个数字就数据同步,变成回车或者脱离输入框才同步
.number - 输入字符串转为有效的数字
.trim - 输入首尾空格过滤
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<form action="">
账号:<input type="text" v-model.lazy="loginForm.user"> <br>
密码:<input type="password" v-model.trim="loginForm.pwd" @keyup.enter="login_btn()"><br>
验证码:<input type="text" v-model.number="loginForm.user"> <br>
<button type="button" @click="login_btn()">点击登录</button>
</form>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
loginForm:{
user:'',
pwd:''
}
},
methods: {
login_btn:function(){
alert('点击了登录')
}
}
})
</script>
</body>
</html>
计算属性
计算属性:通过computed进行定义
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h3>产品名:{{name}}</h3>
<h3>单价:{{price}}</h3>
<h3>
数量:
<button type="button" @click="sub()">-</button>
<span>{{num}}</span>
<button type="button" v-on:click="add()">+</button>
</h3>
<hr>
<!-- <h2>合计金额:{{sum_func()}}</h2> -->
<h2>合计金额:{{price_result}}</h2>
</div>
<script type="text/javascript">
var vue = new Vue({
el: '#app',
data: {
name: '小柠檬',
price: 8,
num: 0,
},
methods: {
add: function() {
this.num++
},
// 定义方法时:将function省略,和上面定义的add方法没有区别
sub() {
this.num--
},
// sum_func(){
// return this.price * this.num
// }
},
// 定义计算属性,
computed: {
price_result: function() {
return this.price * this.num
}
},
})
</script>
</body>
</html>
侦听器
watch 监测数据的值是否发生变化
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="box">
数量:
<button type="button" @click="sub()">-</button>
<input type="text" :value="number">
<button type="button" v-on:click="add()">+</button>
<hr>
<input type="text" v-model.lazy="name">
</div>
<script type="text/javascript">
/**
* 侦听器 监测数据的值是否发生变化
*/
var vue = new Vue({
el: '#box',
data: {
number:0,
name :'musen'
},
methods:{
add:function(){
this.number++
},
// 定义方法时:将function省略,和上面定义的add方法没有区别
sub(){
this.number--
}
},
// 侦听器
watch:{
number:function(value){
console.log('监测到number的值发生了变化',value)
if (value<0){
alert('数量不能少于0')
this.number=0
}
},
name:function(value){
console.log('监测到name的值发生了变化',value)
}
}
})
</script>;
</body>
</html>
过滤器
保留两位小数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h3>产品名:{{name}}</h3>
<h3>单价:{{price | dubboFloat}}</h3>
<h3>
数量:
<button type="button" @click="sub()">-</button>
<span>{{num}}</span>
<button type="button" v-on:click="add()">+</button>
</h3>
<hr>
<!-- <h2>合计金额:{{sum_func()}}</h2> -->
<h2>合计金额:{{sum | dubboFloat}}</h2>
<h2>{{info| upper}}</h2>
</div>
<script type="text/javascript">
var vue = new Vue({
el: '#app',
data: {
name: '小柠檬',
price: 8,
num: 0,
info:'python'
},
methods: {
add: function() {
this.num++
},
// 定义方法时:将function省略,和上面定义的add方法没有区别
sub() {
this.num--
}
},
// 定义计算属性,
computed: {
sum: function() {
return this.price * this.num
}
},
// 定义过滤器
/*
主要用来对页面显示的数据,做一些格式化的处理
*/
filters:{
dubboFloat:function(value){
// 格式化数值,显示两位小数
return value.toFixed(2)
},
upper:function(value){
// 将字符串中的小写字符转换为大写
return value.toUpperCase()
}
}
})
</script>
</body>
</html>
全局组件
1、定义组件:
全局组件:使用Vue.component定义组件
2、在页面中使用组件 <组件名></组件名>
3、组件名的命名注意点:
如果定义组件的之后组件名,使用的是大驼峰的命名风格,在页面中引用组件的时候 ,
要使用中划线命名风格(xx-xx)
4、定义的全局组件 可以再其他的组件中直接引用
注意点:在组件的版本中引用其他组件可以使用大驼峰命名法
建议:组件命名尽量使用中划线命名风格 xxx-xxx
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<style>
.title {
background: #0000FF;
height: 80px;
margin-bottom: 5px;
}
.content {
background: #FAEBD7;
height: 1000px;
}
.footer {
margin-top: 10px;
background: #9ACD32;
height: 100px;
}
.left {
width: 15%;
background: #00FFFF;
height: 100%;
float: left;
margin-right: 0.5%;
}
.right {
width: 84%;
background: #FF0000;
height: 100%;
float: left;
}
</style>
</head>
<body>
<div id="app">
<!-- 顶部菜单栏 -->
<div class="title">
<header-con></header-con>
<!-- <HeaderCon></HeaderCon> -->
</div>
<!-- 中间的主题内容 -->
<div class="content">
<!-- 左边的菜单 -->
<div class="left">
<left-con></left-con>
</div>
<!-- 右边的内容 -->
<div class="right">
<right-con></right-con>
</div>
</div>
</div>
<script type="text/javascript">
// 全局的组件定义:
Vue.component('header-con', {
template: `
<div><h1>这个是页面头部组件</h1></div>
`
})
Vue.component('HeaderCon', {
template: `
<div><h1>这个是页面头部组件9999</h1></div>
`
})
Vue.component('left-con', {
template: `
<div> <h1>这个是页面左边内容的组件</h1>
<my-button></my-button></div>
`
})
Vue.component('right-con', {
template: `
<div>
<h1>这个是页面左边内容的组件</h1>
<my-button></my-button>
<MusenButton></MusenButton>
</div>
`
})
Vue.component('my-button',{
template:`
<button>mybutton</button>
`
})
Vue.component('MusenButton',{
template:`
<button>musen</button>
`
})
var vm = new Vue({
el: '#app',
data: {
},
})
</script>
</body>
</html>
局部组件
// 定义局部组件:只能在父组件(页面)中使用,不能在其他的组件中引用
// 组件名要使用引号包起来,不然会报语法错误
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<header-con></header-con>
<left-con></left-con>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
},
// 定义局部组件:只能在父组件(页面)中使用,不能在其他的组件中引用
components: {
// 组件名要使用引号包起来,不然会报语法错误-
'header-con': {
template: `
<button>按钮1</button>
`
},
'left-con': {
template: `
<div>
<span>python666</span>
</div>
`
}
}
})
</script>
</body>
</html>
组件中的数据和方法定义
1、组件中的数据 同样放在data字段中,
注意点:组件中的data是一个方法(函数),数据为函数return返回出来的内容
注意点:组件的模板内容,必须有一个根节点(最外层只有一个标签)
2、组件中的方法:定义在methods中
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<header-con></header-con>
<left-con></left-con>
</div>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
name: '木森',
age: 99
},
// 定义局部组件:只能在父组件(页面)中使用,不能在其他的组件中引用
components: {
// 组件名要使用引号包起来,不然会报语法错误-
'header-con': {
template: `
<button @click='handleClick'>按钮1</button>
`,
methods: {
handleClick: function() {
console.log('----handleClick-----')
}
},
},
'left-con': {
template: `
<div>
<h2>名字{{name}}</h2>
<h2>名字{{age}}</h2>
<h2>计算属性sum:{{sum}}</h2>
<span>python666</span>
</div>
`,
// 组件中的数据 同样放在data字段中,
// 注意点:组件中的data是一个方法(函数),数据为函数return返回出来的内容
data: function() {
return {
name: 'musen',
age: 18
}
},
computed: {
sum: function() {
return this.name + this.age
}
},
watch: {
age: function(val) {
console.log(val)
}
}
}
}
})
</script>
</body>
</html>
父组件往子组件中传递数据
场景:父组件中引用子组件的时候,给子组件传递数据
实现步骤:
1、在子组件中通过props来定义接收数据的参数名(子组件的属性名)
2、父组件在引用子组件时,通过属性绑定的形式传递参数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<data-views :lists="cases1"></data-views>
<data-views :lists="cases2"></data-views>
</div>
<script type="text/javascript">
// 子组件
Vue.component('data-views', {
// 指定父组件中引用该组件时,传递数据的参数名,使用props
props:['lists'],
template: `
<table border="" cellspacing="" cellpadding="">
<tr>
<th>ID</th>
<th>接口名</th>
<th>测试人员</th>
<th>项目名</th>
<th>项目ID</th>
<th>描述信息</th>
<th>创建时间</th>
<th>用例数</th>
</tr>
<tr :key='item.id' v-for="item in lists">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.tester}}</td>
<td>{{item.project}}</td>
<td>{{item.project_id}}</td>
<td>{{item.desc}}</td>
<td>{{item.create_time}}</td>
<td>{{item.testcases}}</td>
</tr>
</table>
`
})
// 父组件
var vm = new Vue({
el: '#app',
data: {
cases1:[{
"id": 1,
"name": "登录接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "登录接口",
"create_time": "2019-11-06 14:50:30",
"testcases": 9,
}],
cases2:[
{
"id": 3,
"name": "创建项目接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "这是自动化测试平台创建项目接口",
"create_time": "2019-11-06 14:51:45",
"testcases": 1,
}
]
}
})
</script>
</body>
</html>
子组件往父组件中传递数据
场景:一般子组件在操作某个数据之后,要将数据传递给父组件,就要用到子组件给父组件传递数据,
实现步骤:
1、在子组件中通过$emit来触发一个自定义的事件,并把数据传递过去
$emit('自定义的事件名',传递的数据)
2、父组件在引用子组件时,通过事件监听机制,来监听子组件自定义的事件,并指定方法去处理这个事件
@自定义的事件名 = 处理事件的方法($event)
3、在处理事件的方法中接收数据,并处理
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<data-views :lists="cases1" @run='recvData($event)'></data-views>
<hr>
<div>
<h3>执行的用例信息:{{info}}</h3>
</div>
</div>
<script type="text/javascript">
Vue.component('data-views', {
// 指定父组件中引用该组件时,传递数据的参数名
props:['lists'],
template: `
<table border="" cellspacing="" cellpadding="">
<tr>
<th>ID</th>
<th>接口名</th>
<th>测试人员</th>
<th>项目名</th>
<th>项目ID</th>
<th>描述信息</th>
<th>创建时间</th>
<th>用例数</th>
<th>操作</th>
</tr>
<tr :key='item.id' v-for="item in lists">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.tester}}</td>
<td>{{item.project}}</td>
<td>{{item.project_id}}</td>
<td>{{item.desc}}</td>
<td>{{item.create_time}}</td>
<td>{{item.testcases}}</td>
<th><button @click="$emit('run',item)">执行</button></th>
</tr>
</table>
`
})
var vm = new Vue({
el: '#app',
data: {
cases1:[{
"id": 1,
"name": "登录接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "登录接口",
"create_time": "2019-11-06 14:50:30",
"testcases": 9,
},
{
"id": 2,
"name": "注册接口_自动化测试平台项目",
"tester": "可可",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "自动化测试平台项目, 注册接口",
"create_time": "2019-11-06 14:51:00",
"testcases": 0,
}],
info:''
},
methods:{
// 父组件中监听到子组件触发的事件之后,接收数据的方法
recvData:function(value){
console.log(value)
this.info = value
}
}
})
</script>
</body>
</html>
插槽
插槽:定义组件时,给父组件预留填充内容(html)的插口(空白)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<data-views :lists="cases1" @run='recvData($event)'>
<template>
<span>项目名:柠檬班</span>
<span>版本:V1.0</span>
<span>时间:2021-04-02</span>
</template>
</data-views>
<hr>
<div>
<h3>执行的用例信息:{{info}}</h3>
</div>
</div>
<script type="text/javascript">
Vue.component('data-views', {
// 指定父组件中引用该组件时,传递数据的参数名
props: ['lists'],
template: `
<div>
<h1>-------------子组件---------------</h1>
//插槽
<slot></slot>
<table border="" cellspacing="" cellpadding="">
<tr>
<th>ID</th>
<th>接口名</th>
<th>测试人员</th>
<th>项目名</th>
<th>项目ID</th>
<th>描述信息</th>
<th>创建时间</th>
<th>用例数</th>
<th>操作</th>
</tr>
<tr :key='item.id' v-for="item in lists">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.tester}}</td>
<td>{{item.project}}</td>
<td>{{item.project_id}}</td>
<td>{{item.desc}}</td>
<td>{{item.create_time}}</td>
<td>{{item.testcases}}</td>
<th><button @click="$emit('run',item)">执行</button></th>
</tr>
</table>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
cases1: [{
"id": 1,
"name": "登录接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "登录接口",
"create_time": "2019-11-06 14:50:30",
"testcases": 9,
},
{
"id": 2,
"name": "注册接口_自动化测试平台项目",
"tester": "可可",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "自动化测试平台项目, 注册接口",
"create_time": "2019-11-06 14:51:00",
"testcases": 0,
}
],
info: ''
},
methods: {
// 父组件中监听到子组件触发的事件之后,接收数据的方法
recvData: function(value) {
console.log(value)
this.info = value
}
}
})
</script>
</body>
</html>
具名插槽
插槽增加name属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<data-views :lists="cases1" @run='recvData($event)'>
<template slot="title">
<span>项目名:柠檬班</span>
<span>版本:V1.0</span>
<span>时间:2021-04-02</span>
</template>
<template slot='body'>
<h4>python+vue真好用</h4>
</template>
</data-views>
<hr>
<div>
<h3>执行的用例信息:{{info}}</h3>
</div>
</div>
<script type="text/javascript">
Vue.component('data-views', {
// 指定父组件中引用该组件时,传递数据的参数名
props: ['lists'],
template: `
<div>
<slot name='title'></slot>
<h1>-------------子组件---------------</h1>
<slot name='body'></slot>
<table border="" cellspacing="" cellpadding="">
<tr>
<th>ID</th>
<th>接口名</th>
<th>测试人员</th>
<th>项目名</th>
<th>项目ID</th>
<th>描述信息</th>
<th>创建时间</th>
<th>用例数</th>
<th>操作</th>
</tr>
<tr :key='item.id' v-for="item in lists">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.tester}}</td>
<td>{{item.project}}</td>
<td>{{item.project_id}}</td>
<td>{{item.desc}}</td>
<td>{{item.create_time}}</td>
<td>{{item.testcases}}</td>
<th><button @click="$emit('run',item)">执行</button></th>
</tr>
</table>
</div>
`
})
var vm = new Vue({
el: '#app',
data: {
cases1: [{
"id": 1,
"name": "登录接口_自动化测试平台项目",
"tester": "可优",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "登录接口",
"create_time": "2019-11-06 14:50:30",
"testcases": 9,
},
{
"id": 2,
"name": "注册接口_自动化测试平台项目",
"tester": "可可",
"project": "自动化测试平台项目",
"project_id": 1,
"desc": "自动化测试平台项目, 注册接口",
"create_time": "2019-11-06 14:51:00",
"testcases": 0,
}
],
info: ''
},
methods: {
// 父组件中监听到子组件触发的事件之后,接收数据的方法
recvData: function(value) {
console.log(value)
this.info = value
}
}
})
</script>
</body>
</html>
接口
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
# 测试数据
user_info = {"user": 'python01', 'pwd': 'lemonban'}
project_data = {"code": "1",
"data": [{"title": "前程贷", "id": "1001"},
{"title": "智慧金融", "id": "1002"},
{"title": "生鲜到家", "id": "1003"},
{"title": "柠檬班app", "id": "1004"}],
"msg": "四个项目",
}
# 接口数据
interface_data = {
"1001": {"code": "1",
"data": [{"name": "前程贷登录1001"},
{"name": "前程贷注册1001"}],
"msg": "2个接口", },
"1002": {"code": "1",
"data": [{"name": "智慧-登录1002"},
{"name": "智慧-注册1002"},
{"name": "智慧-贷款1004"}, ],
"msg": "3个接口", },
"1003": {"code": "1",
"data": [{"name": "生鲜-登录1003"},
{"name": "生鲜-注册1003"},
{"name": "生鲜下单1003"}, ],
"msg": "3个接口", },
"1004": {"code": "1",
"data": [{"name": "app登录1004"},
{"name": "app注册1004"},
{"name": "app报名1004"},
{"name": "app缴费1004"},
],
"msg": "4个接口", },
}
# 登录
@app.route('/api/user/login', methods=['post'])
def login():
"""
接口地址:http://127.0.0.1:5000/api/user/login
请求方法:post
参数: {user:账号,pwd:密码}
参数类型:表单 、json都支持
返回:该项目的所有接口
"""
data = request.form or request.json
# 判断账号,密码是否正确
if user_info.get('user') == data.get('user') and user_info.get('pwd') == data.get('pwd'):
return jsonify({'code': "1", "data": None, "msg": "成功"})
else:
return jsonify({'code': "0", "data": None, "msg": "密码有误"})
# 获取项目列表
@app.route('/api/projects', methods=['get'])
def pro_list():
"""
接口地址:http://127.0.0.1:5000/api/projects
请求方法:get
参数:无
返回所有的项目
:return:
"""
return jsonify(project_data)
# 获取接口列表
@app.route('/api/interface', methods=['get'])
def interface():
"""
接口地址:http://127.0.0.1:5000/api/interface
请求方法:get
参数: id(项目的id)
参数类型:查询字符串
返回:该项目的所有接口
"""
inter_id = request.args.get('id')
if inter_id:
res_data = interface_data.get(inter_id)
if res_data:
return jsonify(res_data)
else:
return jsonify({"code": "0", "data": None, "msg": "没有该项目"})
else:
return jsonify({"code": "0", "data": None, "msg": "请求参数不能为空"})
if __name__ == '__main__':
cors = CORS(app)
app.run(debug=True)
前端发送请求,使用axios
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios发送一个get请求
const pro = axios.get('http://127.0.0.1:5000/api/projects11')
// 此处的pro并不是接口的响应 它是一个Promise对象(异步事件的对象)
console.log(pro)
// ----------------获取接口返回的数据(请求成功)-------------------
// response是自己定义的变量,接收返回内容
pro.then(function(response){
// 这个回调函数的中的response才是接口返回的响应对象
console.log('接口的响应数据:',response)
// 获取响应体
console.log(response.data)
//获取响应的状态码
console.log((response.status))
})
console.log('999999999999999999')
// ----------------获取接口返回的数据(请求失败:返回的状态码是4,5系列的)-------------------
pro.catch(function(error) {
// 获取请求失败的响应对象
// error是自己定义的变量,接收返回内容
console.log(error.response)
let response = error.response
console.log(response.data)
console.log(response.headers)
})
// 自定义HTTP状态码错误的范围
const pro = axios.get('http://127.0.0.1:5000/api/projects11', {
validateStatus: function(status) {
// 指定HTTP状态码错误的范围
// return status < 500;
return true
}
})
pro.then(function(response) {
// 这个回调函数的中的response才是接口返回的响应对象
console.log('接口的响应数据:', response)
// 获取响应体
console.log(response.data)
//获取响应的状态码
console.log((response.status))
})
</script>
</body>
</html>
axios设置公用属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios创建请求对象,类似python实例化类
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function(status) {
return true
},
// 指定基本的url地址,后面地址会自动拼接上
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
headers: {
'X-Custom-Header': 'foobar'
},
})
//使用请求对象,url自动拼接baseURL
request.get('/api/projects').then(function(response) {
console.log(response.status)
})
request.get('/api/projects11').then(function(response) {
console.log(response.status)
})
</script>
</body>
</html>
get入参
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios创建请求对象
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function(status) {
return true
},
// 指定基本的url地址
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
headers: {
'X-Custom-Header': 'foobar'
},
})
//get方法传递查询字符串参数
// 方式一 :通过params传递 get('url',{params:{key:value,key:value}})
request.get('/api/interface',{
params:{id:1001}
}).then(function(response) {
console.log(response.status)
console.log(response.data)
})
// 方式二:直接在url后面拼接 url?key=value&key=value
request.get('/api/interface?id=1001').then(function(response) {
console.log(response.status)
console.log(response.data)
})
</script>
</body>
</html>
post入参
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios创建请求对象
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function(status) {
return true
},
// 指定基本的url地址
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
headers: {
'X-Custom-Header': 'foobar'
},
})
// 发送post请求
// 第一种,json格式的参数传递: content-Type:application/json
request.post('/api/user/login',{user:'python01',pwd:'lemonban'}).then(function(response){
console.log(response.data)
console.log(response.status)
})
// 第二种,表单参数传递:content-Type:application/x-www-form-urlencoded format
// 创建表单参数对象
const formData = new URLSearchParams();
formData.append('user', 'python01')
formData.append('pwd', 'lemonban')
request.post('/api/user/login', formData).then(function(response) {
console.log(response.data)
console.log(response.status)
})
</script>
</body>
</html>
axios请求拦截器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios创建请求对象
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function(status) {
return true
},
// 指定基本的url地址
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
})
// 添加请求拦截器:每次请求接口都会自动调用
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 接口需要鉴权:token
console.log('请求拦截器:',config)
return config;
});
// 添加响应拦截器:每次请求完之后,A返回响应数据之前会调用。
request.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data;
}, function (error) {
// 对响应错误做点什么
return error.response;
});
//使用请求对象
request.get('/api/projects').then(function(data) {
console.log(data)
})
</script>
</body>
</html>
async和await发起请求
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<script type="text/javascript">
// 使用axios创建请求对象
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function(status) {
return true
},
// 指定基本的url地址
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
})
// 添加请求拦截器:每次请求接口都会自动调用
request.interceptors.request.use(function(config) {
// 在发送请求之前做些什么
// 接口需要鉴权:token
console.log('请求拦截器:', config)
return config;
});
// 添加响应拦截器:每次请求完之后,A返回响应数据之前会调用。
request.interceptors.response.use(function(response) {
// 对响应数据做点什么
return response;
}, function(error) {
// 对响应错误做点什么
return error.response;
});
// 使用请求对象
// 注意点:await 和只能在通过async定义的函数中使用
async function getProjec() {
const response = await request.get('/api/projects')
console.log(response)
}
getProjec()
</script>
</body>
</html>
登录Demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 引入/element-ui -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<!-- 引入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<style>
#app {
width: 300px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#buttonLogin {
width: 200px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div id="app">
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="账号">
<el-input v-model="formLogin.user"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="formLogin.pwd" type='password'></el-input>
</el-form-item>
<el-form-item>
<el-button id='buttonLogin' type="primary" @click="loginHandle">点击登录</el-button>
</el-form-item>
</el-form>
</div>
</body>
<script type="text/javascript">
const request = axios.create({
// 指定请求HTTP响应码错误范围
validateStatus: function (status) {
return true
},
// 指定基本的url地址
baseURL: "http://127.0.0.1:5000",
// 添加固定的请求头
headers: {
'X-Custom-Header': 'foobar'
},
})
// 添加请求拦截器:每次请求接口都会自动调用
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 接口需要鉴权:token
console.log('请求拦截器:', config)
token = window.sessionStorage.getItem('token')
console.log(token)
// 在请求头中添加token
return config;
});
var vm = new Vue({
el: '#app',
data: {
formLogin: {
user: '',
pwd: ''
}
},
methods: {
// 点击登录之后,处理登录的方法
loginHandle: async function () {
// 请求登录接口
const response = await request.post('/api/user/login', this.formLogin)
if (response.data.code === '1') {
alert('登录成功')
window.sessionStorage.setItem('token', 'asasasasasas')
} else {
alert(response.data.msg)
}
}
}
})
</script>
</html>
前端路由
单页面应用spa
第一步:定义展示页面内容的组件
第二步:定义路由规则
第三步:初始化路由对象
第四步:将路由对象绑定到vue实例中去
第五步:在根组件中定义路由视图的展示位置
注意点:如果在html页面中使用vuerouter 在使用script引入的时候 一定要放在vue下面
路由配置:
path:指定路由路径
name:给路由命名
redirect:指定路由重定向的路径
component:指定该路由渲染的组件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 引入vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 引入vue-router -->
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js" type="text/javascript"></script>
</head>
<body>
<div id="app">
<router-link to="/home">主页跳转按钮</router-link>
<router-link to="/login">登录跳转按钮</router-link>
<!-- 路由视图,第五步:在根组件中定义路由视图的展示位置 -->
<router-view></router-view>
</div>
<script type="text/javascript">
// 首页,第一步:定义展示页面内容的组件
const home = {
template: `<h1>这个是项目首页</h1>
`
}
// 登录页面,第一步:定义展示页面内容的组件
const login = {
template: `
<h1>这个是项目登录页面</h1>
`
}
// 第三步:初始化路由对象
const router = new VueRouter({
//通过路由routes指定路由规则,第二步:定义路由规则
routes: [{
path: '/',
// 设置路由重定向
redirect: '/login'
},
{
path: '/login',
component: login,
// 给路由设置名字
name:'login'
},
{
path:'/home',
component:home
}
]
})
var vm = new Vue({
el: '#app',
// 第四步:将路由对象绑定到vue实例中去
router
});
</script>
</body>
</html>
编程式路由和声明式路由
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.js" type="text/javascript"></script>
<!-- 引入vue -->
</head>
<body>
<div id="app">
<div class="box">
<!-- 这个路由访问的方式,叫做声明式路由 -->
<router-link to="/home"><button type="button">用户页面</button></router-link>
<router-link to="/login"><button type="button">登录页面</button> </router-link>
<router-link to="/user/1"><button type="button">用户</button> </router-link>
<router-link to="/user/2"><button type="button">用户</button> </router-link>
<input type="text" v-model="number">
<button type="button" @click="handle">提交数据</button>
</div>
<router-view></router-view>
</div>
<script type="text/javascript">
// 首页
const home = {
template: `<h1>这个是项目首页</h1>
`
}
const cases = {
template: `<h1>case页面</h1>
`
}
// 登录页面
const login = {
template: `
<h1>这个是项目登录页面</h1>
`
}
const user = {
template: `
<h1>这个是项目user页面---{{ $route.params.id }}</h1>
`
}
// 创建一个路由对象
const router = new VueRouter({
//通过路由route指定路由规则
routes: [{
path: '/login',
// 给路由设置名字
name: 'login',
component: login
},
{
path: '/home',
component: home
},
{
path: '/case',
component: cases
},
{ // 路由地址上匹配的参数,在路由对应的组件中可以使用 $route.params.参数名 获取
path: '/user/:id',
component: user,
name: 'user'
},
]
})
var vm = new Vue({
el: '#app',
data: {
number: 0
},
methods: {
handle: function() {
if (this.number > 5) {
// 编程式路由
//跳转到另外一个页面
this.$router.push({
name: 'user',
params: {
id: this.number
}
})
}
}
},
// 将路由对象绑定到vue实例中
router
});
</script>
</body>
</html>
导航守卫
表单相关处理
<template>
<div style="background: #55aaff; height: 900px;">
<div class="login_box">
<el-card class="box-card">
<div class="title">
自 动 化 平 台 登 录
</div>
<el-form :model="formLogin" :rules="loginRules" ref='loginRef'>
<el-form-item prop="username">
<el-input v-model="formLogin.username" prefix-icon='el-icon-user' placeholder="请输入账号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="formLogin.password" type='password' prefix-icon="el-icon-lock"
placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item label="记住账号">
<el-switch v-model="formLogin.status"></el-switch>
</el-form-item>
<el-form-item style="text-align: center;">
<el-button type="primary" @click="loginHandle">点击登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</div>
</template>
<script>
/*
一、输入框数据验证
1、在 el-form 标签上绑定rules属性,指定校验的规则对象
2、在data中定义绑定校验规则
3、在 el-form-item 标签中通过prop指定校验的字段
二、点击登录对表单进行预验证
1、在 el-form 标签通过ref属性,设置表单引用对象
2、在点击登录的处理函数中,通过this.$resf.表单引用对象,获取表单对象,调用表单对象的validate方法进行校验
*/
export default {
data() {
return {
formLogin: {
username: '',
password: '',
status: false
},
loginRules: {
username: [{
required: true,
message: '账号不能为空',
trigger: 'blur'
}],
password: [{
required: true,
message: '密码不能为空',
trigger: 'blur'
},
{
min: 6,
max: 18,
message: '密码的长度在6到18之间',
trigger: 'blur'
}
]
}
}
},
methods: {
// 点击登录之后,处理登录的方法
loginHandle: function() {
// 验证表单,验证通过再发送登录请求
this.$refs.loginRef.validate(async (valid) => {
console.log('表单验证的结果', valid)
// 判断是否验证通过,没有通过则,终止函数执行
if (!valid) return
// -----判断是否要记住账号-----
if (this.formLogin.status){
// 勾选则保存账号到localStorage中
window.localStorage.setItem('username',this.formLogin.username)
}else{
// 没有勾选则删除localStorage中的账号
window.localStorage.removeItem('username')
}
// 验证通过的情况下,发送请求登录
console.log('请求登录接口')
const response = await this.$request.post('/user/login/', this.formLogin)
console.log(response)
// 判断登录请求是否成功
if (response.status === 200) {
this.$message({
message: '登录成功',
type: 'success',
duration:1000
});
window.sessionStorage.setItem('token', response.data.token)
this.$router.push('/home')
} else {
this.$message.error('登录失败');
}
})
}
},
// 组件中的数据挂载到模板中之后,会触发这个生命周期钩子函数
mounted(){
// 获取localStorage中的账号,设置到data中
const username = window.localStorage.getItem('username')
if(username){
this.formLogin.username = username
this.formLogin.status = true
}
}
}
</script>
<style scoped>
/* 在style中添加scoped属性,表示css样式只对该组件生效 */
.login_box {
width: 600px;
height: 400px;
position: absolute;
top: 200px;
left: 500px;
}
.title {
color: #409EFF;
font: bold 28px/60px "microsoft yahei";
width: 100%;
text-align: center;
margin-bottom: 30px;
}
</style>
包含分页,创建弹窗
<template>
<div class="project_list">
<el-card class="box-card">
<!-- 顶部的面包屑 -->
<div slot="header" class="clearfix">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>项目管理</el-breadcrumb-item>
<el-breadcrumb-item>项目列表</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-card class="box-card">
<!-- 添加项目的按钮 -->
<el-button type="primary" icon='el-icon-plus'>创建项目</el-button>
<!-- 项目列表页 -->
<el-table :data="projectList" style="width: 100%;margin-bottom: 10px;">
<el-table-column prop="id" label="ID" width="80" sortable>
</el-table-column>
<el-table-column prop="name" label="项目名" width="200">
</el-table-column>
<el-table-column prop="desc" label="描述信息" width="280">
</el-table-column>
<el-table-column prop="leader" label="负责人" width="100" sortable>
</el-table-column>
<el-table-column prop="tester" label="测试人员" width="100">
</el-table-column>
<el-table-column prop="interfaces" label="接口数量" width="120" sortable>
</el-table-column>
<el-table-column prop="testcases" label="用例数量" width="80">
</el-table-column>
<el-table-column prop="testsuits" label="套件数量" width="80">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" @click="proEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="proDelete(scope.row.id)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 项目翻页管理 -->
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="page" :page-sizes="[5, 10, 20, 30]" :page-size="size"
layout="total, sizes, prev, pager, next, jumper" :total="count">
</el-pagination>
</el-card>
</el-card>
<!-- 编辑项目的弹框 -->
<el-dialog title="编辑项目" :visible.sync="dialogEdit">
<el-form :model="editProject">
<el-form-item label="项目名">
<el-input v-model="editProject.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="负责人">
<el-input v-model="editProject.leader" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="测试人员">
<el-input v-model="editProject.tester" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="开发人员">
<el-input v-model="editProject.programmer" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="发布应用">
<el-input v-model="editProject.publish_app" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="描述信息">
<el-input v-model="editProject.desc" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogEdit = false">取 消</el-button>
<el-button type="primary" @click="updateProject">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
projectList: [],
// 当前选择的页码
page: 1,
// 数据总数
count: 0,
//每页的数据量
size: 10,
//编辑窗口显示
dialogEdit: false,
editProject: {}
}
},
methods: {
// 编辑项目
proEdit(value) {
console.log(value)
// 把接收到的项目数据,
this.editProject = {...value}
// 显示编辑框
this.dialogEdit = true
},
// 请求修改项目的接口
async updateProject(){
const response = await this.$request.put('/projects/' +this.editProject.id +'/',this.editProject)
if (response.status===200){
this.$message({
message: '修改项目成功',
type: 'success',
duration: 1000
});
this.getProject()
this.dialogEdit = false
}else{
this.$message({
message: '修改失败',
type: 'error',
duration: 1000
});
}
},
// 删除项目
async proDelete(id) {
console.log('当前删除的数据id为:', id)
// 通过接口删除后端数据
const response = await this.$request.delete('/projects/' + id +'/')
// 后端接口是安装resetful规范设计的,delete方法成功时,返回的状态码为204
if (response.status === 204) {
// 删除成功
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
// 重写加载数据
this.getProject()
} else {
this.$message({
message: '删除失败',
type: 'error',
duration: 1000
});
}
},
// 获取项目
async getProject() {
// 请求项目列表的接口,获取所有的项目
const response = await this.$request.get('/projects/', {
params: {
page: this.page,
size: this.size
}
})
if (response.status !== 200) return this.$message.error('服务器异常')
// 保存接口返回的项目列表
this.projectList = response.data.results
// 保存数据总数
this.count = response.data.count
console.log(response)
},
// 处理每页数量大小变化的方法
handleSizeChange(size) {
this.size = size
this.page = 1
this.getProject()
},
// 处理页码变化的方法
handleCurrentChange(page) {
this.page = page
this.getProject()
}
},
// vue实例数据挂载之后,出触发这个钩子函数
mounted() {
this.getProject()
}
}
</script>
<style>
</style>
格栅,删除列表,监听数据,写了个postman
<template>
<div class="caseEdit">
<el-card class="box-card">
<!-- 顶部的面包屑 -->
<div slot="header" class="clearfix">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>用例管理</el-breadcrumb-item>
<el-breadcrumb-item>用例编辑</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 显示主体内容的卡片 -->
<el-card class="box-card">
<el-divider content-position="left"><span style="color: #409EFF; font-weight: bold;">Api</span>
</el-divider>
<!-- api请求的基本信息 -->
<el-row :gutter="20">
<!-- 请求方法选择 -->
<el-col :span="3">
<el-select v-model="caseInfo.method" placeholder="请求方法">
<el-option label="GET" value="GET"></el-option>
<el-option label="POST" value="POST"></el-option>
<el-option label="PUT" value="PUT"></el-option>
<el-option label="DELETE" value="DELETE"></el-option>
<el-option label="PATCH" value="PATCH"></el-option>
<el-option label="HEAD" value="HEAD"></el-option>
<el-option label="OPTION" value="OPTION"></el-option>
</el-select>
</el-col>
<!-- host地址输入 -->
<el-col :span="9">
<el-input placeholder="host地址" v-model="caseInfo.host">
<template slot="prepend">Host</template>
</el-input>
</el-col>
<!-- 接口地址输入 -->
<el-col :span="9">
<el-input placeholder="接口路径" v-model="caseInfo.interface">
<template slot="prepend">接口地址</template>
</el-input>
</el-col>
<!-- 运行按钮 -->
<el-col :span="3">
<el-button type="primary" icon="el-icon-s-promotion">Run</el-button>
</el-col>
</el-row>
<el-divider content-position="left"><span style="color: #409EFF; font-weight: bold;">Request</span>
</el-divider>
<!--用例信息 -->
<el-tabs type="border-card">
<!-- 请求头 -->
<el-tab-pane label="请求头">
<el-row :gutter="20" v-for='header in caseInfo.headers' :key='header.key' style="margin: 10px;">
<el-col :span="6">
<el-input v-model.lazy="header.key" placeholder="KEY"></el-input>
</el-col>
<el-col :span="12">
<el-input v-model.lazy="header.value" placeholder="VALUE"></el-input>
</el-col>
<el-col :span="6">
<el-button type="danger" icon="el-icon-delete" @click='deleteRow(header)'></el-button>
</el-col>
</el-row>
</el-tab-pane>
<!-- 请求参数 -->
<el-tab-pane label="请求参数">
<el-tabs>
<el-tab-pane label="application/json" name="second">
<editor height="300" width="100%" ref="editor" :content="caseInfo.json"
v-model="caseInfo.json" :options="{
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: true,
tabSize:2,
fontSize:20,
showPrintMargin:false,}" :lang="'json'" @init="editorInit">
</editor>
</el-tab-pane>
<el-tab-pane label="Params" name="first">查询字符串参数</el-tab-pane>
<el-tab-pane label="application/X-www-urlencode" name="third">表单参数</el-tab-pane>
</el-tabs>
</el-tab-pane>
<el-tab-pane label="响应提取">响应提取</el-tab-pane>
<el-tab-pane label="用例断言">用例断言</el-tab-pane>
<el-tab-pane label="数据库校验">数据库校验</el-tab-pane>
</el-tabs>
</el-card>
</el-card>
</div>
</template>
<script>
import Editor from 'vue2-ace-editor'
export default {
data() {
return {
caseInfo: {
method: 'GET',
host: '',
interface: '',
headers: [{
key: '',
value: ''
}],
params:[{
key: '',
value: ''
}],
data:[{
key: '',
value: ''
}],
json:'{}'
}
}
},
methods: {
deleteRow(header) {
console.log(header)
// 删除一行内容
const newHeaders = this.caseInfo.headers.filter(function(item, index) {
return item !== header
})
this.caseInfo.headers = newHeaders
},
editorInit() {
require('brace/theme/chrome')
require('brace/ext/language_tools')
require('brace/mode/yaml')
require('brace/mode/json')
require('brace/mode/less')
require('brace/snippets/json')
}
},
watch: {
// 监听headers中的数据是否发送变化,当发送变化时,则判断最后一行是否有值,
'caseInfo.headers': {
handler: function(value, oldVal) {
if (value.length === 0) {
this.caseInfo.headers.push({
key: '',
value: ''
})
}
// 判断最后一行是否有值,
if (value[value.length - 1].key || value[value.length - 1].value) {
this.caseInfo.headers.push({
key: '',
value: ''
})
}
},
deep: true
}
},
components:{
Editor
}
}
</script>
<style scoped>
</style>
Vue CLI
启动
vue ui
解决跨域
module.exports = {
devServer: {
// 开发服务器地址
host: 'localhost',
// 端口号
port: 5000,
// 是否自动打开浏览器
// open: true,
// 配置代理
proxy: {
// 当前端请求以 /api 开头时
'/api': {
// 将请求转发到 http://example.com/ 上
// 注意:这里不能加 http://, 如果需要加http://, 请看下面的target属性说明
target: 'http://127.0.0.1', // 实际的接口地址
// 如果要保留原始的主机头和端口,设置为true
changeOrigin: true,
// 如果你想自定义路径,比如去掉路径中的'/api',你可以设置pathRewrite
// pathRewrite: {
// '^/api': '' // 去掉路径中的'/api'
// }
},
// 可以配置多个代理
'/foo': {
target: 'http://other.example.com',
changeOrigin: true,
pathRewrite: { '^/foo': '' }
}
}
}
}
组件样式只在当前组件内生效
表单设置前后图标
表单占位符 
表单必填
表单验证通过,再触发接口请求
记住账号,处理本地存储
可以处理成记住输入框的内容
确认对话框
消息提示修改存在时间
解决根路径和菜单默认展开的子菜单
默认激活的菜单
下拉列表

列表项目
包含,分页,切换页面,删除,编辑弹窗带入数据,新增
<template>
<div class="project_list">
<el-card class="box-card">
<!-- 顶部的面包屑 -->
<div slot="header" class="clearfix">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>项目管理</el-breadcrumb-item>
<el-breadcrumb-item>项目列表</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-card class="box-card">
<!-- 添加项目的按钮 -->
<el-button type="primary" icon='el-icon-plus' @click='dialogCreate="true"'>创建项目</el-button>
<!-- 项目列表页 -->
<el-table :data="projectList" style="width: 100%;margin-bottom: 10px;">
<el-table-column prop="id" label="ID" width="80" sortable>
</el-table-column>
<el-table-column prop="name" label="项目名" width="200">
</el-table-column>
<el-table-column prop="desc" label="描述信息" width="280">
</el-table-column>
<el-table-column prop="leader" label="负责人" width="100" sortable>
</el-table-column>
<el-table-column prop="tester" label="测试人员" width="100">
</el-table-column>
<el-table-column prop="interfaces" label="接口数量" width="120" sortable>
</el-table-column>
<el-table-column prop="testcases" label="用例数量" width="80">
</el-table-column>
<el-table-column prop="testsuits" label="套件数量" width="80">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button size="mini" @click="proEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="proDelete(scope.row.id)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 项目翻页管理 -->
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="page" :page-sizes="[5, 10, 20, 30]" :page-size="size"
layout="total, sizes, prev, pager, next, jumper" :total="count">
</el-pagination>
</el-card>
</el-card>
<!-- 编辑项目的弹框 -->
<el-dialog title="编辑项目" :visible.sync="dialogEdit">
<el-form :model="editProject" :rules="caseRules" ref='updateRef' label-width="80px">
<el-form-item label="项目名" prop="name">
<el-input v-model="editProject.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="负责人" prop="leader">
<el-input v-model="editProject.leader" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="测试人员" prop="tester">
<el-input v-model="editProject.tester" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="开发人员" prop="programmer">
<el-input v-model="editProject.programmer" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="发布应用" prop="publish_app">
<el-input v-model="editProject.publish_app" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="描述信息">
<el-input type="textarea" v-model="editProject.desc" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogEdit = false">取 消</el-button>
<el-button type="primary" @click="updateProject">确 定</el-button>
</div>
</el-dialog>
<!-- 创建项目的弹框 -->
<el-dialog :visible.sync="dialogCreate" :rules="caseRules" ref='createRef'>
<template slot='title'>
<div style="text-align: center;width: 100%;font-size: 24px;">创建项目</div>
</template>
<el-form :model="newProject" label-width="80px" size='mini' :rules="caseRules" ref='createRef'>
<el-form-item label="项目名称" prop="name">
<el-input v-model="newProject.name"></el-input>
</el-form-item>
<el-form-item label="测试者" prop="tester">
<el-input v-model="newProject.tester" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="负责人" prop="leader">
<el-input v-model="newProject.leader" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="开发人员" prop="programmer">
<el-input v-model="newProject.programmer" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="发布应用" prop="publish_app">
<el-input v-model="newProject.publish_app" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="项目描述">
<el-input type="textarea" :rows="5" v-model="newProject.desc"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogCreate = false">取 消</el-button>
<el-button type="primary" @click="createProject">提 交</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
projectList: [],
// 当前选择的页码
page: 1,
// 数据总数
count: 0,
//每页的数据量
size: 10,
//编辑窗口显示
dialogEdit: false,
editProject: {},
//创建项目
dialogCreate: false,
newProject: {
name: '',
leader: '',
tester: '',
programmer: '',
publish_app: '',
desc: ''
},
// 校验规则
caseRules: {
name: [{
required: true,
message: '项目名不能为空',
trigger: 'blur'
}],
leader: [{
required: true,
message: '负责人不能为空',
trigger: 'blur'
}, ],
tester: [{
required: true,
message: '测试人员不能为空',
trigger: 'blur'
}, ],
programmer: [{
required: true,
message: '开发人员不能为空',
trigger: 'blur'
}, ],
publish_app: [{
required: true,
message: '发布应用不能为空',
trigger: 'blur'
}, ]
}
}
},
methods: {
// 创建项目的方法
createProject() {
// 发送请求之前对表单进行预验证
this.$refs.createRef.validate(async (valid) => {
if (!valid) return
// 验证通过,发送请求
const response = await this.$request.post('/projects/', this.newProject)
if (response.status === 201) {
this.$message({
type: 'success',
message: '项目创建成功!',
duration: 1000
});
// 更新页面的数据
this.getProject()
// 关闭弹框
this.dialogCreate = false
} else {
console.log(response)
this.$message.error('服务端异常!')
}
})
},
// 编辑项目
proEdit(value) {
console.log(value)
// 把接收到的项目数据,
this.editProject = {
...value
}
// 显示编辑框
this.dialogEdit = true
},
// 请求修改项目的接口
async updateProject() {
// 发送请求之前对表单进行预验证
this.$refs.updateRef.validate(async (valid) => {
if (!valid) return
const response = await this.$request.put('/projects/' + this.editProject.id + '/', this
.editProject)
if (response.status === 200) {
this.$message({
message: '修改项目成功',
type: 'success',
duration: 1000
})
this.getProject()
this.dialogEdit = false
} else {
this.$message({
message: '修改失败',
type: 'error',
duration: 1000
})
}
})
},
// 删除项目
async proDelete(id) {
console.log('当前删除的数据id为:', id)
// 通过接口删除后端数据
const response = await this.$request.delete('/projects/' + id + '/')
// 后端接口是安装resetful规范设计的,delete方法成功时,返回的状态码为204
if (response.status === 204) {
// 删除成功
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
// 重写加载数据
this.getProject()
} else {
this.$message({
message: '删除失败',
type: 'error',
duration: 1000
});
}
},
// 获取项目
async getProject() {
// 请求项目列表的接口,获取所有的项目
const response = await this.$request.get('/projects/', {
params: {
page: this.page,
size: this.size
}
})
if (response.status !== 200) return this.$message.error('服务器异常')
// 保存接口返回的项目列表
this.projectList = response.data.results
// 保存数据总数
this.count = response.data.count
console.log(response)
},
// 处理每页数量大小变化的方法
handleSizeChange(size) {
this.size = size
this.page = 1
this.getProject()
},
// 处理页码变化的方法
handleCurrentChange(page) {
this.page = page
this.getProject()
}
},
// vue实例数据挂载之后,出触发这个钩子函数
mounted() {
this.getProject()
}
}
</script>
<style>
</style>
登录项目
<template>
<div style="background: #55aaff; height: 900px;">
<div class="login_box">
<el-card class="box-card">
<div class="title">
自 动 化 平 台 登 录
</div>
<el-form :model="formLogin" :rules="loginRules" ref='loginRef'>
<el-form-item prop="username">
<el-input v-model="formLogin.username" prefix-icon='el-icon-user' placeholder="请输入账号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="formLogin.password" type='password' prefix-icon="el-icon-lock"
placeholder="请输入密码"></el-input>
</el-form-item>
<el-form-item label="记住账号">
<el-switch v-model="formLogin.status"></el-switch>
</el-form-item>
<el-form-item style="text-align: center;">
<el-button type="primary" @click="loginHandle">点击登录</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</div>
</template>
<script>
/*
一、输入框数据验证
1、在 el-form 标签上绑定rules属性,指定校验的规则对象
2、在data中定义绑定校验规则
3、在 el-form-item 标签中通过prop指定校验的字段
二、点击登录对表单进行预验证
1、在 el-form 标签通过ref属性,设置表单引用对象
2、在点击登录的处理函数中,通过this.$resf.表单引用对象,获取表单对象,调用表单对象的validate方法进行校验
*/
export default {
data() {
return {
formLogin: {
username: '',
password: '',
status: false
},
loginRules: {
username: [{
required: true,
message: '账号不能为空',
trigger: 'blur'
}],
password: [{
required: true,
message: '密码不能为空',
trigger: 'blur'
},
{
min: 6,
max: 18,
message: '密码的长度在6到18之间',
trigger: 'blur'
}
]
}
}
},
methods: {
// 点击登录之后,处理登录的方法
loginHandle: function() {
// 验证表单,验证通过再发送登录请求
this.$refs.loginRef.validate(async (valid) => {
console.log('表单验证的结果', valid)
// 判断是否验证通过,没有通过则,终止函数执行
if (!valid) return
// -----判断是否要记住账号-----
if (this.formLogin.status){
// 勾选则保存账号到localStorage中
window.localStorage.setItem('username',this.formLogin.username)
}else{
// 没有勾选则删除localStorage中的账号
window.localStorage.removeItem('username')
}
// 验证通过的情况下,发送请求登录
console.log('请求登录接口')
const response = await this.$request.post('/user/login/', this.formLogin)
console.log(response)
// 判断登录请求是否成功
if (response.status === 200) {
this.$message({
message: '登录成功',
type: 'success',
duration:1000
});
window.sessionStorage.setItem('token', response.data.token)
this.$router.push('/home')
} else {
this.$message.error('登录失败');
}
})
}
},
// 组件中的数据挂载到模板中之后,会触发这个生命周期钩子函数
mounted(){
// 获取localStorage中的账号,设置到data中
const username = window.localStorage.getItem('username')
if(username){
this.formLogin.username = username
this.formLogin.status = true
}
}
}
</script>
<style scoped>
/* 在style中添加scoped属性,表示css样式只对该组件生效 */
.login_box {
width: 600px;
height: 400px;
position: absolute;
top: 200px;
left: 600px;
}
.title {
color: #409EFF;
font: bold 28px/60px "microsoft yahei";
width: 100%;
text-align: center;
margin-bottom: 30px;
}
</style>