一、实现效果图及界面布局简单梳理
这块内容分左侧,中间,右侧三大部分
左侧:
上面一行固定内容,显示icon,名字,一个按钮。下面部分通过v-for循环读取数据库获取的测试环境列表用来展示名称。
中间:
中间内容分上中下3块,上面是input框, 编辑和显示基本信息。中间和下面是一些具体配置信息都使用**代码编辑器(Editor)**来展示数据
右侧:
一些python的全局工具函数,也使用Editor来左显示
二、页面内容具体实现
整体布局
这里使用了el-row,进行横向布局
先简单规划出整体样式
左侧
顶部标题部分
上面一个图标 + 名字 + 一个添加按钮。水平flex布局
<!-- 顶部标题 -->
<div class="title_box">
<img src="@/assets/icons/data.png" width="25">
<div class="name">测试环境</div>
<el-button size="small" icon='CirclePlus' plain>添加</el-button>
</div>
加一些scss样式
//左侧
.left_box {
height: 100%;
.title_box {
display: flex;
height: 40px;
align-items: center;
justify-content: center;
border-bottom: solid 1px #6a6a6a;
.name {
font-weight: bold;
font-size: 18px;
margin: 0 10px;
}
}
.el-menu {
border: none;
.el-menu-item {
height: 40px;
line-height: 40px;
}
}
}
下方列表展示部分
在之前部分就已经获取了环境数据,并保存到了ProjectStore的envList,直接从这读取,使用v-for循环显示name就行了
<el-menu >
<el-menu-item :index="item.id.toString()" v-for='item in pstore.envList' :key="item.id">
<img src="@/assets/icons/data.png" width="20" style="margin-right: 10px;">
<span>{{ item.name }}</span>
</el-menu-item>
</el-menu>
再加一点点样式
.el-menu {
border: none;
.el-menu-item {
height: 40px;
line-height: 40px;
}
}
中间部分
基本信息
使用el-divider 分割,也用来显示信息。
两个input输入框,双向绑定数据env_name,env_host
<el-divider content-position="center">
<span class="info_text"> 基本信息 </span>
</el-divider>
<el-input v-model="env_name" placeholder="环境名称">
<template #prepend>环境名称</template>
</el-input>
<el-input v-model="env_host" placeholder="BaseURL" style="margin-top: 5px;">
<template #prepend>BaseURL</template>
</el-input>
// 中间样式
.center_box {
padding: 5px;
.info_text {
border: dashed 1px var(--el-color-primary);
border-radius: 10px;
}
}
请求头/数据库 配置信息
这里用到了代码编辑器组件,在后面有单独写内容。
这里只需要导入,然后直接使用就行了
import Editor from '/src/components/Editor.vue'
,
el-divider内容位置放中间感觉不大好看,放左边去了,然后包裹起来不好看,就给去掉样式了
<el-divider content-position="left"><span class="info_text">请求头/数据库</span></el-divider>
<el-tabs type="border-card">
<el-tab-pane label="全局请求头">
<Editor lang="json" v-model="env_headers"></Editor>
</el-tab-pane>
<el-tab-pane label="数据库配置">
<Editor lang="json" v-model="env_db"></Editor>
</el-tab-pane>
</el-tabs>
全局变量
跟上面的请求头一样,使用代码编辑器
<el-divider content-position="left"><span class="info_text"> 全局变量</span></el-divider>
<el-tabs type="border-card">
<el-tab-pane label="全局变量">
<Editor lang="json" v-model="env_global_variable"</Editor>
</el-tab-pane>
<el-tab-pane label="调试运行变量">
<Editor lang="json" v-model="env_debug_global_variable"></Editor>
</el-tab-pane>
</el-tabs>
下方固定悬浮的按钮
使用 Affix 固钉 组件
https://element-plus.org/zh-CN/component/affix.html#affix-%E5%9B%BA%E9%92%89
<el-affix :offset="50" position="bottom">
<div class="btns" v-show='EnvInfo.id'>
<el-button type="primary" size="small">保存编辑</el-button>
<el-button type="primary" size="small">复制环境</el-button>
<el-button type="danger" size="small">删除环境</el-button>
</div>
</el-affix>
右侧工具函数部分
同样使用代码编辑器
<div class="card right_box">
<el-divider content-position="left">全局工具函数</el-divider>
<Editor lang="python" v-model="env_global_func" height='calc(100% - 50px)'></Editor>
</div>
//右侧样式
.right_box{
height: 100%;
}
页面大致就做好了,现在开始往里面添加功能。
三、函数功能
创建测试环境
这里创建,直接创建一个新的,不需要填写任何内容,创建时默认给定new Env这个名字和127.0.0.1的url。在编辑部分再重新修改内容。
给添加按钮绑定点击函数addEnv
<el-button @click='addEnv' size="small" icon='CirclePlus' plain>添加</el-button>
需要在api/module/ProjectApi下定义好接口(与后端保持一致)
import http from '@/api/index'
// ----------添加测试环境==================
async function addEnv() {
const response = await http.pro.createEnvApi({
project: pstore.pro.id,
name: "new Env",
host: "http://127.0.0.1"
})
if (response.status === 201) {
// 给出提示
ElNotification({
title: '测试环境创建成功',
type: 'success',
})
// 更新页面数据
pstore.getEnvList()
}
}
选择测试环境
菜单组件添加点击函数selectEnv
<el-menu-item @click='selectEnv(item)' :index="item.id.toString()" v-for='item in pstore.envList'
:key="item.id">
let envList = ref([])
// 默认选择第一个
onMounted(async () => {
await pstore.getEnvList()
envList.value = pstore.envList
// 组件上数据挂载完毕之后,设置一个默认选中的测试环境
if (envList.value.length > 0) {
selectEnv(envList.value[0])
}
})
// ==============页面数据==============
let env_name = ref('')
let env_host = ref('')
let env_headers = ref('{}')
let env_db = ref('[]')
let env_global_variable = ref('{}')
let env_debug_global_variable = ref('{}')
let env_global_func = ref('')
// 保存当前选择的测试环境
let EnvInfo = ref({})
function selectEnv(env) {
// 保存当前选中的测试环境
EnvInfo.value = env
// 更新页面上编辑数据的值
env_name.value = env.name
env_host.value = env.host
env_headers.value = JSON.stringify(env.headers, 0, 4) || "{}"
env_db.value = JSON.stringify(env.db, 0, 4) || "[]"
env_global_variable.value = JSON.stringify(env.global_variable, 0, 4) || "{}"
env_debug_global_variable.value = JSON.stringify(env.debug_global_variable, 0, 4) || "{}"
env_global_func.value = env.global_func
}
给el-menu添加默认激活,用来显示激活状态
<el-menu :default-active="EnvInfo.id+''">
删除测试环境
删除按钮绑定点击事件
<el-button @click='clickDeleteEnv' type="danger" size="small">删除环境</el-button>
使用ElMessageBox 做二次确认
//删除环境
function clickDeleteEnv() {
ElMessageBox.confirm(
'此操作不可恢复,确认要删除该测试环境?',
'警告', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(async () => {
const response = await http.pro.deleteEnvApi(EnvInfo.value.id)
if (response.status === 204) {
ElNotification({
title: '删除成功',
type: 'success',
})
// 更新页面数据
pstore.getEnvList()
envList.value = pstore.envList
// 这里重新选择存在的第一个环境就行了。
if (envList.value.length > 0) {
selectEnv(envList.value[0])
}
}
})
}
辅助一份测试环境
<el-button @click='copyEnv' type="primary" size="small">复制环境</el-button>
只需在名字后加一个Copy就行,其他内容不变。然后调用创建的接口新建一个
//复制测试环境
async function copyEnv() {
const params = EnvInfo.value
params.name = params.name + 'Copy'
const response = await http.pro.createEnvApi(params)
if (response.status === 201) {
// 给出提示
ElNotification({
title: '复制成功',
type: 'success',
})
// 更新页面数据
pstore.getEnvList()
envList.value = pstore.envList
}
}
保存环境
<el-button @click="saveEnv" type="primary" size="small">保存编辑</el-button>
// 保存环境
async function saveEnv() {
const env_id = EnvInfo.value.id
// 修改时传递的参数
const params = {
name: env_name.value,
host: env_host.value,
global_func: env_global_func.value,
db: JSON.parse(env_db.value),
headers: JSON.parse(env_headers.value),
global_variable: JSON.parse(env_global_variable.value),
debug_global_variable: JSON.parse(env_debug_global_variable.value),
}
const response = await http.pro.updateEnvApi(env_id, params)
if (response.status === 200) {
// 给出提示
ElNotification({
title: '保存成功',
type: 'success',
})
// 更新页面数据
pstore.getEnvList()
}
}
到此,页面功能几乎就写完了。
代码编辑器
基于 vue3-ace-editor封装的代码编辑器组件,可以用于编辑 json 数据和代码。
首先需要安装一下
npm install vue3-ace-editor
直接在项目目录的 components 中新建一个Edit.vue组件,代码如下,直接使用就行。
<template>
<VAceEditor :options="editOption" v-model:value="dataEdit" :lang="lang" :theme="theme"
:style="{ minHeight: height }" />
<el-button type="primary" size="small" @click="formatJson" v-if="lang === 'json'" plain>格式化json</el-button>
</template>
<script>
import { VAceEditor } from 'vue3-ace-editor';
import 'ace-builds/src-noconflict/snippets/json';
import 'ace-builds/src-noconflict/mode-json';
import 'ace-builds/src-noconflict/snippets/python';
import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/mode-html';
import 'ace-builds/src-noconflict/snippets/html';
import 'ace-builds/src-noconflict/theme-chrome';
import 'ace-builds/src-noconflict/theme-monokai';
import 'ace-builds/src-noconflict/theme-merbivore';
import 'ace-builds/src-noconflict/theme-twilight';
import 'ace-builds/src-noconflict/theme-gruvbox';
import 'ace-builds/src-noconflict/ext-language_tools';
import 'ace-builds/src-noconflict/worker-json'
export default {
components: {
VAceEditor
},
methods: {
formatJson() {
const jsObj = JSON.parse(this.dataEdit);
this.dataEdit = JSON.stringify(jsObj, null, 4);
}
},
props: {
lang: {
default: 'json'
},
modelValue: {
type: String,
default: '{}'
},
theme: {
default: 'merbivore'
},
height: {
default: '200px'
},
readOnly: {
default: false
}
},
emits: ['update:modelValue'],
computed: {
editOption() {
return {
enableBasicAutocompletion: true, // 启用基本自动补全
enableSnippets: true, // 启用代码段
enableLiveAutocompletion: true, // 启用实时自动提示
tabSize: 4, // tab键占用的空格的位置
fontSize: 14, // 设置字号
useWorker: false, // 使用校验语法是否正确
showPrintMargin: false, //去除编辑器里的竖线
enableMultiselect: true, // 支持鼠标选中多处
readOnly: this.readOnly, // 是否只读
showFoldWidgets: true, // 显示折叠部件
fadeFoldWidgets: true, // 淡入折叠部件
wrap: true, //换行,
};
},
dataEdit: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
}
}
}
};
</script>
<style></style>