一.使用vite初始化项目
-
官网地址:开始 | Vite 官方中文文档
-
执行命令:
npm create vite@latest
-
第一步:选择vue
-
第二步:选择javascript
-
第三步:
- cd vite-project
- npm install
- npm run dev
二. vscode打开项目
- 项目结构
- vscode安装以下插件
- Chinese (Simplified),为 VS Code 提供本地化界面,按下“Ctrl+Shift+P”组合键以显示“命令面板”,然后键入“display”以筛选并显示“Configure Display Language”命令。按“Enter”,然后会按区域设置显示安装的语言列表,并突出显示当前语言设置
- Vue.js Extension Pack,用于vue3的智能代码提示,语法高亮、智能感知、Emmet等
- Prettier - Code formatter,代码格式化
- Auto Rename Tag,修改 html 标签,自动完成尾部闭合标签的同步修改
- Auto Close Tag,自动闭合HTML标签
- Path Intellisense,自动路径补全
三.安装依赖库
-
1.安装接口请求工具 axios
npm install axios
-
2.安装路由 vue-router
npm install vue-router@4
-
3.安装组件库 element-plus
npm install element-plus npm install @element-plus/icons-vue
四.封装接口请求
- 新建api文件
- 在api文件中新建http.js,初始化axios
// 完成http请求的基本配置
// 导入axios
import axios from "axios"
// 创建axios实例
var instance = axios.create({
// 请求体
headers: {
'Content-Type': 'application/json'
},
// 超时时间
timeout: 2500,
// 基础url,后端的接口服务地址
// baseURL: 'https://dev-hogwarts-platform-backend.hogwarts.ceshiren.com'
baseURL: 'http://127.0.0.1:5000'
})
// 添加请求拦截器,在请求头中加入token
instance.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
// console.log('token', token)
if (token) {
// 设置请求头中的 Authorization 字段
config.headers.Authorization = `Bearer ${token}`;
// console.log('token', config.headers.Authorization)
}
return config
},
error => {
return Promise.reject(error)
})
export default instance
五.封装路由
- 新建router文件
- 在router文件中新建index.js,封装路由映射
import instance from './http'
const api = {
// get方法
get(url, params) {
return instance({
url: url,
method: "get",
params: params
})
},
// post方法
post(url, data) {
return instance({
url: url,
method: "post",
data: data
})
},
// put方法
put(url, data) {
return instance({
url: url,
method: "put",
data: data
})
},
// delete方法
delete(url, data) {
return instance({
url: url,
method: "delete",
data: data
})
},
}
export default api
六.使用依赖包
- main.js中引入依赖包并使用
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 导入api
import api from './api/api'
// 导入路由
import router from './router'
// 导入element-plus
import ElementPlus from 'element-plus'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 注册api
window.$api = api
// 初始化vue App
const app = createApp(App)
// 全局引入icon库
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
// 全局引入路由router和element-plus
app.use(router).use(ElementPlus, {locale: zhCn,}).mount('#app')
七.初始化页面
-
新建views文件,在views文件中新建以下vue文件
-
User.vue
<template> <div> User </div> </template> <script setup> </script> <style scoped> </style>
-
Index.vue
<template> <router-view></router-view> </template> <script setup> </script> <style scoped> </style>
-
Testcase.vue
<template> <div> Testcase </div> </template> <script setup> </script> <style scoped> </style>
-
Plan.vue
<template> <div> Plan </div> </template> <script setup> </script> <style scoped> </style>
-
Record.vue
<template> <div> Record </div> </template> <script setup> </script> <style scoped> </style>
八.实现登录页面 User.vue
<template>
<div class="main">
<el-card class="box-card" style="margin-bottom: 10%;margin-right: 4%;">
<template #header>
<div class="card-header">
<span>登录表单</span>
</div>
</template>
<el-form :model="form" label-width="80px">
<el-form-item label="用户名">
<el-input v-model="form.username" />
</el-form-item>
<el-form-item label="密码">
<el-input type="password" v-model="form.password" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="login">登录</el-button>
<el-button type="success" @click="register">注册</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
const router = useRouter()
const form = ref({
username: '',
password: '',
})
// 登录
const login = async () => {
const result = await $api.post('/user/login', form.value)
const { code, data, msg } = result.data
console.log(code, data, msg);
// 登录成功时
if (code == 0) {
ElMessage.success(msg)
localStorage.setItem('token', data.token)
router.push('/index/testcase')
} else {
ElMessage.error(msg)
}
}
// 注册
const register = async () => {
const result = await $api.post('/user/register', form.value)
const { code, data, msg } = result.data
// 注册成功时
if (code == 0) {
ElMessage.success(msg)
login()
} else {
ElMessage.error(msg)
}
}
</script>
<style scoped>
.main {
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-image: url('https://ceshiren.com/uploads/default/original/3X/2/8/289416e80d70439819bd6003883d8dfe4e2cd5f3.jpeg');
background-size: 100% 100%;
}
</style>
实现顶部栏侧边栏 Index.vue
<template>
<div class="common-layout">
<el-container>
<el-header>
<div style="height: 100%;display: flex;justify-content:space-between;align-items: center;">
<h1>测试平台</h1>
<el-button type="primary" @click="logout">退出</el-button>
</div>
</el-header>
<el-container>
<el-aside width="200px" style="height: 100vh;">
<el-menu router default-active="2" class="el-menu-vertical-demo" @open="handleOpen"
@close="handleClose">
<el-menu-item index="testcase">
<el-icon>
<document />
</el-icon>
<span>测试用例</span>
</el-menu-item>
<el-menu-item index="plan">
<el-icon><icon-menu /></el-icon>
<span>测试计划</span>
</el-menu-item>
<el-menu-item index="record">
<el-icon>
<setting />
</el-icon>
<span>测试记录</span>
</el-menu-item>
</el-menu>
</el-aside>
<el-main>
<!-- 三个界面,用router-view占位 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import {
Document,
Menu as IconMenu,
Setting,
} from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const logout = () => {
router.push('/user')
}
</script>
<style scoped></style>
八.实现测试用例页面 Testcase.vue
<template>
<!-- 顶部栏按钮 -->
<el-button type="success" @click="dialogPlan = true">新增计划</el-button>
<el-button type="primary" @click="dialogAdd = true">新增用例</el-button>
<!-- 表格 -->
<el-table ref="tableRef" stripe border @selection-change="selectionChange" :data="tableData" style="width: 100%">
<el-table-column type="selection" width="40" />
<el-table-column prop="id" label="用例Id" width="80" />
<el-table-column prop="name" label="用例名称" />
<el-table-column prop="step" label="用例步骤" />
<el-table-column prop="method" label="用例方法" />
<el-table-column prop="remark" label="备注" />
<el-table-column prop="actions" label="操作" width="140">
<template #default="scope">
<el-button size="small" type="primary" @click="handleEdit(scope.$index)">修改</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增计划弹框 -->
<el-dialog v-model="dialogPlan" title="新增计划">
<!-- 内容区域 -->
<el-form>
<el-form-item label="计划名称">
<el-input v-model="planName" />
</el-form-item>
</el-form>
<!-- 底部插槽 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogPlan = false">取消</el-button>
<el-button type="primary" @click="addPlan"> 确认 </el-button>
</span>
</template>
</el-dialog>
<!-- 新增用例弹框 -->
<el-dialog v-model="dialogAdd" title="新增用例">
<!-- 内容区域 -->
<el-form :model="addData">
<el-form-item label="用例名称">
<el-input v-model="addData.name" />
</el-form-item>
<el-form-item label="用例步骤">
<el-input type="textarea" v-model="addData.step" />
</el-form-item>
<el-form-item label="用例方法">
<el-input type="textarea" v-model="addData.method" />
</el-form-item>
<el-form-item label="用例备注">
<el-input type="textarea" v-model="addData.remark" />
</el-form-item>
</el-form>
<!-- 底部插槽 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogAdd = false">取消</el-button>
<el-button type="primary" @click="addTestcase"> 确认 </el-button>
</span>
</template>
</el-dialog>
<!-- 修改用例弹框 -->
<el-dialog v-model="dialogPut" title="修改用例">
<!-- 内容区域 -->
<el-form :model="putData">
<el-form-item label="用例ID">
<el-input v-model="putData.id" disabled />
</el-form-item>
<el-form-item label="用例名称">
<el-input v-model="putData.name" />
</el-form-item>
<el-form-item label="用例步骤">
<el-input type="textarea" v-model="putData.step" />
</el-form-item>
<el-form-item label="用例方法">
<el-input type="textarea" v-model="putData.method" />
</el-form-item>
<el-form-item label="用例备注">
<el-input type="textarea" v-model="putData.remark" />
</el-form-item>
</el-form>
<!-- 底部插槽 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogAdd = false">取消</el-button>
<el-button type="primary" @click="putTestcase"> 确认 </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import { onMounted, ref } from "vue"
import { ElMessage, ElMessageBox } from "element-plus"
const tableRef = ref()
// 计划弹框
const dialogPlan = ref(false)
// 计划名称
const planName = ref('')
// 新增计划
const addPlan = async () => {
let planData = { 'name': planName.value, 'testcase_ids': idList.value }
const result = await $api.post('/plan/post', planData)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
dialogPlan.value = false
planName.value = ''
tableRef.value.clearSelection()
} else {
ElMessage.error(msg)
}
}
// 新增弹框
const dialogAdd = ref(false)
// 用例数据
const addData = ref({})
// 新增用例
const addTestcase = async () => {
const result = await $api.post('/testcase/post', addData.value)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
dialogAdd.value = false
addData.value = {}
initData()
} else {
ElMessage.error(msg)
}
}
// 修改弹框
const dialogPut = ref(false)
// 点击修改
const handleEdit = (index) => {
putData.value = tableData.value[index]
dialogPut.value = true
}
// 用例数据
const putData = ref({})
// 修改用例
const putTestcase = async () => {
const result = await $api.post('/testcase/put', putData.value)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
dialogPut.value = false
putData.value = {}
initData()
} else {
ElMessage.error(msg)
}
}
// 删除用例
const handleDelete = (index) => {
ElMessageBox.alert('请确认是否删除,删除数据后无法找回', '警告!', {
confirmButtonText: '确认',
callback: async () => {
let deleteData = { 'id': tableData.value[index].id }
const result = await $api.post('/testcase/delete', deleteData)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
initData()
} else {
ElMessage.error(msg)
}
},
})
}
// id列表
const idList = ref([])
// 勾选后会自动触发
const selectionChange = (items) => {
idList.value = items.map((value) => value.id)
}
// 表格数据
const tableData = ref([])
// 初始化数据
const initData = async () => {
const result = await $api.get('/testcase/get')
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
tableData.value = data
} else {
ElMessage.error(msg)
}
}
// 获取数据操作放在onMounted生命周期中
onMounted(() => {
initData()
})
</script>
<style scoped></style>
八.实现测试计划页面 Plan.vue
<template>
<!-- 表格 -->
<el-table stripe border :data="tableData" style="width: 100%">
<el-table-column prop="id" label="计划Id" width="80" />
<el-table-column prop="name" label="计划名称" />
<el-table-column prop="testcases" label="用例详情">
<template #default="scope">
<span v-for="item in scope.row.testcases">{{ item.name }},</span>
</template>
</el-table-column>
<el-table-column prop="actions" label="操作" width="220">
<template #default="scope">
<el-button size="small" type="success" @click="handleBuild(scope.$index)">执行</el-button>
<el-button size="small" type="primary" @click="getRecord(scope.$index)">历史记录</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 历史记录弹框 -->
<el-dialog v-model="dialogRecord" title="历史记录">
<!-- 内容区域 -->
<el-table stripe border :data="recordData" style="width: 100%">
<el-table-column prop="id" label="记录Id" width="80" />
<el-table-column prop="plan_id" label="计划Id" width="80" />
<el-table-column prop="report" label="用例详情">
<template #default="scope">
<a :href="scope.row.report">{{ scope.row.report }}</a>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" />
</el-table>
</el-dialog>
</template>
<script setup>
import { onMounted, ref } from "vue"
import { ElMessage, ElMessageBox } from "element-plus"
// 历史记录弹框
const dialogRecord = ref(false)
// 历史记录数据
const recordData = ref()
// 获取指定计划的报告
const getRecord = async (index) => {
let getData = { 'plan_id': tableData.value[index].id }
const result = await $api.get('/record/get', getData)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
recordData.value = data
} else {
ElMessage.error(msg)
}
dialogRecord.value = true
}
// 执行
const handleBuild = async (index) => {
let postData = { 'plan_id': tableData.value[index].id }
const result = await $api.post('/record/post', postData)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
} else {
ElMessage.error(msg)
}
}
// 删除
const handleDelete = (index) => {
ElMessageBox.alert('请确认是否删除,删除数据后无法找回', '警告!', {
confirmButtonText: '确认',
callback: async () => {
let deleteData = { 'id': tableData.value[index].id }
const result = await $api.post('/plan/delete', deleteData)
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
initData()
} else {
ElMessage.error(msg)
}
},
})
}
// 表格数据
const tableData = ref([])
// 初始化数据
const initData = async () => {
const result = await $api.get('/plan/get')
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
tableData.value = data
} else {
ElMessage.error(msg)
}
}
// 获取数据操作放在onMounted生命周期中
onMounted(() => {
initData()
})
</script>
<style scoped></style>
八.实现测试记录页面 Record.vue
<template>
<!-- 表格 -->
<el-table stripe border :data="tableData" style="width: 100%">
<el-table-column prop="id" label="记录Id" width="180" />
<el-table-column prop="plan_id" label="计划Id" width="180" />
<el-table-column prop="report" label="用例详情">
<template #default="scope">
<a :href="scope.row.report">{{ scope.row.report }}</a>
</template>
</el-table-column>
<el-table-column prop="create_time" label="创建时间" />
</el-table>
</template>
<script setup>
import { onMounted, ref } from "vue"
import { ElMessage, ElMessageBox } from "element-plus"
// 表格数据
const tableData = ref([])
// 初始化数据
const initData = async () => {
const result = await $api.get('/record/get')
const { code, data, msg } = result.data
// 成功时
if (code == 0) {
ElMessage.success(msg)
tableData.value = data
} else {
ElMessage.error(msg)
}
}
// 获取数据操作放在onMounted生命周期中
onMounted(() => {
initData()
})
</script>
<style scoped></style>