手把手教你撸一个接口自动化测试平台(二)
2023-04-13 本文已影响0人
HC2
- 一、用户登录功能
准备:
pip3 install djangorestframework
pip3 install djangorestframework-simplejwt
1、进入setting.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api_autotest_app', #注册app
'rest_framework.authtoken', #添加token模块
'rest_framework', #注册rest_framework
]
# REST_FRAMEWORK、simplejwt相关配置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 使用JWT进行权限验证
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication', # 通过 JWT 进行用户授权,验证过程需要访问数据库
'rest_framework_simplejwt.authentication.JWTTokenUserAuthentication', # 通过 JWT 的 Token 进行用户验证,验证过程不需要访问数据库
),
'DEFAULT_PAGINATION_CLASS': (
'rest_framework.pagination.PageNumberPagination',
),
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', # 指定支持coreapi的Schema
}
AUTH_USER_MODEL = 'api_autotest_app.mUser'
# simplejwt 配置
JWT_SIGNING_KEY = '123456'
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=15),
'REFRESH_TOKEN_LIFETIME': timedelta(days=15),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': JWT_SIGNING_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(days=15),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=15),
}
2、重写User模块
models.py
from django.db import models
import hashlib
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import gettext_lazy as _
# Create your models here.
class mUser(AbstractUser):
"""
自定义的用户模块
"""
# 微信同步的用户信息
openid = models.CharField(
verbose_name=_('钉钉OpenID'), help_text=_('钉钉OpenID'), max_length=100, unique=True, null=True, blank=True)
avatar_url = models.URLField(
verbose_name=_('头像'), help_text=_('头像'), null=True, blank=True)
nick_name = models.CharField(
verbose_name=_('昵称'), help_text=_('昵称'), max_length=100, null=True, blank=True, unique=True)
phone = models.CharField(
verbose_name=_('手机号'), help_text=_('手机号'), max_length=100, null=True, blank=True)
def create_username_password(self):
'''
自动通过openid创建用户名 username 和密码 password。
'''
if not self.username and not self.password and self.openid:
key = settings.SECRET_KEY
self.username = hashlib.pbkdf2_hmac(
"sha256", getattr(self, 'openid').encode(encoding='utf-8'), key.encode(encoding='utf-8'), 10).hex()
def save(self, *args, **kwargs):
self.create_username_password()
super().save(*args, **kwargs)
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_dMODEL'
3、实现用户登录功能
(1)、使用rest_framework模块的serializers序列化
api_autotest_app目录下新建serializers.py
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from .models import *
class JfwTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super(JfwTokenObtainPairSerializer, cls).get_token(user)
token['username'] = user.username
return token
(2)、重写rest_framework 接口返回格式
api_autotest_app->commom目录下新建api_response.py
from rest_framework.response import Response
from rest_framework.serializers import Serializer
class JsonResponse(Response):
"""
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
def __init__(self, data=None, code=None, msg=None,
status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super().__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = {"code": code, "msg": msg, "data": data}
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in headers.items():
self[name] = value
(3)、实现login接口
api_autotest_app->api目录下新建login.py
from rest_framework.views import APIView
from api_autotest_app.serializers import JfwTokenObtainPairSerializer
from api_autotest_app.common.api_response import JsonResponse
from django.contrib.auth import authenticate
class Login(APIView):
"""
post:
管理登录接口
"""
authentication_classes = []
permission_classes = []
def post(self, request):
username = request.data.get('username')
password = request.data.get('password')
if not username or not password:
return JsonResponse(code=999995, msg="参数有误")
user = authenticate(username=username, password=password)
if user and user.is_active and user.is_superuser:
token = JfwTokenObtainPairSerializer.get_token(user).access_token
data = {
'access_token': str(token)
}
return JsonResponse(data=data, code=0, msg="成功!")
else:
return JsonResponse(code=99995, msg="账号或密码错误!")
(4)、配置路由
api_autotest_app目录新建urls.py
from django.conf.urls import url
from api_autotest_app.api import login
urlpatterns = [
url(r'^user/login$', login.Login.as_view()),
]
api_autotest_admin->urls.py
"""api_autotest_admin URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from django.urls import path, include
from api_autotest_app import urls
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/', include(urls)),
]
创建管理员:
python3 manage.py createsuperuser
启动项目:
python3 manage.py runserver 8005
(5)、实现登录页面
克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git
进入根目录:
npm install
基础依赖包添加成功后执行
npm run dev
启动成功后可以看到进入登录页面
image.png- 对接登录功能
修改文件将mock改为我们自己的后端接口
image.png
配置代理:
image.png修改接口:
src -> api->user.js
import request from '@/utils/request'
export function login(data) {
return request({
url: '/api/user/login',
method: 'post',
data
})
}
export function getInfo(token) {
return request({
url: '/api/internal/admin/getUser',
method: 'get',
params: { }
})
}
src->utils->request.js
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 0) {
Message({
message: res.msg || 'Error',
type: 'error',
duration: 5 * 1000
})
登录页面
src->views->login-index.vue 去掉一些不要的东西
<template>
<div class="login-container">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">
<div class="title-container">
<h3 class="title">v的接口自动化测试平台</h3>
</div>
<el-form-item prop="username">
<span class="svg-container">
<svg-icon icon-class="user" />
</span>
<el-input
ref="username"
v-model="loginForm.username"
placeholder="Username"
name="username"
type="text"
tabindex="1"
auto-complete="on"
/>
</el-form-item>
<el-form-item prop="password">
<span class="svg-container">
<svg-icon icon-class="password" />
</span>
<el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
:type="passwordType"
placeholder="Password"
name="password"
tabindex="2"
auto-complete="on"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="showPwd">
<svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
</span>
</el-form-item>
<el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>
</el-form>
</div>
</template>
<script>
import { validUsername } from '@/utils/validate'
export default {
name: 'Login',
data() {
const validateUsername = (rule, value, callback) => {
if (!validUsername(value)) {
callback(new Error('Please enter the correct user name'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('The password can not be less than 6 digits'))
} else {
callback()
}
}
return {
loginForm: {
username: '',
password: ''
},
loginRules: {
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
loading: false,
passwordType: 'password',
redirect: undefined
}
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect
},
immediate: true
}
},
methods: {
showPwd() {
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus()
})
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
}
}
</script>
<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
$bg:#283443;
$light_gray:#fff;
$cursor: #fff;
@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
.login-container .el-input input {
color: $cursor;
}
}
/* reset element-ui css */
.login-container {
.el-input {
display: inline-block;
height: 47px;
width: 85%;
input {
background: transparent;
border: 0px;
-webkit-appearance: none;
border-radius: 0px;
padding: 12px 5px 12px 15px;
color: $light_gray;
height: 47px;
caret-color: $cursor;
&:-webkit-autofill {
box-shadow: 0 0 0px 1000px $bg inset !important;
-webkit-text-fill-color: $cursor !important;
}
}
}
.el-form-item {
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.1);
border-radius: 5px;
color: #454545;
}
}
</style>
<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;
.login-container {
min-height: 100%;
width: 100%;
background-color: $bg;
overflow: hidden;
.login-form {
position: relative;
width: 520px;
max-width: 100%;
padding: 160px 35px 0;
margin: 0 auto;
overflow: hidden;
}
.tips {
font-size: 14px;
color: #fff;
margin-bottom: 10px;
span {
&:first-of-type {
margin-right: 16px;
}
}
}
.svg-container {
padding: 6px 5px 6px 15px;
color: $dark_gray;
vertical-align: middle;
width: 30px;
display: inline-block;
}
.title-container {
position: relative;
.title {
font-size: 26px;
color: $light_gray;
margin: 0px auto 40px auto;
text-align: center;
font-weight: bold;
}
}
.show-pwd {
position: absolute;
right: 10px;
top: 7px;
font-size: 16px;
color: $dark_gray;
cursor: pointer;
user-select: none;
}
}
</style>
使用管理员账号登录成功后进入首页
image.png- 实现项目模块
编写后台接口
1、创建project表 model.py
class Project(models.Model):
"""
项目表
"""
ProjectType = (
('prd', '生产'),
('test', '测试')
)
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, verbose_name='项目名称')
version = models.CharField(max_length=50, verbose_name='版本')
type = models.CharField(max_length=50, verbose_name='类型', choices=ProjectType)
description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述')
status = models.BooleanField(default=True, verbose_name='状态')
LastUpdateTime = models.DateTimeField(auto_now=True, verbose_name='最近修改时间')
createTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
user = models.ForeignKey(mUser, on_delete=models.SET_NULL, null=True, max_length=1024, verbose_name='创建人')
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Meta:
verbose_name = '项目'
verbose_name_plural = '项目'
2、序列化信息
class ProjectDeserializer(serializers.ModelSerializer):
"""
项目信息反序列化
"""
class Meta:
model = Project
fields = "__all__"
class ProjectSerializer(serializers.ModelSerializer):
"""
项目信息序列化
"""
LastUpdateTime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
createTime = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
user = serializers.CharField(source='user.username')
class Meta:
model = Project
fields = "__all__"
3、接口实现
class ProjectList(APIView):
def get(self, request):
"""
获取项目列表
:param request:
:return:
"""
try:
page_size = int(request.GET.get("page_size", 20)) #每页20条
page = int(request.GET.get("page", 1)) # 默认从第一页开始
except (TypeError, ValueError):
return JsonResponse(code=999995, msg="page字段和page_size字段 必须为整数!")
param = {}
name = request.GET.get("name")
status = request.GET.get("status")
if name:
param['name'] = name
if status:
param['status'] = True if status=="true" else False
if param:
obi = Project.objects.filter(**param).order_by("id")
else:
obi = Project.objects.all().order_by("id")
paginator = Paginator(obi, page_size)
total = paginator.num_pages
try:
obm = paginator.page(page)
except PageNotAnInteger:
obm = paginator.page(1)
except EmptyPage:
obm = []
serialize = ProjectSerializer(obm, many=True)
return JsonResponse(data={"data": serialize.data,
"page": page,
"total": total
}, code=Code.SUCCESS, msg="成功")
class AddProject(APIView):
def parameter_check(self, data):
"""
验证参数
:param data:
:return:
"""
try:
# 必传参数 name, version, type
if not data["name"] or not data["version"] or not data["type"]:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
if data["type"] not in ["pro", "test"]:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
except KeyError:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
def post(self, request):
"""
新增项目
:param request:
:return:
"""
data = JSONParser().parse(request)
result = self.parameter_check(data)
if result:
return result
data["user"] = request.user.pk
project_serializer = ProjectDeserializer(data=data)
try:
Project.objects.get(name=data["name"])
return JsonResponse(code=Code.DATA_HAS_EXISTED, msg="存在相同名称")
except ObjectDoesNotExist:
if project_serializer.is_valid():
project_serializer.save()
return JsonResponse(data={
"project_id": project_serializer.data.get("id")
}, code=Code.SUCCESS, msg="成功")
else:
return JsonResponse(code=Code.ERROR, msg="失败")
class UpdateProject(APIView):
def parameter_check(self, data):
"""
校验参数
:param data:
:return:
"""
try:
# 校验project_id类型为int
if not isinstance(data["id"], int):
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
# 必传参数 name, version , type
if not data["name"] or not data["version"] or not data["type"]:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
# type 必为pro, test
if data["type"] not in ["pro", "test"]:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
except KeyError:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
def post(self, request):
"""
修改项目
:param request:
:return:
"""
data = JSONParser().parse(request)
result = self.parameter_check(data)
if result:
return result
del data['user']
# 查找项目是否存在
try:
obj = Project.objects.get(id=data["id"])
if not request.user.is_superuser and obj.user.is_superuser:
return JsonResponse(code=Code.USER_NOT_PERMISSION, msg="无操作权限!")
except ObjectDoesNotExist:
return JsonResponse(code=Code.DATA_NOT_EXISTED, msg="项目不存在!")
# 查找是否相同名称的项目
pro_name = Project.objects.filter(name=data["name"]).exclude(id=data["id"])
if len(pro_name):
return JsonResponse(code=Code.DATA_HAS_EXISTED, msg="存在相同名称")
else:
serializer = ProjectDeserializer(data=data)
with transaction.atomic():
if serializer.is_valid(raise_exception=True):
# 修改项目
serializer.update(instance=obj, validated_data=data)
return JsonResponse(code=Code.SUCCESS, msg="成功")
else:
return JsonResponse(code=Code.ERROR, msg="失败")
class DelProject(APIView):
def parameter_check(self, data):
"""
校验参数
:param data:
:return:
"""
try:
if not isinstance(data["ids"], list):
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
for i in data["ids"]:
if not isinstance(i, int):
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
except KeyError:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
def post(self, request):
"""
删除项目
:param request:
:return:
"""
data = JSONParser().parse(request)
result = self.parameter_check(data)
if result:
return result
try:
for i in data["ids"]:
try:
obj = Project.objects.get(id=i)
if not request.user.is_superuser and obj.user.is_superuser:
return JsonResponse(code=Code.USER_NOT_PERMISSION, msg=str(obj)+"无操作权限!")
except ObjectDoesNotExist:
return JsonResponse(code=Code.DATA_NOT_EXISTED, msg="项目不存在!")
for j in data["ids"]:
obj = Project.objects.filter(id=j)
obj.delete()
return JsonResponse(code=Code.SUCCESS, msg="成功")
except ObjectDoesNotExist:
return JsonResponse(code=Code.DATA_NOT_EXISTED, msg="项目不存在!")
class DisableProject(APIView):
def parameter_check(self, data):
"""
校验参数
:param data:
:return:
"""
try:
# 校验project_id类型为int
if not isinstance(data["id"], int):
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
except KeyError:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
def post(self, request):
"""
禁用项目
:param request:
:return:
"""
data = JSONParser().parse(request)
result = self.parameter_check(data)
if result:
return result
try:
obj = Project.objects.get(id=data["id"])
if not request.user.is_superuser and obj.user.is_superuser:
return JsonResponse(code=Code.USER_NOT_PERMISSION, msg=str(obj) + "无操作权限!")
obj.status = False
obj.save()
return JsonResponse(code=Code.SUCCESS, msg="成功")
except ObjectDoesNotExist:
return JsonResponse(code=Code.DATA_NOT_EXISTED, msg="项目不存在!")
class EnableProject(APIView):
def parameter_check(self, data):
"""
校验参数
:param data:
:return:
"""
try:
if not isinstance(data["id"], int):
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
except KeyError:
return JsonResponse(code=Code.PARAM_IS_INVALID, msg="参数有误!")
def post(self, request):
"""
启用项目
:param request:
:return:
"""
data = JSONParser().parse(request)
result = self.parameter_check(data)
if result:
return result
# 查找项目是否存在
try:
obj = Project.objects.get(id=data["id"])
if not request.user.is_superuser and obj.user.is_superuser:
return JsonResponse(code=Code.USER_NOT_PERMISSION, msg=str(obj) + "无操作权限!")
obj.status = True
obj.save()
return JsonResponse(code=Code.SUCCESS, msg="成功")
except ObjectDoesNotExist:
return JsonResponse(code=Code.DATA_NOT_EXISTED, msg="项目不存在!")
4、配置接口路由
- 实现前端页面
dashoard->index
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.name" placeholder="项目名称" style="width: 200px;" class="filter-item" @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.status" placeholder="状态" clearable class="filter-item" style="width: 130px;margin-left: 10px;">
<el-option v-for="(item,index) in statusOptions" :key="index" :label="item.name" :value="item.status" />
</el-select>
<el-button v-waves class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-search" @click="handleFilter">
查询
</el-button>
<el-button class="filter-item" style="margin-left: 10px;" type="primary" icon="el-icon-edit" @click="handleCreate">
添加
</el-button>
</div>
<el-table
:key="tableKey"
v-loading="listLoading"
:data="list"
border
fit
highlight-current-row
style="width: 100%;margin-top: 50px;"
@sort-change="sortChange"
>
<el-table-column label="ID" prop="id" sortable="custom" align="center" width="80" :class-name="getSortClass('id')">
<template slot-scope="{row}">
<span>{{ row.id }}</span>
</template>
</el-table-column>
<el-table-column label="项目名称" width="150px" align="center">
<template slot-scope="{row}">
<span>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column label="类型" width="150px" align="center">
<template slot-scope="{row}">
<span v-if="row.type='test'">测试环境</span>
<span v-else>生产环境</span>
</template>
</el-table-column>
<el-table-column label="创建人" width="150px" align="center">
<template slot-scope="{row}">
<span>{{row.user}}</span>
</template>
</el-table-column>
<el-table-column label="描述" align="center">
<template slot-scope="{row}">
<span>{{row.description}}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" width="200px" align="center">
<template slot-scope="{row}">
<span>{{ row.createTime }}</span>
</template>
</el-table-column>
<el-table-column label="修改时间" width="200px" align="center">
<template slot-scope="{row}">
<span>{{ row.LastUpdateTime }}</span>
</template>
</el-table-column>
<el-table-column label="状态" class-name="status-col" width="100" align="center">
<template slot-scope="{row}">
<el-tag v-if="row.status==false" :type="row.status | statusFilter">
已禁用
</el-tag>
<el-tag v-if="row.status==true" :type="row.status | statusFilter">
已启动
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="260" class-name="small-padding fixed-width">
<template slot-scope="{row,$index}">
<el-button type="primary" size="mini" @click="handleUpdate(row)">
编辑
</el-button>
<el-button v-if="row.status!='1'" size="mini" type="success" @click="handleDisableProject(row,true)">
启动
</el-button>
<el-button v-if="row.status!='0'" size="mini" @click="handleEnableProject(row,false)">
禁用
</el-button>
<el-button v-if="row.status!='2'" size="mini" type="danger" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.page_size" @pagination="getList" />
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="temp" label-position="left" label-width="70px" style="width: 400px; margin-left:50px;">
<el-form-item label="项目名称" prop="name">
<el-input v-model="temp.name" />
</el-form-item>
<el-form-item label="版本号" prop="version">
<el-input v-model="temp.version" />
</el-form-item>
<el-form-item label="类型">
<el-select v-model="temp.type" class="filter-item" placeholder="Please select">
<el-option v-for="(item,index) in typeOptions" :key="index" :label="item.name" :value="item.type" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop='description'>
<el-input type="textarea" :rows="6" v-model="temp.description"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">
取消
</el-button>
<el-button type="primary" @click="dialogStatus==='create'?createData():updateData()">
确认
</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="dialogPvVisible" title="Reading statistics">
<el-table :data="pvData" border fit highlight-current-row style="width: 100%">
<el-table-column prop="key" label="Channel" />
<el-table-column prop="pv" label="Pv" />
</el-table>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogPvVisible = false">Confirm</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {getList,addProject,updateProject,DisableProject,EnableProject,DelProject } from '@/api/dashboard.js'
import waves from '@/directive/waves' // waves directive
import { parseTime } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
export default {
name: 'ComplexTable',
components: { Pagination },
directives: { waves },
filters: {
statusFilter(status) {
const statusMap = {
1: 'success',
0: 'info',
2: 'danger'
}
return statusMap[status]
},
typeFilter(type) {
return calendarTypeKeyValue[type]
}
},
data() {
return {
tableKey: 0,
list: null,
total: 0,
listLoading: true,
listQuery: {
page: 1,
page_size: 20,
name: undefined,
type: undefined,
description:undefined,
status:undefined,
},
importanceOptions: [1, 2, 3],
sortOptions: [{ label: 'ID Ascending', key: '+id' }, { label: 'ID Descending', key: '-id' }],
typeOptions: [
{
name:'测试环境',type:'test'
},
{
name:'生产环境',type:'pro'
}
],
statusOptions: [
{
name:'启动',status:true
},
{
name:'禁用',status:false
}
],
temp: {
id:undefined,
name: undefined,
type: undefined,
description:undefined,
version:undefined
},
dialogFormVisible: false,
dialogStatus: '',
textMap: {
update: 'Edit',
create: 'Create'
},
dialogPvVisible: false,
pvData: [],
rules: {
name: [{ required: true, message: 'name is required', trigger: 'change' }],
type: [{ required: true, message: 'type is required', trigger: 'blur' }],
status: [{ required: true, message: 'status is required', trigger: 'blur' }]
},
downloadLoading: false
}
},
created() {
this.getList()
},
methods: {
getList() {
this.listLoading = true
getList(this.listQuery).then(response => {
this.list = response.data.data
this.total = response.data.total
// Just to simulate the time of the request
setTimeout(() => {
this.listLoading = false
}, 1.5 * 1000)
})
},
handleFilter() {
this.listQuery.page = 1
this.getList()
},
handleDisableProject(row, status) {
let data = {id:row.id,status:status}
DisableProject(data).then((res) => {
this.$message({
message: '操作Success',
type: 'success'
})
row.status = status
})
},
handleEnableProject(row, status) {
let data = {id:row.id,status:status}
EnableProject(data).then((res) => {
this.$message({
message: '操作Success',
type: 'success'
})
row.status = status
})
},
sortChange(data) {
const { prop, order } = data
if (prop === 'id') {
this.sortByID(order)
}
},
sortByID(order) {
if (order === 'ascending') {
this.listQuery.sort = '+id'
} else {
this.listQuery.sort = '-id'
}
this.handleFilter()
},
handleCreate() {
this.dialogStatus = 'create'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
addProject(this.temp).then(() => {
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Created Successfully',
type: 'success',
duration: 2000
})
this.getList()
})
}
})
},
handleUpdate(row) {
this.temp = Object.assign({}, row)
this.dialogStatus = 'update'
this.dialogFormVisible = true
this.$nextTick(() => {
this.$refs['dataForm'].clearValidate()
})
},
updateData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
const tempData = Object.assign({}, this.temp)
updateProject(tempData).then(() => {
this.dialogFormVisible = false
this.$notify({
title: 'Success',
message: 'Update Successfully',
type: 'success',
duration: 2000
})
this.getList()
})
}
})
},
handleDelete(row, status) {
let data = {ids:[row.id]}
this.$confirm('确认删除该记录吗?', '提示', {
type: 'warning'
}).then(() => {
DelProject(data).then((res) => {
this.$notify({
title: 'Success',
message: 'Delete Successfully',
type: 'success',
duration: 2000
})
this.getList()
})
})
},
handleFetchPv(pv) {
fetchPv(pv).then(response => {
this.pvData = response.data.pvData
this.dialogPvVisible = true
})
},
handleDownload() {
this.downloadLoading = true
import('@/vendor/Export2Excel').then(excel => {
const tHeader = ['time', 'name', 'status']
const filterVal = ['time', 'name', 'status']
const data = this.formatJson(filterVal)
excel.export_json_to_excel({
header: tHeader,
data,
filename: 'table-list'
})
this.downloadLoading = false
})
},
formatJson(filterVal) {
return this.list.map(v => filterVal.map(j => {
if (j === 'time') {
return parseTime(v[j])
} else {
return v[j]
}
}))
},
getSortClass: function(key) {
const sort = this.listQuery.sort
return sort === `+${key}` ? 'ascending' : 'descending'
}
}
}
</script>
具体一些配置就不一一说明了
实现项目详情页面