自动化测试平台开发(八):接口测试 - vue前端请求接口渲染页面

本篇以接口管理为例,记录前端vue请求后端接口,获取接口表数据,分页展示接口列表。并实现接口新增、编辑、更新等功能。

 

前端基于开源vue-admin-template

 

1. 页面路由

router/index.js
  1 import Vue from 'vue'
  2 import Router from 'vue-router'
  3 
  4 Vue.use(Router)
  5 
  6 /* Layout */
  7 import Layout from '@/layout'
  8 // import fa from 'element-ui/src/locale/lang/fa'
  9 
 10 /**
 11  * Note: sub-menu only appear when route children.length >= 1
 12  * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
 13  *
 14  * hidden: true                   if set true, item will not show in the sidebar(default is false)
 15  * alwaysShow: true               if set true, will always show the root menu
 16  *                                if not set alwaysShow, when item has more than one children route,
 17  *                                it will becomes nested mode, otherwise not show the root menu
 18  * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
 19  * name:'router-name'             the name is used by <keep-alive> (must set!!!)
 20  * meta : {
 21     roles: ['admin','editor']    control the page roles (you can set multiple roles)
 22     title: 'title'               the name show in sidebar and breadcrumb (recommend set)
 23     icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
 24     breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
 25     activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
 26   }
 27  */
 28 
 29 /**
 30  * constantRoutes
 31  * a base page that does not have permission requirements
 32  * all roles can be accessed
 33  */
 34 export const constantRoutes = [
 35   {
 36     path: '/login',
 37     component: () => import('@/views/login/index'),
 38     hidden: true
 39   },
 40 
 41   {
 42     path: '/404',
 43     component: () => import('@/views/404'),
 44     hidden: true
 45   },
 46   {
 47     path: '/',
 48     component: Layout,
 49     redirect: '/dashboard',
 50     hidden: true,
 51     children: [
 52       {
 53         path: 'dashboard',
 54         name: 'Dashboard',
 55         hidden: true,
 56         component: () => import('@/views/dashboard'),
 57         meta: { title: 'Dashboard', icon: 'dashboard' }
 58       }
 59     ]
 60   },
 61   // 仪表盘
 62   {
 63     path: '/api/dashboard',
 64     component: Layout,
 65     // redirect: '/',
 66     name: 'Dashboard',
 67     meta: { title: 'Dashboard', icon: '' }
 68   },
 69   // 工单分析
 70   {
 71     path: '/api/order',
 72     component: Layout,
 73     redirect: '/api/order/list',
 74     name: 'Orders',
 75     meta: { title: '工单分析', icon: 'form' },
 76     children: [
 77       {
 78         path: 'list',
 79         name: 'order',
 80         component: () => import('@/views/orders/OrderList'),
 81         meta: { title: '工单列表', icon: 'table' }
 82       },
 83       {
 84         path: 'statistic',
 85         name: 'Statistics',
 86         component: () => import('@/views/orders/Statistics'),
 87         meta: { title: '数据统计', icon: 'blue-bar' }
 88       },
 89       {
 90         path: 'analysis',
 91         name: 'Analysis',
 92         component: () => import('@/views/orders/Analysis'),
 93         meta: { title: '趋势分析', icon: 'blue-line' }
 94       }
 95     ]
 96   },
 97   // 接口测试
 98   {
 99     path: '/api/apiTest',
100     redirect: '/api/apiTest/workbench',
101     component: Layout,
102     name: '接口测试',
103     meta: { title: '接口测试', icon: 'el-icon-cpu' },
104     children: [
105       {
106         path: 'workbench',
107         component: () => import('@/views/apiTest/workbench'),
108         name: '工作台',
109         meta: { title: '工作台', icon: 'el-icon-s-platform' }
110       },
111       {
112         path: 'global',
113         component: () => import('@/views/apiTest/globalMgr'),
114         name: '全局配置',
115         meta: { title: '全局配置', icon: 'el-icon-setting' }
116       },
117       {
118         path: 'projectMgr',
119         name: '项目管理',
120         component: () => import('@/views/apiTest/projectMgr'),
121         meta: { title: '项目管理', icon: 'tree' }
122       },
123       {
124         path: 'projectMgr/project_id=:project_id',
125         component: () => import('@/views/apiTest/projectMgr/projectDetail'),
126         hidden: true,
127         name: '项目详情',
128         meta: { title: '项目详情', icon: 'table' }
129       },
130       {
131         path: 'apiMgr',
132         name: '接口管理',
133         component: () => import('@/views/apiTest/apiMgr'),
134         meta: { title: '接口管理', icon: 'el-icon-s-ticket' },
135         children: [
136           {
137             path: 'api_id=:api_id',
138             component: () => import('@/views/apiTest/apiMgr/apiDetail'),
139             hidden: true,
140             name: '接口详情1',
141             meta: { title: '接口详情1' }
142           }
143         ]
144       },
145       {
146         path: 'apiMgr/api_id=:api_id',
147         component: () => import('@/views/apiTest/apiMgr/apiDetail'),
148         hidden: true,
149         name: '接口详情',
150         meta: { title: '接口详情', icon: 'table', keepAlive: true }
151       },
152       {
153         path: 'caseMgr',
154         component: () => import('@/views/apiTest/caseMgr'),
155         name: '用例管理',
156         meta: { title: '用例管理', icon: 'el-icon-suitcase', keepAlive: true }
157       },
158       {
159         path: 'caseMgr/case_id=:case_id',
160         component: () => import('@/views/apiTest/caseMgr/caseDetail'),
161         hidden: true,
162         name: '用例详情',
163         meta: { title: '用例详情', icon: 'table', keepAlive: true }
164       },
165       {
166         path: 'testReport',
167         name: '历史报告',
168         component: () => import('@/views/apiTest/reportMgr'),
169         meta: { title: '历史报告', icon: 'el-icon-notebook-1' },
170         children: [
171           {
172             hidden: true,
173             path: 'pytest_html/report_id=:report_id',
174             component: () => import('@/views/apiTest/reportMgr/reportDetail/PytestHtmlReport'),
175             name: 'PytestHtml',
176             meta: { title: 'PytestHtml' }
177           },
178           {
179             hidden: true,
180             path: 'allure_html/report_id=:report_id',
181             component: () => import('@/views/apiTest/reportMgr/reportDetail/AllureHtmlReport'),
182             name: 'AllureHtml',
183             meta: { title: 'AllureHtml' }
184           },
185           {
186             hidden: true,
187             path: 'test_logs/report_id=:report_id',
188             component: () => import('@/views/apiTest/reportMgr/reportDetail/TestLogs'),
189             name: 'TestLogs',
190             meta: { title: 'TestLogs' }
191           }
192         ]
193       },
194       {
195         path: 'dataMgr',
196         name: '导入导出',
197         component: () => import('@/views/apiTest/dataMgr/index'),
198         meta: { title: '数据管理', icon: 'el-icon-upload' }
199       },
200       {
201         path: 'taskMgr',
202         name: '任务管理',
203         component: () => import('@/views/apiTest/taskMgr/index'),
204         meta: { title: '任务管理', icon: 'el-icon-s-promotion' }
205       },
206       {
207         path: '/api/apiTest/statisticalAnalysis',
208         component: () => import('@/views/apiTest/statisticalAnalysis'),
209         name: '统计分析',
210         meta: { title: '统计分析', icon: 'el-icon-s-data' },
211         children: [
212           {
213             path: 'summary',
214             component: () => import('@/views/apiTest/statisticalAnalysis/summary/index'),
215             name: '概览',
216             meta: { title: '概览' }
217           },
218           {
219             path: 'api',
220             component: () => import('@/views/apiTest/statisticalAnalysis/apiAnalysis/index'),
221             name: '接口分析',
222             meta: { title: '接口分析' }
223           },
224           {
225             path: 'case',
226             component: () => import('@/views/apiTest/statisticalAnalysis/caseAnalysis/index'),
227             name: '用例分析',
228             meta: { title: '用例分析' }
229           },
230           {
231             path: 'result',
232             component: () => import('@/views/apiTest/statisticalAnalysis/resultAnalysis/index'),
233             name: '结果分析',
234             meta: { title: '结果分析' }
235           },
236           {
237             path: 'work',
238             component: () => import('@/views/apiTest/statisticalAnalysis/workAnalysis/index'),
239             name: '进度分析',
240             meta: { title: '进度分析' }
241           }
242         ]
243       },
244       {
245         path: '/api/apiTest/help',
246         name: '帮助文档',
247         component: () => import('@/views/apiTest/help'),
248         meta: { title: '帮助', icon: 'tree' },
249         children: [
250           {
251             path: 'validate_rules',
252             component: () => import('@/views/apiTest/help/BuiltinComparators'),
253             name: '数据校验器',
254             meta: { title: '数据校验器' }
255           },
256           {
257             path: 'builtin_functions',
258             component: () => import('@/views/apiTest/help/BuiltinFunctions'),
259             name: '内建函数',
260             meta: { title: '内建函数' }
261           },
262           {
263             path: 'customized_functions',
264             component: () => import('@/views/apiTest/help/CustomizedFunctions'),
265             name: '接口封装',
266             meta: { title: '接口封装' }
267           }
268         ]
269       }
270     ]
271   },
272   // 系统管理
273   {
274     path: '/api/systemManage',
275     component: Layout,
276     name: '系统管理',
277     meta: { title: '系统管理', icon: 'el-icon-setting' },
278     children: [
279       {
280         path: 'organization',
281         component: () => import('@/views/system-manage/organization'),
282         name: '团队组织',
283         meta: { title: '团队组织', icon: 'peoples' },
284         // hidden: true,
285         children: [
286           {
287             path: 'department',
288             component: () => import('@/views/system-manage/organization/department'),
289             name: '部门管理',
290             meta: { title: '部门管理', icon: 'tree-table' }
291           },
292           {
293             path: 'user',
294             component: () => import('@/views/system-manage/organization/user'),
295             name: '用户管理',
296             meta: { title: '用户管理', icon: 'el-icon-user' }
297           },
298           {
299             path: 'permission',
300             component: () => import('@/views/system-manage/config-center/Permission'),
301             name: '权限管理',
302             meta: { title: '权限管理', icon: 'el-icon-lock' }
303           }
304         ]
305       },
306       {
307         path: 'configCenter',
308         component: () => import('@/views/system-manage/config-center'),
309         name: '配置中心',
310         meta: { title: '配置中心', icon: 'el-icon-setting' },
311         hidden: true,
312         children: [
313           {
314             path: 'permission',
315             component: () => import('@/views/system-manage/config-center/Permission'),
316             name: '权限管理',
317             meta: { title: '权限管理', icon: 'el-icon-lock' }
318           },
319           {
320             path: 'theme',
321             component: () => import('@/views/system-manage/config-center/Theme'),
322             name: '主题配置',
323             meta: { title: '主题配置', icon: 'el-icon-s-open' }
324           }
325         ]
326       },
327       {
328         path: 'systemLogs',
329         component: () => import('@/views/system-manage/system-logs'),
330         name: '系统日志',
331         meta: { title: '系统日志', icon: 'el-icon-notebook-2' },
332         children: [
333           {
334             path: 'message',
335             component: () => import('@/views/system-manage/system-logs/Message'),
336             name: 'message',
337             meta: { title: 'message', icon: 'el-icon-notebook-2' }
338           },
339           {
340             path: 'logs/api_test',
341             component: () => import('@/views/system-manage/system-logs/ApiTest'),
342             name: 'api_test',
343             meta: { title: 'api_test', icon: 'el-icon-notebook-2' }
344           },
345           {
346             path: 'logs/error',
347             component: () => import('@/views/system-manage/system-logs/Error'),
348             name: 'error',
349             meta: { title: 'error', icon: 'el-icon-notebook-2' }
350           }
351         ]
352       }
353     ]
354   },
355   // 404 page must be placed at the end !!!
356   { path: '*', redirect: '/404', hidden: true }
357 ]
358 
359 
360 const createRouter = () => new Router({
361   // mode: 'history', // require service support
362   scrollBehavior: () => ({ y: 0 }),
363   routes: constantRoutes
364 })
365 
366 const router = createRouter()
367 
368 // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
369 export function resetRouter() {
370   const newRouter = createRouter()
371   router.matcher = newRouter.matcher // reset router
372 }
373 
374 export default router
View Code

 

