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

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 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>

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

实现项目详情页面

上一篇下一篇

猜你喜欢

热点阅读