美文网首页
手把手教你撸一个接口自动化测试平台(二)

手把手教你撸一个接口自动化测试平台(二)

作者: HC2 | 来源:发表于2023-04-13 15:29 被阅读0次
    • 一、用户登录功能

    准备:

    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
    • 实现项目模块
    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>
    

    具体一些配置就不一一说明了

    实现项目详情页面

    相关文章

      网友评论

          本文标题:手把手教你撸一个接口自动化测试平台(二)

          本文链接:https://www.haomeiwen.com/subject/opzftrtx.html