2. 从0到1实现一个页面

  示例:全局环境配置页面

  1. src/api/新建global_env.js并添加后端接口调用方法
     1 import request from '@/utils/request'
     2 
     3 // 获取全局ENV(环境配置)配置列表
     4 export function getGlobalEnvList(params) {
     5   return request({
     6     url: '/api_test/global/env/list',
     7     method: 'get',
     8     params: params
     9   })
    10 }
    11 
    12 // 获取全局ENV(环境配置)详情
    13 export function getGlobalEnvDetail(pk, params) {
    14   return request({
    15     url: '/api_test/global/env/detail/' + pk + '/',
    16     method: 'get',
    17     params
    18   })
    19 }
    20 
    21 // 新增全局ENV(环境配置)
    22 export function addGlobalEnv(data) {
    23   return request({
    24     url: '/api_test/global/env/add/',
    25     method: 'post',
    26     data: data
    27   })
    28 }
    29 
    30 // 更新全局ENV(环境配置)
    31 export function updateGlobalEnv(pk, data) {
    32   return request({
    33     url: '/api_test/global/env/update/' + pk + '/',
    34     method: 'patch',
    35     data
    36   })
    37 }
    38 
    39 // 批量 局部更新
    40 export function bulkUpdateGlobalEnv(dataArr) {
    41   return request({
    42     url: '/api_test/global/env/bulk/',
    43     method: 'patch',
    44     data: dataArr
    45   })
    46 }
    47 
    48 // 删除全局ENV(环境配置)
    49 export function deleteGlobalEnv(pk) {
    50   return request({
    51     url: '/api_test/global/env/del/' + pk + '/',
    52     method: 'delete'
    53   })
    54 }
    55 
    56 // 批量 删除
    57 export function bulkDeleteGlobalEnv(params) {
    58   return request({
    59     url: '/api_test/global/env/bulk/',
    60     method: 'delete',
    61     params
    62   })
    63 }
    64 
    65 // 爬取/获取全局ENV对应环境的数据
    66 export function getGlobalEnvData(pk, params) {
    67   return request({
    68     url: '/api_test/global/env/data/' + pk + '/',
    69     method: 'get',
    70     params
    71   })
    72 }
    73 
    74 // 获取全局env.config默认数据
    75 export function getGlobalEnvConfigDefault(params) {
    76   return request({
    77     url: '/api_test/global/env/config/default',
    78     method: 'get',
    79     params
    80   })
    81 }
    82 
    83 // 获取全局env.qw_external_contact_config默认数据
    84 export function getGlobalEnvQWExternalContactConfigDefault(params) {
    85   return request({
    86     url: '/api_test/global/env/qw_external_contact_config/default',
    87     method: 'get',
    88     params
    89   })
    90 }
    View Code

     

  2. 编写前端页面:environment/index.vue
  1 <template>
  2   <div class="main" style="padding:10px;">
  3     <!--工具条-->
  4     <el-col :span="24" class="toolbar">
  5       <el-form :inline="true" :model="filters" :size="themeSize" @submit.native.prevent>
  6         <el-form-item>
  7           <el-input v-model.trim="filters.name" placeholder="名称" clearable @keyup.enter.native="fetchData" />
  8         </el-form-item>
  9         <el-form-item>
 10           <el-button type="primary" @click="fetchData">查询</el-button>
 11         </el-form-item>
 12         <el-form-item>
 13           <el-button type="primary" @click="handleAdd">新增</el-button>
 14         </el-form-item>
 15       </el-form>
 16     </el-col>
 17 
 18     <!--列表-->
 19     <el-table
 20       v-loading="tableConfig.isLoading"
 21       :data="dataList"
 22       highlight-current-row
 23       :size="themeSize"
 24       :height="tableConfig.height"
 25       style="width: 100%;"
 26       @selection-change="selsChange"
 27     >
 28       <el-table-column type="selection" min-width="50" />
 29       <el-table-column prop="name" label="名称" sortable show-overflow-tooltip min-width="150" />
 30       <el-table-column prop="description" label="描述" show-overflow-tooltip min-width="200" />
 31       <el-table-column prop="config" label="公司ID" sortable show-overflow-tooltip min-width="150">
 32         <template slot-scope="scope">
 33           {{ scope.row.config['company_id']['value'] }}
 34         </template>
 35       </el-table-column>
 36       <el-table-column prop="config" label="配置详情" min-width="100">
 37         <template slot-scope="scope">
 38           <el-popover
 39             placement="right"
 40             width="800"
 41             trigger="click"
 42           >
 43             <el-card class="box-card" shadow="never">
 44               <div slot="header" class="clearfix">环境配置({{ scope.row.name }}:{{ scope.row.description }})</div>
 45               <el-table :data="dictConfigToArray(scope.row.config, scope.row.qw_external_contact_config)" style="width: 100%" :size="themeSize">
 46                 <el-table-column label="名称" prop="key" sortable show-overflow-tooltip />
 47                 <el-table-column label="值" prop="value" sortable show-overflow-tooltip />
 48                 <el-table-column label="描述" prop="description" sortable show-overflow-tooltip />
 49               </el-table>
 50             </el-card>
 51             <el-button slot="reference" :size="themeSize" type="text">详情</el-button>
 52           </el-popover>
 53         </template>
 54       </el-table-column>
 55       <el-table-column prop="data" label="环境数据" min-width="100">
 56         <template slot-scope="scope">
 57           <el-popover
 58             placement="right"
 59             width="600"
 60             trigger="click"
 61           >
 62             <el-row>
 63               <el-button style="float: right; padding: 3px 0" type="text" @click="clearData(scope.row)">清除数据</el-button>
 64               <el-button style="padding-left: 10px" type="primary" :size="themeSize" @click="toggleExpanded">{{ expanded ? '收起' : '展开' }}</el-button>
 65               环境数据({{ scope.row.name }}:{{ scope.row.description }})
 66             </el-row>
 67             <json-viewer
 68               :key="expanded"
 69               :value="scope.row.data"
 70               :expand-depth="1"
 71               :expanded="expanded"
 72               copyable
 73               boxed
 74               sort
 75             />
 76             <el-button slot="reference" :size="themeSize" type="text">详情</el-button>
 77           </el-popover>
 78         </template>
 79       </el-table-column>
 80       <el-table-column prop="mock" label="Mock数据" min-width="100">
 81         <template slot-scope="scope">
 82           <el-popover
 83             placement="right"
 84             width="500"
 85             trigger="click"
 86           >
 87             <el-row>
 88               <el-button style="float: right;" type="text" :size="themeSize" @click="clearMock(scope.row)">清除数据</el-button>
 89               <el-button style="padding-left: 10px" type="primary" :size="themeSize" @click="toggleExpanded">{{ expanded ? '收起' : '展开' }}</el-button>
 90               Mock数据,执行时自动更新({{ scope.row.name }}:{{ scope.row.description }})
 91             </el-row>
 92             <json-viewer
 93               :key="expanded"
 94               :value="scope.row.mock"
 95               :expand-depth="1"
 96               :expanded="expanded"
 97               copyable
 98               boxed
 99               sort
100             />
101             <el-button slot="reference" :size="themeSize" type="text">详情</el-button>
102           </el-popover>
103         </template>
104       </el-table-column>
105       <el-table-column prop="status" label="状态" sortable min-width="70">
106         <template slot-scope="scope">
107           <img v-show="scope.row.status" src="@/assets/icon-yes.svg" alt="">
108           <img v-show="!scope.row.status" src="@/assets/icon-no.svg" alt="">
109         </template>
110       </el-table-column>
111       <el-table-column label="操作" min-width="180">
112         <template slot-scope="scope">
113           <el-button type="info" icon="el-icon-edit" circle :size="themeSize" title="编辑" @click="handleEdit(scope.$index, scope.row)" />
114           <el-button type="warning" icon="el-icon-refresh" circle :size="themeSize" title="更新环境数据" @click="handleUpdateEnvData(scope.$index, scope.row)" />
115           <el-button type="warning" icon="el-icon-s-flag" circle :size="themeSize" :title="scope.row.status===false?'启用':'禁用'" @click="handleChangeStatus(scope.$index, scope.row)" />
116           <el-button type="danger" icon="el-icon-delete" circle :size="themeSize" :loading="delLoading" title="删除" @click="handleDel(scope.$index, scope.row)" />
117         </template>
118       </el-table-column>
119     </el-table>
120 
121     <!--底部工具条-->
122     <el-row class="toolbar">
123       <!--分页-->
124       <el-col class="pagination-toolbar" :span="24">
125         <el-pagination
126           background
127           style="float:right;"
128           :current-page.sync="page"
129           layout="total, sizes, prev, pager, next, jumper"
130           :page-size="page_size"
131           :page-sizes="[20, 50, 100, 1000]"
132           :total="total"
133           @size-change="handleSizeChange"
134           @current-change="handleCurrentChange"
135         />
136       </el-col>
137       <!--批量处理-->
138       <el-col v-show="sels.length>0" class="bulk-toolbar" :span="24">
139         <span style="font-weight:bold;font-size:14px;color:#2C8DF4;">批量处理: </span>
140         <el-button type="danger" plain :disabled="sels.length===0" :size="themeSize" @click="bulkRemove">删除</el-button>
141         <span style="font-size: 14px; padding-right: 30px;"> 选中{{ sels.length }}条</span>
142         <el-button type="text" plain :size="themeSize" @click="sels = []">取消</el-button>
143       </el-col>
144     </el-row>
145 
146     <!--编辑界面-->
147     <el-drawer
148       title="编辑"
149       :with-header="true"
150       :wrapper-closable="false"
151       :visible.sync="editFormVisible"
152       direction="rtl"
153       size="50%"
154     >
155       <div class="demo-drawer__content">
156         <el-form ref="editForm" :size="themeSize" :model="editForm" :rules="editFormRules" label-width="160px">
157           <!--  基本信息  -->
158           <el-collapse value="1">
159             <el-collapse-item name="1">
160               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">基本信息</span>
161               <el-form-item label="名称" prop="name">
162                 <el-input v-model.trim="editForm.name" auto-complete="off" />
163               </el-form-item>
164               <el-form-item v-for="(k,index) in editForm.config" :key="index" :label="k.description" :prop="index">
165                 <el-input v-model.trim="editForm.config[index].value" auto-complete="off" />
166               </el-form-item>
167               <el-form-item label="描述" prop="description">
168                 <el-input v-model.trim="editForm.description" type="textarea" :rows="2" />
169               </el-form-item>
170             </el-collapse-item>
171           </el-collapse>
172 
173           <!--  企微客户联系配置  -->
174           <el-collapse value="1">
175             <el-collapse-item name="1">
176               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">企微客户联系配置</span>
177               <el-form-item v-for="(k,index) in editForm.qw_external_contact_config" :key="index" :label="k.description" :prop="index">
178                 <el-input v-model.trim="editForm.qw_external_contact_config[index].value" auto-complete="off" />
179               </el-form-item>
180             </el-collapse-item>
181           </el-collapse>
182 
183           <!--  设置  -->
184           <el-collapse value="1">
185             <el-collapse-item name="1">
186               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">设置</span>
187               <el-form-item label="动态更新mock" prop="mock_dynamic">
188                 <el-switch
189                   v-model="editForm.mock_dynamic"
190                   active-color="#13ce66"
191                 />
192               </el-form-item>
193               <el-form-item label="默认环境" prop="is_default">
194                 <el-switch
195                   v-model="editForm.is_default"
196                   active-color="#13ce66"
197                 />
198               </el-form-item>
199             </el-collapse-item>
200           </el-collapse>
201         </el-form>
202         <!-- 取消、提交 -->
203         <div class="demo-drawer__footer">
204           <el-button :size="themeSize" @click.native="editFormVisible = false; editLoading = false">取消</el-button>
205           <el-button :size="themeSize" type="primary" :loading="editLoading" @click.native="editSubmit">提交</el-button>
206         </div>
207       </div>
208     </el-drawer>
209 
210     <!--新增界面-->
211     <el-drawer
212       title="编辑"
213       :with-header="true"
214       :wrapper-closable="false"
215       :visible.sync="addFormVisible"
216       direction="rtl"
217       size="50%"
218     >
219       <div class="demo-drawer__content">
220         <el-form ref="addForm" :size="themeSize" :model="addForm" label-width="160px" :rules="addFormRules">
221           <!--  基本信息  -->
222           <el-collapse value="1">
223             <el-collapse-item name="1">
224               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">基本信息</span>
225               <el-form-item label="名称" prop="name">
226                 <el-input v-model.trim="addForm.name" auto-complete="off" />
227               </el-form-item>
228               <el-form-item v-for="(k,index) in addForm.config" :key="index" :label="k.description" :prop="'config.'+index">
229                 <el-input v-model.trim="addForm.config[index].value" auto-complete="off" />
230               </el-form-item>
231               <el-form-item label="环境描述" prop="description">
232                 <el-input v-model.trim="addForm.description" type="textarea" :rows="2" />
233               </el-form-item>
234             </el-collapse-item>
235           </el-collapse>
236 
237           <!--  企微客户联系配置  -->
238           <el-collapse value="1">
239             <el-collapse-item name="1">
240               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">企微客户联系配置</span>
241               <el-form-item v-for="(k,index) in addForm.qw_external_contact_config" :key="index" :label="k.description" :prop="'qw_external_contact_config.'+index">
242                 <el-input v-model.trim="addForm.qw_external_contact_config[index].value" auto-complete="off" />
243               </el-form-item>
244             </el-collapse-item>
245           </el-collapse>
246 
247           <!--  设置  -->
248           <el-collapse value="1">
249             <el-collapse-item name="1">
250               <span slot="title" style="font-weight:bold;font-size:14px;color:#2C8DF4;">设置</span>
251               <el-form-item label="动态更新mock" prop="mock_dynamic">
252                 <el-switch
253                   v-model="addForm.mock_dynamic"
254                   active-color="#13ce66"
255                 />
256               </el-form-item>
257               <el-form-item label="默认环境" prop="is_default">
258                 <el-switch
259                   v-model="addForm.is_default"
260                   active-color="#13ce66"
261                 />
262               </el-form-item>
263             </el-collapse-item>
264           </el-collapse>
265 
266         </el-form>
267         <!-- 取消、提交 -->
268         <div class="demo-drawer__footer">
269           <el-button :size="themeSize" @click.native="addFormVisible = false; addLoading = false">取消</el-button>
270           <el-button :size="themeSize" type="primary" :loading="addLoading" @click.native="addSubmit">提交</el-button>
271         </div>
272       </div>
273     </el-drawer>
274   </div>
275 </template>
276 
277 <script>
278 import JsonViewer from 'vue-json-viewer'
279 import {
280   addGlobalEnv,
281   bulkDeleteGlobalEnv,
282   deleteGlobalEnv,
283   getGlobalEnvConfigDefault,
284   getGlobalEnvQWExternalContactConfigDefault,
285   getGlobalEnvData,
286   getGlobalEnvList,
287   updateGlobalEnv
288 } from '@/api/apiTest/global_env'
289 
290 export default {
291   name: 'GlobalEnv',
292   components: { JsonViewer },
293   data() {
294     return {
295       themeSize: this.$store.state.settings.themeSize,
296       tableConfig: {
297         isLoading: false,
298         height: window.innerHeight - 230 // 下面剩余多少空白部分(即最下面距离底部有多少距离)
299       },
300       editLoading: false,
301       addLoading: false,
302       syncLoading: false,
303       delLoading: false,
304       batchDelLoading: false,
305       expanded: true,
306 
307       filters: {
308         name: ''
309       },
310       dataList: null,
311       total: 0,
312       page: 1,
313       page_size: 20,
314       page_count: 0,
315       sels: [], // 列表选中列
316       envDefaultConfig: {},
317 
318       editFormVisible: false, // 编辑界面是否显示
319       // 编辑界面数据规则
320       editFormRules: {
321         name: [
322           { required: true, message: '请输入名称', trigger: 'blur' },
323           { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
324         ],
325         config: [
326           { required: true, message: '请输入配置信息', trigger: 'blur' }
327         ],
328         description: [
329           { required: false, message: '请输入描述', trigger: 'blur' },
330           { max: 1024, message: '不能超过1024个字符', trigger: 'blur' }
331         ]
332       },
333       // 编辑界面数据
334       editForm: {
335         name: '',
336         config: {},
337         mock_dynamic: false,
338         is_default: false,
339         description: ''
340       },
341 
342       addFormVisible: false, // 新增界面是否显示
343       // 新增界面数据规则
344       addFormRules: {
345         name: [
346           { required: true, message: '请输入名称', trigger: 'blur' },
347           { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
348         ],
349         // config: [
350         //   { required: true, message: '请输入配置信息', trigger: 'blur' }
351         // ],
352         description: [
353           { required: false, message: '请输入描述', trigger: 'blur' },
354           { max: 1024, message: '不能超过1024个字符', trigger: 'blur' }
355         ]
356       },
357       // 新增界面数据
358       addForm: {
359         name: '',
360         config: {},
361         qw_external_contact_config: {},
362         data: {},
363         mock: {},
364         mock_dynamic: false,
365         is_default: false,
366         description: ''
367       }
368 
369     }
370   },
371   created() {
372   },
373   mounted() {
374     this.fetchEnvConfigDefault()
375     this.fetchEnvQWExternalContactConfigDefault()
376     this.fetchData()
377   },
378   methods: {
379     // 获取ENV列表
380     fetchData() {
381       this.tableConfig.isLoading = true
382       const params = {
383         page: this.page,
384         page_size: this.page_size,
385         name: this.filters.name
386       }
387       getGlobalEnvList(params).then(response => {
388         const { msg, code } = response
389         this.tableConfig.isLoading = false
390         if (code === 2) {
391           this.total = response.data.count
392           this.dataList = response.data.list
393         } else {
394           this.$message.error({
395             message: msg,
396             center: true
397           })
398         }
399       })
400     },
401     fetchEnvConfigDefault() {
402       this.syncLoading = true
403       getGlobalEnvConfigDefault({}).then(response => {
404         const { msg, code } = response
405         this.syncLoading = false
406         if (code === 2) {
407           this.addForm.config = response.data
408           for (const k in response.data) {
409             this.addFormRules['config.' + k] = [{ required: true, message: '请输入配置信息' + k, trigger: 'blur' }]
410           }
411         } else {
412           this.$message.error({
413             message: msg,
414             center: true
415           })
416         }
417       })
418     },
419     fetchEnvQWExternalContactConfigDefault() {
420       this.syncLoading = true
421       getGlobalEnvQWExternalContactConfigDefault({}).then(response => {
422         const { msg, code } = response
423         this.syncLoading = false
424         if (code === 2) {
425           this.addForm.qw_external_contact_config = response.data
426           for (const k in response.data) {
427             this.addFormRules['qw_external_contact_config.' + k] = [{ required: true, message: '请输入配置信息' + k, trigger: 'blur' }]
428           }
429         } else {
430           this.$message.error({
431             message: msg,
432             center: true
433           })
434         }
435       })
436     },
437     // 选中行
438     selsChange: function(sels) {
439       this.sels = sels
440     },
441     // 显示编辑界面
442     handleEdit: function(index, row) {
443       this.editFormVisible = true
444       this.editForm = Object.assign({}, row)
445     },
446     // 显示新增界面
447     handleAdd: function() {
448       this.addFormVisible = true
449     },
450     // 改变状态: enable/disable
451     handleChangeStatus: function(index, row) {
452       if (row.status) {
453         // 禁用
454         updateGlobalEnv(row.id, { status: false }).then(response => {
455           const { code, msg } = response
456           if (code === 2) {
457             this.$message({
458               message: '禁用成功',
459               center: true,
460               type: 'success'
461             })
462             row.status = !row.status
463           } else {
464             this.$message.error({
465               message: msg,
466               center: true
467             })
468           }
469         })
470       } else {
471         // 启用
472         updateGlobalEnv(row.id, { status: true }).then(response => {
473           const { msg, code } = response
474           if (code === 2) {
475             this.$message({
476               message: '启用成功',
477               center: true,
478               type: 'success'
479             })
480             row.status = !row.status
481           } else {
482             this.$message.error({
483               message: msg,
484               center: true
485             })
486           }
487         })
488       }
489     },
490     // 刷新每页数据条数
491     handleSizeChange(val) {
492       console.log(`每页 ${val} 条`)
493       this.page_size = val
494       this.fetchData()
495     },
496     // 刷新指定页数据
497     handleCurrentChange(val) {
498       console.log(`当前页: ${val}`)
499       this.page = val
500       this.fetchData()
501     },
502     // 编辑
503     editSubmit: function() {
504       this.$refs.editForm.validate((valid) => {
505         if (valid) {
506           this.$confirm('确认提交吗?', '提示', {}).then(() => {
507             this.editLoading = true
508             // NProgress.start();
509             updateGlobalEnv(Number(this.editForm.id), this.editForm).then(response => {
510               const { msg, code } = response
511               this.editLoading = false
512               if (code === 2) {
513                 this.$message({
514                   message: '修改成功',
515                   center: true,
516                   type: 'success'
517                 })
518                 this.$refs['editForm'].resetFields()
519                 this.editFormVisible = false
520               } else if (code === 3) {
521                 this.$message.error({
522                   message: msg,
523                   center: true
524                 })
525               } else {
526                 this.$message.error({
527                   message: msg,
528                   center: true
529                 })
530               }
531             }).then(() => { this.fetchData() })
532           })
533         }
534       })
535     },
536     // 新增
537     addSubmit: function() {
538       this.$refs.addForm.validate((valid) => {
539         if (valid) {
540           this.$confirm('确认提交吗?', '提示', {}).then(() => {
541             this.addLoading = true
542             // NProgress.start();
543             addGlobalEnv(this.addForm).then(response => {
544               const { msg, code } = response
545               this.addLoading = false
546               if (code === 2) {
547                 this.$message({
548                   message: '添加成功',
549                   center: true,
550                   type: 'success'
551                 })
552                 this.$refs['addForm'].resetFields()
553                 this.addFormVisible = false
554               } else if (code === 3) {
555                 this.$message.error({
556                   message: msg,
557                   center: true
558                 })
559               } else {
560                 this.$message.error({
561                   message: msg,
562                   center: true
563                 })
564                 this.$refs['addForm'].resetFields()
565                 this.addFormVisible = false
566               }
567             }).then(() => { this.fetchData() })
568           })
569         }
570       })
571     },
572     // 删除
573     handleDel: function(index, row) {
574       this.$confirm('确认删除该记录吗?', '提示', {
575         type: 'warning'
576       }).then(() => {
577         this.delLoading = true
578         // NProgress.start();
579         deleteGlobalEnv(row.id).then(response => {
580           const { msg, code } = response
581           this.delLoading = false
582           if (code === 2) {
583             this.$message({
584               message: '删除成功',
585               center: true,
586               type: 'success'
587             })
588           } else {
589             this.$message.error({
590               message: msg,
591               center: true
592             })
593           }
594         }).then(() => { this.fetchData() })
595       })
596     },
597     // 批量删除
598     bulkRemove: function() {
599       const ids = this.sels.map(item => item.id)
600       this.$confirm('确认删除选中记录吗?', '提示', {
601         type: 'warning'
602       }).then(() => {
603         const params = {
604           id_in: ids.join(',')
605         }
606         bulkDeleteGlobalEnv(params).then(response => {
607           const { code, msg } = response
608           if (code === 2) {
609             this.$message({
610               message: '删除成功',
611               center: true,
612               type: 'success'
613             })
614           } else {
615             this.$message.error({
616               message: msg,
617               center: true
618             })
619           }
620         }).then(() => { this.fetchData() })
621       })
622     },
623     // 获取环境数据并更新到数据库
624     handleUpdateEnvData: function(index, row) {
625       this.$confirm('确认提交吗?', '提示', {}).then(() => {
626         this.syncLoading = true
627         getGlobalEnvData(Number(row.id), {}).then(response => {
628           const { msg, code } = response
629           this.syncLoading = false
630           if (code === 2) {
631             updateGlobalEnv(Number(row.id), response.data).then(response => {
632               const { msg, code } = response
633               if (code === 2) {
634                 this.$message({
635                   message: '更新成功',
636                   center: true,
637                   type: 'success'
638                 })
639               } else if (code === 3) {
640                 this.$message.error({
641                   message: msg,
642                   center: true
643                 })
644               } else {
645                 this.$message.error({
646                   message: msg,
647                   center: true
648                 })
649               }
650             }).then(() => { this.fetchData() })
651           } else {
652             this.$message.error({
653               message: msg,
654               center: true
655             })
656           }
657         })
658       })
659     },
660     // 清除env.data
661     clearData: function(row) {
662       this.$confirm('确认清除env.data数据吗?', '提示', {}).then(() => {
663         updateGlobalEnv(Number(row.id), { data: {}}).then(response => {
664           const { msg, code } = response
665           if (code === 2) {
666             this.$message({
667               message: '清除成功',
668               center: true,
669               type: 'success'
670             })
671             this.fetchData()
672           } else if (code === 3) {
673             this.$message.error({
674               message: msg,
675               center: true
676             })
677           }
678         })
679       })
680     },
681     clearMock: function(row) {
682       this.$confirm('确认清除env.mock数据吗?', '提示', {}).then(() => {
683         updateGlobalEnv(Number(row.id), { mock: {}}).then(response => {
684           const { msg, code } = response
685           if (code === 2) {
686             this.$message({
687               message: '清除成功',
688               center: true,
689               type: 'success'
690             })
691             this.fetchData()
692           } else if (code === 3) {
693             this.$message.error({
694               message: msg,
695               center: true
696             })
697           }
698         })
699       })
700     },
701     // env.data/env.mock 字典key-value 转Array[{key:key, value:value}]
702     dictKVToArray: function(src) {
703       const dst = []
704       for (const k in src) {
705         const v = src[k]
706         dst.push({ key: k, value: JSON.stringify(v) })
707       }
708       return dst
709     },
710     // env.config/env.qw_external_contact_config 字典key:{value:'', description:''} 转Array[{key:key, value:value, description:description}]
711     dictConfigToArray: function(src1, src2) {
712       const dst = []
713       for (const k in src1) {
714         const v = src1[k]
715         dst.push({ key: k, value: JSON.stringify(v.value), description: v.description })
716       }
717       for (const k in src2) {
718         const v = src2[k]
719         dst.push({ key: k, value: JSON.stringify(v.value), description: v.description })
720       }
721       return dst
722     },
723     toggleExpanded() {
724       this.expanded = !this.expanded
725     }
726   }
727 }
728 </script>
729 
730 <style lang="scss" scoped>
731   ::v-deep .el-drawer__body {
732     overflow: auto;
733   }
734   ::v-deep .demo-drawer__content {
735     margin-bottom: 2px;
736     padding: 10px 20px 20px;
737     overflow: auto;
738   }
739   ::v-deep .demo-drawer__footer{
740     width: 100%;
741     position: absolute;
742     bottom: 0;
743     left: 0;
744     border-top: 1px solid #e8e8e8;
745     padding: 10px 16px;
746     text-align: center;
747     background-color: white;
748   }
749 </style>
View Code

 

 

3. 页面组件化

  示例:工作台页面 - 快速测试、业务巡检、冒烟测试、环境验证

  

  1. components/TabsMenu.vue
     1 <template>
     2   <div class="main">
     3     <!--  标签页  -->
     4     <el-tabs v-model="activeName" :tab-position="tabPosition" @tab-click="clickTab">
     5       <el-tab-pane
     6         v-for="(item,index) in tabPaneList"
     7         :key="index"
     8         :label="item.label"
     9         :name="item.name"
    10         lazy
    11       >
    12         <component :is="component" v-for="(component,idx) in item.components" :key="idx" />
    13       </el-tab-pane>
    14     </el-tabs>
    15   </div>
    16 </template>
    17 
    18 <script>
    19 export default {
    20   name: '',
    21   props: {
    22     tabPaneList: {
    23       type: Array,
    24       default() {
    25         return []
    26       }
    27     },
    28     tabPosition: {
    29       type: String,
    30       default() {
    31         return 'top'
    32       }
    33     }
    34   },
    35   data() {
    36     return {
    37       activeName: '' // 默认选中
    38     }
    39   },
    40   created() {
    41     this.getMenuList()
    42   },
    43   mounted() {
    44   },
    45   methods: {
    46     // 获取标签页列表
    47     getMenuList() {
    48       if (this.tabPaneList.length > 0) {
    49         this.activeName = this.tabPaneList[0].name
    50       }
    51     },
    52     // 控制每次点击标签,都重新请求子组件的接口
    53     clickTab(tab, event) {
    54       // console.log(tab, event)
    55     }
    56   }
    57 }
    58 </script>
    59 
    60 <style lang="scss" scoped>
    61   ::v-deep .el-tabs__item {
    62     box-sizing: border-box;
    63     height: 35px;
    64     color: #2c4068;
    65     margin: 0;
    66     text-align: center;
    67     overflow: hidden!important;
    68     text-overflow: ellipsis;
    69     white-space: nowrap;
    70     word-break: break-all;
    71     font-weight: 400;
    72     min-width: 100px;
    73     font-size: 14px;
    74     line-height: 22px;
    75     padding: 9px 0;
    76     position: relative;
    77     display: inline-block;
    78     list-style: none;
    79   }
    80   ::v-deep .el-tabs__item.is-active {
    81     color: #047AE2;
    82     font-weight: 700;
    83   }
    84   ::v-deep .el-tabs__active-bar {
    85     height: 4px;
    86     position: absolute;
    87     bottom: 0;
    88     left: 0;
    89     z-index: 1;
    90     list-style: none;
    91   }
    92   ::v-deep .el-tabs--left .el-tabs__item.is-left {
    93     text-align: center;
    94   }
    95 </style>
    View Code
  2. workbench/index.vue
     1 <template>
     2   <tabs-menu :tab-pane-list="tabPaneList" />
     3 </template>
     4 
     5 <script>
     6 import TabsMenu from '@/views/apiTest/components/TabsMenu'
     7 // import jobBoard from '@/views/apiTest/workbench/jobBoard'
     8 import FastTest from '@/views/apiTest/workbench/FastTest'
     9 import EnvInspection from '@/views/apiTest/workbench/EnvInspection'
    10 import EnvSmoke from '@/views/apiTest/workbench/EnvSmoke'
    11 import EnvValidate from '@/views/apiTest/workbench/EnvValidate'
    12 // import task from '@/views/apiTest/taskMgr/TaskSchedule'
    13 
    14 export default {
    15   name: 'Workbench',
    16   components: { TabsMenu },
    17   data() {
    18     return {
    19       tabPaneList: [
    20         // {
    21         //   name: 'jobBoard',
    22         //   label: '工作看板',
    23         //   components: [jobBoard]
    24         // },
    25         {
    26           name: 'FastTest',
    27           label: '快速测试',
    28           components: [FastTest]
    29         },
    30         {
    31           name: 'EnvInspection',
    32           label: '业务巡检',
    33           components: [EnvInspection]
    34         },
    35         {
    36           name: 'EnvSmoke',
    37           label: '冒烟测试',
    38           components: [EnvSmoke]
    39         },
    40         {
    41           name: 'EnvValidate',
    42           label: '环境验证',
    43           components: [EnvValidate]
    44         }
    45       ]
    46     }
    47   }
    48 }
    49 </script>
    50 
    51 <style scoped>
    52 
    53 </style>
    View Code

     

 

 

 

 

-------- THE END --------

posted @ 2021-12-04 17:50  徒手沉浮  阅读(687)  评论(0编辑  收藏  举报