增加测试平台功能,系统设置,支持多个角色,分配菜单
This commit is contained in:
129
.plan/MEVsZvRZopyrJGc3cNsiN.md
Normal file
129
.plan/MEVsZvRZopyrJGc3cNsiN.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# 前端实现计划:测试平台页面与接口
|
||||||
|
|
||||||
|
## 1. 现状确认
|
||||||
|
- 当前项目不是 React 18 + TypeScript,而是 **Vue 2 + Element UI + vue-router + vuex + axios**。
|
||||||
|
- 已有页面以“效能平台/造数工具”为主,目录集中在:
|
||||||
|
- `src/components/`
|
||||||
|
- `src/api/`
|
||||||
|
- `src/router/index.js`
|
||||||
|
- 已存在一套造数相关接口封装:`src/api/CreateDtapi.js`
|
||||||
|
- 已存在基础布局页:`src/components/Home.vue`
|
||||||
|
- 因此本次应遵循**现有 Vue 2 结构**落地,不直接按你给出的 React 目录硬切,否则与当前代码库不兼容。
|
||||||
|
|
||||||
|
## 2. 实施目标
|
||||||
|
按你提供的详细设计,在当前项目结构下补齐并升级前端代码,重点实现:
|
||||||
|
- 测试平台导航结构
|
||||||
|
- 项目/用例/计划/报告/造数 五大功能入口页面
|
||||||
|
- 对应 API 封装层
|
||||||
|
- 页面级列表、详情、表单、执行、报告查看等基础交互
|
||||||
|
- 复用当前 Element UI 风格,保持现有代码可运行
|
||||||
|
|
||||||
|
## 3. 实施范围拆分
|
||||||
|
|
||||||
|
### 阶段 A:基础设施与路由改造
|
||||||
|
1. 梳理现有路由与导航。
|
||||||
|
2. 扩展左侧菜单,新增模块入口:
|
||||||
|
- 项目管理
|
||||||
|
- 用例管理
|
||||||
|
- 测试计划
|
||||||
|
- 测试报告
|
||||||
|
- 造数工厂
|
||||||
|
3. 在 `src/router/index.js` 注册对应页面路由。
|
||||||
|
4. 保留现有造数页面入口,避免破坏已有功能。
|
||||||
|
|
||||||
|
### 阶段 B:API 服务层补齐
|
||||||
|
1. 新增 API 文件,按你给出的接口定义封装:
|
||||||
|
- `src/api/projectApi.js`
|
||||||
|
- `src/api/caseApi.js`
|
||||||
|
- `src/api/planApi.js`
|
||||||
|
- `src/api/reportApi.js`
|
||||||
|
- `src/api/dataFactoryApi.js`
|
||||||
|
2. 统一复用 `src/utils/request.js`。
|
||||||
|
3. 尽量映射到 REST 风格路径,例如:
|
||||||
|
- `/api/v1/projects/{projectId}/cases`
|
||||||
|
- `/api/v1/projects/{projectId}/plans`
|
||||||
|
- `/api/v1/projects/{projectId}/reports`
|
||||||
|
- `/api/v1/projects/{projectId}/data/*`
|
||||||
|
4. 对分页、详情、创建、更新、执行、导出等接口做最小可用封装。
|
||||||
|
|
||||||
|
### 阶段 C:页面骨架实现
|
||||||
|
在 `src/components/` 下新增测试平台模块页面,优先做可运行页面骨架与核心交互:
|
||||||
|
|
||||||
|
1. 项目模块
|
||||||
|
- `src/components/TestPlatform/Project/ProjectList.vue`
|
||||||
|
- `src/components/TestPlatform/Project/ProjectDetail.vue`
|
||||||
|
- `src/components/TestPlatform/Project/ProjectSettings.vue`
|
||||||
|
|
||||||
|
2. 用例模块
|
||||||
|
- `src/components/TestPlatform/Case/CaseList.vue`
|
||||||
|
- `src/components/TestPlatform/Case/CaseEditor.vue`
|
||||||
|
- `src/components/TestPlatform/Case/CaseReview.vue`
|
||||||
|
|
||||||
|
3. 测试计划模块
|
||||||
|
- `src/components/TestPlatform/Plan/PlanList.vue`
|
||||||
|
- `src/components/TestPlatform/Plan/PlanBuilder.vue`
|
||||||
|
- `src/components/TestPlatform/Plan/PlanExecute.vue`
|
||||||
|
- `src/components/TestPlatform/Plan/PlanProgress.vue`
|
||||||
|
|
||||||
|
4. 报告模块
|
||||||
|
- `src/components/TestPlatform/Report/ReportList.vue`
|
||||||
|
- `src/components/TestPlatform/Report/ReportViewer.vue`
|
||||||
|
|
||||||
|
5. 造数模块升级
|
||||||
|
- 保留现有 `CreateData/*`
|
||||||
|
- 新增测试平台语义下页面:
|
||||||
|
- `src/components/TestPlatform/DataFactory/BuilderList.vue`
|
||||||
|
- `src/components/TestPlatform/DataFactory/BuilderEditor.vue`
|
||||||
|
- `src/components/TestPlatform/DataFactory/TaskHistory.vue`
|
||||||
|
- `src/components/TestPlatform/DataFactory/MockService.vue`
|
||||||
|
|
||||||
|
### 阶段 D:公共组件抽取
|
||||||
|
新增可复用基础组件,减少页面重复:
|
||||||
|
- `src/components/TestPlatform/common/JsonViewer.vue`
|
||||||
|
- `src/components/TestPlatform/common/KeyValueDescriptions.vue`
|
||||||
|
- `src/components/TestPlatform/common/PageSection.vue`
|
||||||
|
|
||||||
|
### 阶段 E:联调友好处理
|
||||||
|
1. 页面初版支持后端未完成时的容错:
|
||||||
|
- 空数据态
|
||||||
|
- 请求失败提示
|
||||||
|
- 默认 projectId 占位
|
||||||
|
2. 不引入新依赖,避免额外安装。
|
||||||
|
3. 保持接口函数独立,后续联调时只需替换 URL 或参数格式。
|
||||||
|
|
||||||
|
## 4. 执行顺序
|
||||||
|
1. 先改路由与主页导航。
|
||||||
|
2. 再补 API 封装。
|
||||||
|
3. 再按模块逐步落页面:
|
||||||
|
- 项目
|
||||||
|
- 用例
|
||||||
|
- 计划
|
||||||
|
- 报告
|
||||||
|
- 造数
|
||||||
|
4. 最后做公共组件复用与样式收口。
|
||||||
|
|
||||||
|
## 5. 每阶段交付物
|
||||||
|
- 阶段 A:可点击进入的菜单与路由
|
||||||
|
- 阶段 B:完整接口文件
|
||||||
|
- 阶段 C:各模块页面可打开、可发请求、可展示表格/表单
|
||||||
|
- 阶段 D:重复 UI 收敛
|
||||||
|
- 阶段 E:整体自检,确保不破坏原有功能
|
||||||
|
|
||||||
|
## 6. 风险与约束
|
||||||
|
- 当前代码库是旧版 Vue 2 工程,不能直接生成 React/TSX 代码。
|
||||||
|
- 你的接口设计是目标态,实际后端返回结构可能与设计不完全一致,因此前端会适当做字段兼容。
|
||||||
|
- 由于当前 `request.js` 已固定 `baseURL`,本次先复用,不额外改动接口基础配置,避免影响现有功能。
|
||||||
|
|
||||||
|
## 7. 本次建议执行策略
|
||||||
|
按“先骨架、再细化”的方式推进:
|
||||||
|
- 第一步:完成导航、路由、API 文件、页面骨架
|
||||||
|
- 第二步:优先把列表页和详情/编辑页做成可联调版本
|
||||||
|
- 第三步:再补计划执行、报告查看、造数编排等复杂交互
|
||||||
|
|
||||||
|
## 8. 下一步实际执行内容
|
||||||
|
获批后,我将从以下顺序开始落地:
|
||||||
|
1. 修改 `src/components/Home.vue` 增加测试平台菜单
|
||||||
|
2. 修改 `src/router/index.js` 注册测试平台路由
|
||||||
|
3. 新增五类 API 文件
|
||||||
|
4. 新增项目/用例/计划/报告/造数页面骨架
|
||||||
|
5. 检查并修正页面间跳转与基础请求
|
||||||
@@ -23,7 +23,7 @@ pipeline {
|
|||||||
DEPLOY_PORT = '22'
|
DEPLOY_PORT = '22'
|
||||||
DEPLOY_USER = 'user'
|
DEPLOY_USER = 'user'
|
||||||
CONTAINER_NAME = 'effekt-interface-frontend'
|
CONTAINER_NAME = 'effekt-interface-frontend'
|
||||||
HOST_PORT = '8080'
|
HOST_PORT = '8881'
|
||||||
CONTAINER_PORT = '80'
|
CONTAINER_PORT = '80'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,12 @@ module.exports = {
|
|||||||
// Paths
|
// Paths
|
||||||
assetsSubDirectory: 'static',
|
assetsSubDirectory: 'static',
|
||||||
assetsPublicPath: '/',
|
assetsPublicPath: '/',
|
||||||
proxyTable: {},
|
proxyTable: {
|
||||||
|
'/it/api': {
|
||||||
|
target: 'http://192.168.11.46:5010',
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Various Dev Server settings
|
// Various Dev Server settings
|
||||||
// host: '172.19.29.63', // can be overwritten by process.env.HOST
|
// host: '172.19.29.63', // can be overwritten by process.env.HOST
|
||||||
@@ -18,7 +23,7 @@ module.exports = {
|
|||||||
port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||||
autoOpenBrowser: false,
|
autoOpenBrowser: false,
|
||||||
errorOverlay: true,
|
errorOverlay: true,
|
||||||
notifyOnErrors: true,
|
notifyOnErrors: false,
|
||||||
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
26
src/App.vue
26
src/App.vue
@@ -5,16 +5,30 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { userGetAdvance } from '@/api/Userapi'
|
import { getRoleList } from '@/api/rbacApi'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
mounted() {
|
mounted() {
|
||||||
const userinfo = JSON.parse(localStorage.getItem('userinfo'))
|
const authUser = JSON.parse(localStorage.getItem('authUser') || 'null')
|
||||||
if (userinfo) {
|
const userMenus = JSON.parse(localStorage.getItem('userMenus') || '[]')
|
||||||
userGetAdvance({ userId: userinfo.userId }).then(data => {
|
if (authUser) {
|
||||||
this.$store.commit('SetRole', data.data.userStatus)
|
this.$store.commit('SetCurrentUser', authUser)
|
||||||
})
|
this.$store.commit('SetRole', authUser.roleIds || [])
|
||||||
|
this.$store.commit('SetUserMenus', userMenus)
|
||||||
|
this.loadUserMenus(authUser)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadUserMenus(authUser) {
|
||||||
|
const roleId = authUser && authUser.roleIds && authUser.roleIds.length ? authUser.roleIds[0] : undefined
|
||||||
|
if (!roleId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getRoleList({ roleId }).then(res => {
|
||||||
|
const menus = res && Array.isArray(res.data) ? res.data : []
|
||||||
|
this.$store.commit('SetUserMenus', menus)
|
||||||
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import request from '@/utils/request'
|
|||||||
// 造数任务列表
|
// 造数任务列表
|
||||||
export function ItApiList(params) {
|
export function ItApiList(params) {
|
||||||
return request({
|
return request({
|
||||||
url: '/it/api/list',
|
url: '/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
@@ -12,7 +12,7 @@ export function ItApiList(params) {
|
|||||||
// 执行造数任务
|
// 执行造数任务
|
||||||
export function ItApiRun(data) {
|
export function ItApiRun(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/it/api/execute',
|
url: '/execute',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
@@ -21,7 +21,7 @@ export function ItApiRun(data) {
|
|||||||
// 新增/修改造数任务
|
// 新增/修改造数任务
|
||||||
export function ItApiCreate(data) {
|
export function ItApiCreate(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/it/api/create',
|
url: '/create',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
@@ -30,7 +30,7 @@ export function ItApiCreate(data) {
|
|||||||
// 造数任务详情
|
// 造数任务详情
|
||||||
export function ItApiDetail(params) {
|
export function ItApiDetail(params) {
|
||||||
return request({
|
return request({
|
||||||
url: '/it/api/detail',
|
url: '/detail',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
@@ -39,7 +39,7 @@ export function ItApiDetail(params) {
|
|||||||
// 删除造数任务
|
// 删除造数任务
|
||||||
export function ItApiDelete(data) {
|
export function ItApiDelete(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/it/api/delete',
|
url: '/delete',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
/*注册模块*/
|
/*注册模块*/
|
||||||
//注册提交用户名
|
|
||||||
export function Register(data) {
|
export function Register(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/Register',
|
url: '/auth/register',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*登录功能*/
|
/*登录功能*/
|
||||||
//登录
|
|
||||||
export function Login(data) {
|
export function Login(data) {
|
||||||
return request({
|
return request({
|
||||||
url: '/Login',
|
url: '/auth/login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
|||||||
122
src/api/caseApi.js
Normal file
122
src/api/caseApi.js
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getModuleTree(params) {
|
||||||
|
return request({
|
||||||
|
url: '/module/tree',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createModule(data) {
|
||||||
|
return request({
|
||||||
|
url: '/module/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateModule(data) {
|
||||||
|
return request({
|
||||||
|
url: '/module/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteModule(data) {
|
||||||
|
return request({
|
||||||
|
url: '/module/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCaseList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/case/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCaseDetail(projectId, caseId) {
|
||||||
|
return request({
|
||||||
|
url: '/case/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: caseId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCase(projectId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/case/create',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCase(projectId, caseId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/case/update',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, id: caseId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCase(projectId, caseId) {
|
||||||
|
return request({
|
||||||
|
url: '/case/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: caseId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCaseSnapshot(projectId, caseId) {
|
||||||
|
return request({
|
||||||
|
url: '/case/snapshot/create',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
project_id: projectId,
|
||||||
|
case_id: caseId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCaseSnapshotList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/case/snapshot/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitCaseReview(projectId, caseId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/case/review/create',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, case_id: caseId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCaseReview(data) {
|
||||||
|
return request({
|
||||||
|
url: '/case/review/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCaseReviewList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/case/review/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
66
src/api/dataFactoryApi.js
Normal file
66
src/api/dataFactoryApi.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getBuilderList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBuilderDetail(projectId, builderId) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: builderId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createBuilder(projectId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/create',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateBuilder(projectId, builderId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/update',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, id: builderId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteBuilder(projectId, builderId) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: builderId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function executeBuilder(projectId, builderId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/data/builder/execute',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, builder_id: builderId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataTaskStatus(projectId, taskId) {
|
||||||
|
return request({
|
||||||
|
url: '/data/task/status',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
task_id: taskId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
98
src/api/planApi.js
Normal file
98
src/api/planApi.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getPlanList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPlan(projectId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/create',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updatePlan(projectId, planId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/update',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, id: planId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deletePlan(projectId, planId) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: planId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanDetail(projectId, planId) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: planId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPlanRound(data) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/round/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanRoundList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/round/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addPlanCases(projectId, planId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/case/add',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, plan_id: planId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanCaseList(projectId, planId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/case/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, plan_id: planId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function executePlanCase(projectId, planId, planCaseId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/case/execute',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId, plan_id: planId, id: planCaseId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlanProgress(projectId, planId) {
|
||||||
|
return request({
|
||||||
|
url: '/plan/progress',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
plan_id: planId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
33
src/api/productApi.js
Normal file
33
src/api/productApi.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getProductList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/product/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createProduct(data) {
|
||||||
|
return request({
|
||||||
|
url: '/product/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateProduct(data) {
|
||||||
|
return request({
|
||||||
|
url: '/product/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteProduct(data) {
|
||||||
|
return request({
|
||||||
|
url: '/product/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
91
src/api/projectApi.js
Normal file
91
src/api/projectApi.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getProjectList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/project/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectDetail(projectId) {
|
||||||
|
return request({
|
||||||
|
url: '/project/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
id: projectId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createProject(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateProject(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteProject(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectMembers(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/project/member/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createProjectMember(data) {
|
||||||
|
return request({
|
||||||
|
url: '/project/member/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProjectEnvironments(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/environment/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEnvironment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/environment/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateEnvironment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/environment/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteEnvironment(data) {
|
||||||
|
return request({
|
||||||
|
url: '/environment/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
169
src/api/rbacApi.js
Normal file
169
src/api/rbacApi.js
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getRoleList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/role/list',
|
||||||
|
method: 'get',
|
||||||
|
params: params || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRolePageList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/role/page/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleDetail(roleId) {
|
||||||
|
return request({
|
||||||
|
url: '/role/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { roleId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRole(data) {
|
||||||
|
return request({
|
||||||
|
url: '/role/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateRole(data) {
|
||||||
|
return request({
|
||||||
|
url: '/role/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteRole(data) {
|
||||||
|
return request({
|
||||||
|
url: '/role/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleMenuTree(params) {
|
||||||
|
return request({
|
||||||
|
url: '/role/menu/tree',
|
||||||
|
method: 'get',
|
||||||
|
params: params || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoleMenuList(roleId) {
|
||||||
|
return request({
|
||||||
|
url: '/role/menu/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { roleId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assignRoleMenus(data) {
|
||||||
|
return request({
|
||||||
|
url: '/role/menu/assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserList(params) {
|
||||||
|
return request({
|
||||||
|
url: '/user/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserDetail(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/user/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteUser(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserRoleList(userId) {
|
||||||
|
return request({
|
||||||
|
url: '/user/role/list',
|
||||||
|
method: 'get',
|
||||||
|
params: { userId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assignUserRoles(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/role/assign',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMenuTree(params) {
|
||||||
|
return request({
|
||||||
|
url: '/menu/tree',
|
||||||
|
method: 'get',
|
||||||
|
params: params || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMenuDetail(menuId) {
|
||||||
|
return request({
|
||||||
|
url: '/menu/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { menuId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMenu(data) {
|
||||||
|
return request({
|
||||||
|
url: '/menu/create',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateMenu(data) {
|
||||||
|
return request({
|
||||||
|
url: '/menu/update',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteMenu(data) {
|
||||||
|
return request({
|
||||||
|
url: '/menu/delete',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
28
src/api/reportApi.js
Normal file
28
src/api/reportApi.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export function getReportList(projectId, params) {
|
||||||
|
return request({
|
||||||
|
url: '/report/list',
|
||||||
|
method: 'get',
|
||||||
|
params: Object.assign({ project_id: projectId, pageNo: 1, pageSize: 10 }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateReport(projectId, data) {
|
||||||
|
return request({
|
||||||
|
url: '/report/generate',
|
||||||
|
method: 'post',
|
||||||
|
data: Object.assign({ project_id: projectId }, data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getReportDetail(projectId, reportId) {
|
||||||
|
return request({
|
||||||
|
url: '/report/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
id: reportId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -14,24 +14,58 @@
|
|||||||
<i class="el-icon-house"></i>
|
<i class="el-icon-house"></i>
|
||||||
<span slot="title">首页</span>
|
<span slot="title">首页</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
<el-submenu index="test-platform">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="el-icon-s-operation"></i>
|
||||||
|
<span slot="title">测试协作工作台</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/test-platform/products">产品管理</el-menu-item>
|
||||||
|
<el-menu-item index="/test-platform/projects">项目管理</el-menu-item>
|
||||||
|
<el-menu-item index="/test-platform/cases">用例管理</el-menu-item>
|
||||||
|
<el-menu-item index="/test-platform/plans">测试计划</el-menu-item>
|
||||||
|
<el-menu-item index="/test-platform/reports">测试报告</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
<el-submenu index="create-tool">
|
<el-submenu index="create-tool">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<i class="el-icon-setting"></i>
|
<i class="el-icon-setting"></i>
|
||||||
<span slot="title">造数工具</span>
|
<span slot="title">测试工具</span>
|
||||||
</template>
|
</template>
|
||||||
<el-menu-item index="/create/data">数据库造数</el-menu-item>
|
<el-menu-item index="/create/data">数据库造数</el-menu-item>
|
||||||
<el-menu-item index="/create/interface">接口造数</el-menu-item>
|
<el-menu-item index="/create/interface">接口造数</el-menu-item>
|
||||||
|
<el-menu-item index="/test-platform/data-factory/builders">造数工厂</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
<el-submenu index="system-manage">
|
||||||
|
<template slot="title">
|
||||||
|
<i class="el-icon-user-solid"></i>
|
||||||
|
<span slot="title">系统管理</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/system/roles">角色管理</el-menu-item>
|
||||||
|
<el-menu-item index="/system/users">用户管理</el-menu-item>
|
||||||
|
<el-menu-item index="/system/menus">菜单管理</el-menu-item>
|
||||||
</el-submenu>
|
</el-submenu>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</div>
|
</div>
|
||||||
<el-container>
|
<el-container>
|
||||||
<el-header class="header" style="background-color: rgba(230, 226, 215, 0.9)">
|
<el-header class="header" style="background-color: rgba(230, 226, 215, 0.9)">
|
||||||
<div class="header-icon" style="float: left;padding-left: 15px;padding-right: 15px">
|
<div class="header-left">
|
||||||
<i v-if="isCollapse" class="el-icon-s-unfold" style="font-size: 20px" @click="setCollapse"></i>
|
<div class="header-icon">
|
||||||
<i v-else class="el-icon-s-fold" style="font-size: 20px" @click="setCollapse"></i>
|
<i v-if="isCollapse" class="el-icon-s-unfold" style="font-size: 20px" @click="setCollapse"></i>
|
||||||
|
<i v-else class="el-icon-s-fold" style="font-size: 20px" @click="setCollapse"></i>
|
||||||
|
</div>
|
||||||
|
<div class="system-name">
|
||||||
|
<span>{{ systemName }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="system-name" style="float: left">
|
<div class="header-user">
|
||||||
<span>{{ systemName }}</span>
|
<el-dropdown v-if="currentUser" trigger="click" @command="handleUserCommand">
|
||||||
|
<span class="user-name-dropdown">
|
||||||
|
{{ displayName }}<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
<span v-else class="login-label" @click="goLogin">登录</span>
|
||||||
</div>
|
</div>
|
||||||
</el-header>
|
</el-header>
|
||||||
<el-main>
|
<el-main>
|
||||||
@@ -51,9 +85,77 @@ export default {
|
|||||||
systemName: '效能平台'
|
systemName: '效能平台'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
currentUser() {
|
||||||
|
return this.$store.state.currentUser
|
||||||
|
},
|
||||||
|
userMenus() {
|
||||||
|
return this.$store.state.userMenus || []
|
||||||
|
},
|
||||||
|
displayMenus() {
|
||||||
|
if (!this.userMenus.length) {
|
||||||
|
return [{ name: '首页', path: '/effekt', icon: 'el-icon-house', children: [] }]
|
||||||
|
}
|
||||||
|
return this.filterMenus(this.userMenus)
|
||||||
|
},
|
||||||
|
displayName() {
|
||||||
|
if (!this.currentUser) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return this.currentUser.username || this.currentUser.realName || '未命名用户'
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setCollapse() {
|
setCollapse() {
|
||||||
this.isCollapse = !this.isCollapse
|
this.isCollapse = !this.isCollapse
|
||||||
|
},
|
||||||
|
goLogin() {
|
||||||
|
this.$router.push({ name: 'login' })
|
||||||
|
},
|
||||||
|
menuKey(item) {
|
||||||
|
return String(item.menuId || item.id || item.path || item.name)
|
||||||
|
},
|
||||||
|
menuIndex(item) {
|
||||||
|
return String(item.path || item.code || item.menuId || item.id || item.name)
|
||||||
|
},
|
||||||
|
menuPath(item) {
|
||||||
|
const pathMap = {
|
||||||
|
'/system/role': '/system/roles',
|
||||||
|
'/system/user': '/system/users',
|
||||||
|
'/system/menu': '/system/menus'
|
||||||
|
}
|
||||||
|
return pathMap[item.path] || item.path || '/effekt'
|
||||||
|
},
|
||||||
|
menuIcon(item) {
|
||||||
|
const iconMap = {
|
||||||
|
setting: 'el-icon-setting',
|
||||||
|
peoples: 'el-icon-user-solid',
|
||||||
|
user: 'el-icon-user',
|
||||||
|
lock: 'el-icon-lock',
|
||||||
|
menu: 'el-icon-menu'
|
||||||
|
}
|
||||||
|
return iconMap[item.icon] || (item.icon && item.icon.indexOf('el-icon-') === 0 ? item.icon : 'el-icon-menu')
|
||||||
|
},
|
||||||
|
filterMenus(menus) {
|
||||||
|
return (menus || []).filter(item => item.visible !== 0 && item.status !== 0).map(item => {
|
||||||
|
const children = this.filterMenus(item.children || [])
|
||||||
|
return Object.assign({}, item, { children })
|
||||||
|
}).filter(item => {
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !!item.path && !!this.menuPath(item)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleUserCommand(command) {
|
||||||
|
if (command === 'logout') {
|
||||||
|
localStorage.removeItem('authUser')
|
||||||
|
localStorage.removeItem('accessToken')
|
||||||
|
localStorage.removeItem('userMenus')
|
||||||
|
this.$store.commit('ClearCurrentUser')
|
||||||
|
this.$message.success('已退出登录')
|
||||||
|
this.$router.push({ name: 'login' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,14 +175,37 @@ export default {
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
height: 60px;
|
height: 60px;
|
||||||
line-height: 60px
|
line-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-header {
|
.el-header {
|
||||||
padding-left: 0;
|
padding: 0 20px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-menu-vertical-demo {
|
.el-menu-vertical-demo {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icon {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-user {
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-label {
|
||||||
|
color: #409EFF;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
322
src/components/System/MenuManage.vue
Normal file
322
src/components/System/MenuManage.vue
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="菜单管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="openCreate(0)">新建菜单</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable placeholder="全部状态">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchTree">查询</el-button>
|
||||||
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table
|
||||||
|
v-loading="loading"
|
||||||
|
:data="tableData"
|
||||||
|
border
|
||||||
|
row-key="id"
|
||||||
|
default-expand-all
|
||||||
|
:tree-props="{ children: 'children' }"
|
||||||
|
style="width: 100%; margin-top: 16px;">
|
||||||
|
<el-table-column prop="name" label="菜单名称" min-width="180"></el-table-column>
|
||||||
|
<el-table-column prop="code" label="菜单编码" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="type" label="类型" width="90">
|
||||||
|
<template slot-scope="scope">{{ formatType(scope.row.type) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="path" label="路径" min-width="180" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="component" label="组件" min-width="180" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column label="权限标识" min-width="160" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">{{ scope.row.permission_code || scope.row.permissionCode }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sort" label="排序" width="80"></el-table-column>
|
||||||
|
<el-table-column prop="visible" label="显示" width="80">
|
||||||
|
<template slot-scope="scope">{{ scope.row.visible === 0 ? '否' : '是' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="80">
|
||||||
|
<template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="220" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openCreate(scope.row.id)">新增下级</el-button>
|
||||||
|
<el-button type="text" @click="openEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog :title="dialogMode === 'create' ? '新建菜单' : '编辑菜单'" :visible.sync="dialogVisible" width="620px" @close="resetDialogForm">
|
||||||
|
<el-form ref="menuForm" :model="menuForm" :rules="menuRules" label-width="100px" size="small">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="上级菜单" prop="parentId">
|
||||||
|
<el-input :value="getParentName(menuForm.parentId)" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="菜单类型" prop="type">
|
||||||
|
<el-select v-model="menuForm.type" placeholder="请选择类型" style="width: 100%;">
|
||||||
|
<el-option label="目录" :value="1"></el-option>
|
||||||
|
<el-option label="菜单" :value="2"></el-option>
|
||||||
|
<el-option label="按钮" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="菜单名称" prop="name">
|
||||||
|
<el-input v-model.trim="menuForm.name" maxlength="64" placeholder="请输入菜单名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="菜单编码" prop="code">
|
||||||
|
<el-input v-model.trim="menuForm.code" maxlength="64" placeholder="请输入菜单编码"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="访问路径" prop="path">
|
||||||
|
<el-input v-model.trim="menuForm.path" maxlength="128" placeholder="请输入访问路径"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="组件路径" prop="component">
|
||||||
|
<el-input v-model.trim="menuForm.component" maxlength="128" placeholder="请输入组件路径"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="图标" prop="icon">
|
||||||
|
<el-input v-model.trim="menuForm.icon" maxlength="64" placeholder="请输入图标"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="权限标识" prop="permissionCode">
|
||||||
|
<el-input v-model.trim="menuForm.permissionCode" maxlength="64" placeholder="请输入权限标识"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="排序" prop="sort">
|
||||||
|
<el-input-number v-model="menuForm.sort" :min="0" :max="9999" controls-position="right" style="width: 100%;"></el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="是否显示" prop="visible">
|
||||||
|
<el-select v-model="menuForm.visible" placeholder="请选择" style="width: 100%;">
|
||||||
|
<el-option label="是" :value="1"></el-option>
|
||||||
|
<el-option label="否" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="menuForm.status" placeholder="请选择" style="width: 100%;">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="submitting" @click="submitForm">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createMenu, deleteMenu, getMenuTree, updateMenu } from '@/api/rbacApi'
|
||||||
|
|
||||||
|
const getDefaultForm = () => ({
|
||||||
|
menuId: undefined,
|
||||||
|
parentId: 0,
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
type: 2,
|
||||||
|
path: '',
|
||||||
|
component: '',
|
||||||
|
icon: '',
|
||||||
|
permissionCode: '',
|
||||||
|
sort: 0,
|
||||||
|
visible: 1,
|
||||||
|
status: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MenuManage',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogMode: 'create',
|
||||||
|
queryForm: {
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
menuForm: getDefaultForm(),
|
||||||
|
menuRules: {
|
||||||
|
name: [{ required: true, message: '请输入菜单名称', trigger: 'blur' }],
|
||||||
|
code: [{ required: true, message: '请输入菜单编码', trigger: 'blur' }],
|
||||||
|
type: [{ required: true, message: '请选择菜单类型', trigger: 'change' }],
|
||||||
|
sort: [{ required: true, message: '请输入排序', trigger: 'change' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
},
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchTree() {
|
||||||
|
this.loading = true
|
||||||
|
getMenuTree({ status: this.queryForm.status }).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || []
|
||||||
|
this.tableData = Array.isArray(data) ? data : (data.list || data.items || [])
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetSearch() {
|
||||||
|
this.queryForm = {
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.fetchTree()
|
||||||
|
},
|
||||||
|
formatType(type) {
|
||||||
|
const map = {
|
||||||
|
1: '目录',
|
||||||
|
2: '菜单',
|
||||||
|
3: '按钮'
|
||||||
|
}
|
||||||
|
return map[type] || type
|
||||||
|
},
|
||||||
|
flattenMenus(list, result) {
|
||||||
|
(list || []).forEach(item => {
|
||||||
|
result.push(item)
|
||||||
|
if (item.children && item.children.length) {
|
||||||
|
this.flattenMenus(item.children, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getParentName(parentId) {
|
||||||
|
if (!parentId) {
|
||||||
|
return '顶级菜单'
|
||||||
|
}
|
||||||
|
const result = []
|
||||||
|
this.flattenMenus(this.tableData, result)
|
||||||
|
const target = result.find(item => item.id === parentId)
|
||||||
|
return target ? target.name : '顶级菜单'
|
||||||
|
},
|
||||||
|
openCreate(parentId) {
|
||||||
|
this.dialogMode = 'create'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.menuForm = Object.assign(getDefaultForm(), { parentId: parentId || 0, sort: 0 })
|
||||||
|
if (this.$refs.menuForm) {
|
||||||
|
this.$refs.menuForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit(row) {
|
||||||
|
this.dialogMode = 'edit'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.menuForm = Object.assign(getDefaultForm(), row, {
|
||||||
|
menuId: row.menuId || row.id,
|
||||||
|
parentId: row.parentId !== undefined ? row.parentId : (row.parent_id || 0),
|
||||||
|
permissionCode: row.permissionCode !== undefined ? row.permissionCode : (row.permission_code || ''),
|
||||||
|
sort: row.sort !== undefined && row.sort !== null ? Number(row.sort) : 0
|
||||||
|
})
|
||||||
|
if (this.$refs.menuForm) {
|
||||||
|
this.$refs.menuForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetDialogForm() {
|
||||||
|
this.menuForm = getDefaultForm()
|
||||||
|
this.submitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.menuForm) {
|
||||||
|
this.$refs.menuForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
this.$refs.menuForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitting = true
|
||||||
|
const basePayload = {
|
||||||
|
parentId: Number(this.menuForm.parentId || 0),
|
||||||
|
name: this.menuForm.name,
|
||||||
|
code: this.menuForm.code,
|
||||||
|
type: Number(this.menuForm.type),
|
||||||
|
path: this.menuForm.path,
|
||||||
|
component: this.menuForm.component,
|
||||||
|
icon: this.menuForm.icon,
|
||||||
|
permissionCode: this.menuForm.permissionCode,
|
||||||
|
sort: Number(this.menuForm.sort || 0),
|
||||||
|
visible: Number(this.menuForm.visible),
|
||||||
|
status: Number(this.menuForm.status)
|
||||||
|
}
|
||||||
|
const payload = this.dialogMode === 'create'
|
||||||
|
? basePayload
|
||||||
|
: Object.assign({ menuId: Number(this.menuForm.menuId) }, basePayload)
|
||||||
|
const request = this.dialogMode === 'create'
|
||||||
|
? createMenu(payload)
|
||||||
|
: updateMenu(payload)
|
||||||
|
request.then(res => {
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success((res && res.message) || (this.dialogMode === 'create' ? '菜单创建成功' : '菜单更新成功'))
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.fetchTree()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error((res && res.message) || (this.dialogMode === 'create' ? '菜单创建失败' : '菜单更新失败'))
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认删除该菜单吗?', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteMenu({ menuId: row.menuId || row.id }).then(res => {
|
||||||
|
if (res && res.code !== 20000) {
|
||||||
|
this.$message.error((res && res.message) || '菜单删除失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.success((res && res.message) || '菜单删除成功')
|
||||||
|
this.fetchTree()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchTree()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
353
src/components/System/RoleManage.vue
Normal file
353
src/components/System/RoleManage.vue
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="角色管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="openCreate">新建角色</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryForm.keyword" placeholder="角色名称" clearable @keyup.enter.native="handleSearch"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable placeholder="全部状态">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="width: 100%; margin-top: 16px;">
|
||||||
|
<el-table-column prop="code" label="角色编码" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="角色名称" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="description" label="描述" min-width="220" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="is_system" label="系统角色" width="100">
|
||||||
|
<template slot-scope="scope">{{ (scope.row.is_system !== undefined ? scope.row.is_system : scope.row.isSystem) === 1 ? '是' : '否' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="240" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openAssignMenus(scope.row)">分配菜单</el-button>
|
||||||
|
<el-button type="text" @click="openEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog :title="dialogMode === 'create' ? '新建角色' : '编辑角色'" :visible.sync="dialogVisible" width="520px" @close="resetDialogForm">
|
||||||
|
<el-form ref="roleForm" :model="roleForm" :rules="roleRules" label-width="94px" size="small">
|
||||||
|
<el-form-item label="角色编码" prop="code">
|
||||||
|
<el-input v-model.trim="roleForm.code" maxlength="64" placeholder="请输入角色编码" :disabled="dialogMode === 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角色名称" prop="name">
|
||||||
|
<el-input v-model.trim="roleForm.name" maxlength="64" placeholder="请输入角色名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="roleForm.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="系统角色" prop="isSystem">
|
||||||
|
<el-select v-model="roleForm.isSystem" placeholder="请选择" style="width: 100%;" :disabled="dialogMode === 'edit'">
|
||||||
|
<el-option label="否" :value="0"></el-option>
|
||||||
|
<el-option label="是" :value="1"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述" prop="description">
|
||||||
|
<el-input v-model.trim="roleForm.description" type="textarea" :rows="4" maxlength="255" show-word-limit placeholder="请输入描述"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="submitting" @click="submitForm">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="分配菜单权限" :visible.sync="assignDialogVisible" width="620px" @close="resetAssignForm">
|
||||||
|
<el-form label-width="90px" size="small">
|
||||||
|
<el-form-item label="角色">
|
||||||
|
<el-input :value="assignRoleName" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="菜单权限">
|
||||||
|
<el-tree
|
||||||
|
ref="menuTree"
|
||||||
|
:data="menuTreeData"
|
||||||
|
node-key="id"
|
||||||
|
show-checkbox
|
||||||
|
default-expand-all
|
||||||
|
check-strictly
|
||||||
|
:props="treeProps">
|
||||||
|
</el-tree>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="assignDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="assignSubmitting" @click="submitAssignMenus">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { assignRoleMenus, createRole, deleteRole, getMenuTree, getRoleMenuList, getRoleMenuTree, updateRole } from '@/api/rbacApi'
|
||||||
|
|
||||||
|
const getDefaultForm = () => ({
|
||||||
|
roleId: undefined,
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
status: 1,
|
||||||
|
isSystem: 0,
|
||||||
|
createdBy: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RoleManage',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
assignSubmitting: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
assignDialogVisible: false,
|
||||||
|
dialogMode: 'create',
|
||||||
|
queryForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
roleForm: getDefaultForm(),
|
||||||
|
roleRules: {
|
||||||
|
code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }],
|
||||||
|
name: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
},
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
tableData: [],
|
||||||
|
assignRoleId: undefined,
|
||||||
|
assignRoleName: '',
|
||||||
|
menuTreeData: [],
|
||||||
|
treeProps: {
|
||||||
|
children: 'children',
|
||||||
|
label: 'name'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getRolePageList({
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
keyword: this.queryForm.keyword,
|
||||||
|
status: this.queryForm.status
|
||||||
|
}).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.tableData = Array.isArray(data) ? data : (data.items || data.list || data.data || [])
|
||||||
|
this.total = Array.isArray(data) ? data.length : (data.total || data.totalCount || this.tableData.length)
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
resetSearch() {
|
||||||
|
this.queryForm = {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNo = val
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
openCreate() {
|
||||||
|
this.dialogMode = 'create'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.roleForm = getDefaultForm()
|
||||||
|
if (this.$refs.roleForm) {
|
||||||
|
this.$refs.roleForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit(row) {
|
||||||
|
this.dialogMode = 'edit'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.roleForm = Object.assign(getDefaultForm(), row, {
|
||||||
|
roleId: row.roleId || row.id,
|
||||||
|
isSystem: row.isSystem !== undefined ? row.isSystem : (row.is_system || 0)
|
||||||
|
})
|
||||||
|
if (this.$refs.roleForm) {
|
||||||
|
this.$refs.roleForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openAssignMenus(row) {
|
||||||
|
this.assignRoleId = row.roleId || row.id
|
||||||
|
this.assignRoleName = row.name
|
||||||
|
this.assignDialogVisible = true
|
||||||
|
Promise.all([getMenuTree({}), getRoleMenuList(this.assignRoleId)]).then(([menuRes, assignRes]) => {
|
||||||
|
const menuData = menuRes && menuRes.data ? menuRes.data : menuRes || []
|
||||||
|
this.menuTreeData = Array.isArray(menuData) ? menuData : (menuData.list || menuData.items || [])
|
||||||
|
const assignData = assignRes && assignRes.data ? assignRes.data : assignRes || {}
|
||||||
|
const menuIds = Array.isArray(assignData)
|
||||||
|
? assignData.map(item => item.menuId || item.id)
|
||||||
|
: (assignData.menuIds || assignData.menu_ids || [])
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.menuTree) {
|
||||||
|
this.$refs.menuTree.setCheckedKeys(menuIds)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {
|
||||||
|
this.menuTreeData = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetDialogForm() {
|
||||||
|
this.roleForm = getDefaultForm()
|
||||||
|
this.submitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.roleForm) {
|
||||||
|
this.$refs.roleForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetAssignForm() {
|
||||||
|
this.assignSubmitting = false
|
||||||
|
this.assignRoleId = undefined
|
||||||
|
this.assignRoleName = ''
|
||||||
|
this.menuTreeData = []
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
this.$refs.roleForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitting = true
|
||||||
|
const payload = this.dialogMode === 'create'
|
||||||
|
? this.roleForm
|
||||||
|
: {
|
||||||
|
roleId: this.roleForm.roleId,
|
||||||
|
name: this.roleForm.name,
|
||||||
|
description: this.roleForm.description,
|
||||||
|
status: this.roleForm.status
|
||||||
|
}
|
||||||
|
const request = this.dialogMode === 'create'
|
||||||
|
? createRole(payload)
|
||||||
|
: updateRole(payload)
|
||||||
|
request.then(res => {
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success((res && res.message) || (this.dialogMode === 'create' ? '角色创建成功' : '角色更新成功'))
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error((res && res.message) || (this.dialogMode === 'create' ? '角色创建失败' : '角色更新失败'))
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitAssignMenus() {
|
||||||
|
this.assignSubmitting = true
|
||||||
|
const checkedKeys = this.$refs.menuTree ? this.$refs.menuTree.getCheckedKeys() : []
|
||||||
|
const halfCheckedKeys = this.$refs.menuTree ? this.$refs.menuTree.getHalfCheckedKeys() : []
|
||||||
|
const menuIds = Array.from(new Set([].concat(checkedKeys, halfCheckedKeys)))
|
||||||
|
assignRoleMenus({
|
||||||
|
roleId: this.assignRoleId,
|
||||||
|
menuIds
|
||||||
|
}).then(res => {
|
||||||
|
if (res && res.code !== 20000) {
|
||||||
|
this.$message.error((res && res.message) || '分配菜单失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.success((res && res.message) || '分配菜单成功')
|
||||||
|
this.assignDialogVisible = false
|
||||||
|
}).finally(() => {
|
||||||
|
this.assignSubmitting = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认删除该角色吗?', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteRole({ roleId: row.roleId || row.id }).then(res => {
|
||||||
|
if (res && res.code !== 20000) {
|
||||||
|
this.$message.error((res && res.message) || '角色删除失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.success((res && res.message) || '角色删除成功')
|
||||||
|
if (this.tableData.length === 1 && this.pageNo > 1) {
|
||||||
|
this.pageNo = this.pageNo - 1
|
||||||
|
}
|
||||||
|
this.fetchList()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
reated() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
358
src/components/System/UserManage.vue
Normal file
358
src/components/System/UserManage.vue
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="用户管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="openCreate">新建用户</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryForm.keyword" placeholder="用户名" clearable @keyup.enter.native="handleSearch"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable placeholder="全部状态">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="width: 100%; margin-top: 16px;">
|
||||||
|
<el-table-column prop="username" label="用户名" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="real_name" label="姓名" min-width="120">
|
||||||
|
<template slot-scope="scope">{{ scope.row.real_name || scope.row.realName }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="mobile" label="手机号" min-width="140"></el-table-column>
|
||||||
|
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column label="角色" min-width="180" show-overflow-tooltip>
|
||||||
|
<template slot-scope="scope">{{ getRoleNames(scope.row) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="240" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openAssignRoles(scope.row)">分配角色</el-button>
|
||||||
|
<el-button type="text" @click="openEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog :title="dialogMode === 'create' ? '新建用户' : '编辑用户'" :visible.sync="dialogVisible" width="620px" @close="resetDialogForm">
|
||||||
|
<el-form ref="userForm" :model="userForm" :rules="userRules" label-width="100px" size="small">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="用户名" prop="username">
|
||||||
|
<el-input v-model.trim="userForm.username" maxlength="64" placeholder="请输入用户名" :disabled="dialogMode === 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="密码" prop="password">
|
||||||
|
<el-input v-model.trim="userForm.password" maxlength="64" placeholder="请输入密码" :disabled="dialogMode === 'edit'"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="姓名" prop="realName">
|
||||||
|
<el-input v-model.trim="userForm.realName" maxlength="64" placeholder="请输入姓名"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="手机号" prop="mobile">
|
||||||
|
<el-input v-model.trim="userForm.mobile" maxlength="20" placeholder="请输入手机号"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model.trim="userForm.email" maxlength="128" placeholder="请输入邮箱"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="userForm.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-form-item label="头像" prop="avatar">
|
||||||
|
<el-input v-model.trim="userForm.avatar" maxlength="255" placeholder="请输入头像地址"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="submitting" @click="submitForm">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="分配角色" :visible.sync="assignDialogVisible" width="520px" @close="resetAssignForm">
|
||||||
|
<el-form label-width="90px" size="small">
|
||||||
|
<el-form-item label="用户">
|
||||||
|
<el-input :value="assignUserName" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角色选择">
|
||||||
|
<el-select v-model="selectedRoleIds" multiple filterable clearable collapse-tags placeholder="请选择角色" style="width: 100%;">
|
||||||
|
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="assignDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="assignSubmitting" @click="submitAssignRoles">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { assignUserRoles, createUser, deleteUser, getRoleList, getUserList, getUserRoleList, updateUser } from '@/api/rbacApi'
|
||||||
|
|
||||||
|
const getDefaultForm = () => ({
|
||||||
|
userId: undefined,
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
realName: '',
|
||||||
|
mobile: '',
|
||||||
|
email: '',
|
||||||
|
avatar: '',
|
||||||
|
status: 1,
|
||||||
|
createdBy: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'UserManage',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
assignSubmitting: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
assignDialogVisible: false,
|
||||||
|
dialogMode: 'create',
|
||||||
|
queryForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
userForm: getDefaultForm(),
|
||||||
|
userRules: {
|
||||||
|
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||||
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
|
realName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
},
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
tableData: [],
|
||||||
|
roleOptions: [],
|
||||||
|
assignUserId: undefined,
|
||||||
|
assignUserName: '',
|
||||||
|
selectedRoleIds: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getUserList({
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
keyword: this.queryForm.keyword,
|
||||||
|
status: this.queryForm.status
|
||||||
|
}).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.tableData = data.items || data.list || data.data || []
|
||||||
|
this.total = data.total || data.totalCount || this.tableData.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchRoleOptions() {
|
||||||
|
return getRoleList({ pageNo: 1, pageSize: 9999 }).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
const list = data.items || data.list || data.data || []
|
||||||
|
this.roleOptions = list.map(item => ({
|
||||||
|
id: item.id || item.roleId,
|
||||||
|
name: item.name
|
||||||
|
}))
|
||||||
|
}).catch(() => {
|
||||||
|
this.roleOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getRoleNames(row) {
|
||||||
|
const roleNames = row.role_names || row.roleNames || []
|
||||||
|
return Array.isArray(roleNames) ? roleNames.join(',') : ''
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
resetSearch() {
|
||||||
|
this.queryForm = {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNo = val
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
openCreate() {
|
||||||
|
this.dialogMode = 'create'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.userForm = getDefaultForm()
|
||||||
|
if (this.$refs.userForm) {
|
||||||
|
this.$refs.userForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit(row) {
|
||||||
|
this.dialogMode = 'edit'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.userForm = Object.assign(getDefaultForm(), row, {
|
||||||
|
userId: row.userId || row.id,
|
||||||
|
realName: row.realName || row.real_name || '',
|
||||||
|
password: ''
|
||||||
|
})
|
||||||
|
if (this.$refs.userForm) {
|
||||||
|
this.$refs.userForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openAssignRoles(row) {
|
||||||
|
this.assignUserId = row.userId || row.id
|
||||||
|
this.assignUserName = row.username
|
||||||
|
this.assignDialogVisible = true
|
||||||
|
Promise.all([this.fetchRoleOptions(), getUserRoleList(this.assignUserId)]).then(([, res]) => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.selectedRoleIds = data.roleIds || data.role_ids || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.selectedRoleIds = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetDialogForm() {
|
||||||
|
this.userForm = getDefaultForm()
|
||||||
|
this.submitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.userForm) {
|
||||||
|
this.$refs.userForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetAssignForm() {
|
||||||
|
this.assignSubmitting = false
|
||||||
|
this.assignUserId = undefined
|
||||||
|
this.assignUserName = ''
|
||||||
|
this.selectedRoleIds = []
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
this.$refs.userForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitting = true
|
||||||
|
const payload = this.dialogMode === 'create'
|
||||||
|
? this.userForm
|
||||||
|
: {
|
||||||
|
userId: this.userForm.userId,
|
||||||
|
realName: this.userForm.realName,
|
||||||
|
mobile: this.userForm.mobile,
|
||||||
|
email: this.userForm.email,
|
||||||
|
avatar: this.userForm.avatar,
|
||||||
|
status: this.userForm.status
|
||||||
|
}
|
||||||
|
const request = this.dialogMode === 'create'
|
||||||
|
? createUser(payload)
|
||||||
|
: updateUser(payload)
|
||||||
|
request.then(res => {
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success((res && res.message) || (this.dialogMode === 'create' ? '用户创建成功' : '用户更新成功'))
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error((res && res.message) || (this.dialogMode === 'create' ? '用户创建失败' : '用户更新失败'))
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitAssignRoles() {
|
||||||
|
this.assignSubmitting = true
|
||||||
|
assignUserRoles({
|
||||||
|
userId: this.assignUserId,
|
||||||
|
roleIds: this.selectedRoleIds
|
||||||
|
}).then(res => {
|
||||||
|
if (res && res.code !== 20000) {
|
||||||
|
this.$message.error((res && res.message) || '分配角色失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.success((res && res.message) || '分配角色成功')
|
||||||
|
this.assignDialogVisible = false
|
||||||
|
this.fetchList()
|
||||||
|
}).finally(() => {
|
||||||
|
this.assignSubmitting = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认删除该用户吗?', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteUser({ userId: row.userId || row.id }).then(res => {
|
||||||
|
if (res && res.code !== 20000) {
|
||||||
|
this.$message.error((res && res.message) || '用户删除失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.success((res && res.message) || '用户删除成功')
|
||||||
|
if (this.tableData.length === 1 && this.pageNo > 1) {
|
||||||
|
this.pageNo = this.pageNo - 1
|
||||||
|
}
|
||||||
|
this.fetchList()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
116
src/components/TestPlatform/Case/CaseEditor.vue
Normal file
116
src/components/TestPlatform/Case/CaseEditor.vue
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section :title="form.id ? '编辑用例' : '新建用例'">
|
||||||
|
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标题" prop="title">
|
||||||
|
<el-input v-model="form.title"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="前置条件">
|
||||||
|
<el-input v-model="form.preconditions" type="textarea" :rows="3"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="步骤(JSON)" prop="steps">
|
||||||
|
<el-input v-model="stepsText" type="textarea" :rows="10"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="优先级">
|
||||||
|
<el-select v-model="form.priority">
|
||||||
|
<el-option label="P0" :value="0"></el-option>
|
||||||
|
<el-option label="P1" :value="1"></el-option>
|
||||||
|
<el-option label="P2" :value="2"></el-option>
|
||||||
|
<el-option label="P3" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="类型">
|
||||||
|
<el-select v-model="form.case_type">
|
||||||
|
<el-option label="功能" :value="1"></el-option>
|
||||||
|
<el-option label="性能" :value="2"></el-option>
|
||||||
|
<el-option label="安全" :value="3"></el-option>
|
||||||
|
<el-option label="接口" :value="4"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="标签">
|
||||||
|
<el-input v-model="tagsText" placeholder="多个标签用逗号分隔"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
|
||||||
|
<el-button @click="$router.push('/test-platform/cases')">返回</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createCase, getCaseDetail, updateCase } from '@/api/caseApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CaseEditor',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
saving: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
form: {
|
||||||
|
id: '',
|
||||||
|
title: '',
|
||||||
|
preconditions: '',
|
||||||
|
steps: [],
|
||||||
|
priority: 2,
|
||||||
|
case_type: 1,
|
||||||
|
tags: []
|
||||||
|
},
|
||||||
|
stepsText: '[]',
|
||||||
|
tagsText: '',
|
||||||
|
rules: {
|
||||||
|
title: [{ required: true, message: '请输入标题', trigger: 'blur' }],
|
||||||
|
steps: [{ required: true, message: '请输入步骤', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchDetail() {
|
||||||
|
const caseId = this.$route.query.caseId
|
||||||
|
if (!caseId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getCaseDetail(this.projectId, caseId).then(res => {
|
||||||
|
const data = (res && res.data) || res || {}
|
||||||
|
this.form = Object.assign({}, this.form, data)
|
||||||
|
this.stepsText = JSON.stringify(data.steps || [], null, 2)
|
||||||
|
this.tagsText = (data.tags || []).join(',')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
try {
|
||||||
|
this.form.steps = JSON.parse(this.stepsText || '[]')
|
||||||
|
} catch (e) {
|
||||||
|
this.$message({ type: 'error', message: '步骤 JSON 格式错误' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.form.tags = this.tagsText ? this.tagsText.split(',').map(item => item.trim()).filter(Boolean) : []
|
||||||
|
this.saving = true
|
||||||
|
const request = this.form.id || this.$route.query.caseId
|
||||||
|
? updateCase(this.projectId, this.form.id || this.$route.query.caseId, this.form)
|
||||||
|
: createCase(this.projectId, this.form)
|
||||||
|
request.then(() => {
|
||||||
|
this.$message({ type: 'success', message: '保存成功' })
|
||||||
|
this.$router.push({ path: '/test-platform/cases', query: { projectId: this.projectId } })
|
||||||
|
}).finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
110
src/components/TestPlatform/Case/CaseList.vue
Normal file
110
src/components/TestPlatform/Case/CaseList.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="用例管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="goEditor()">新建用例</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryForm.keyword" clearable @keyup.enter.native="fetchList"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="优先级">
|
||||||
|
<el-select v-model="queryForm.priority" clearable>
|
||||||
|
<el-option label="P0" :value="0"></el-option>
|
||||||
|
<el-option label="P1" :value="1"></el-option>
|
||||||
|
<el-option label="P2" :value="2"></el-option>
|
||||||
|
<el-option label="P3" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchList">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="margin-top: 16px;">
|
||||||
|
<el-table-column prop="case_key" label="用例编号" min-width="120"></el-table-column>
|
||||||
|
<el-table-column prop="title" label="标题" min-width="220"></el-table-column>
|
||||||
|
<el-table-column prop="priority" label="优先级" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="case_type" label="类型" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="240">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="goEditor(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" @click="goReview(scope.row)">评审</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="remove(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { deleteCase, getCaseList } from '@/api/caseApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CaseList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
queryForm: {
|
||||||
|
keyword: '',
|
||||||
|
priority: ''
|
||||||
|
},
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getCaseList(this.projectId, this.queryForm).then(res => {
|
||||||
|
const data = (res && res.data) || res || {}
|
||||||
|
this.tableData = data.items || data.list || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goEditor(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/cases/editor', query: { projectId: this.projectId, caseId: row && row.id } })
|
||||||
|
},
|
||||||
|
goReview(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/cases/review', query: { projectId: this.projectId, caseId: row.id } })
|
||||||
|
},
|
||||||
|
remove(row) {
|
||||||
|
this.$confirm('确认删除该用例吗?', '提示', { type: 'warning' }).then(() => {
|
||||||
|
deleteCase(this.projectId, row.id).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '删除成功' })
|
||||||
|
this.fetchList()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
64
src/components/TestPlatform/Case/CaseReview.vue
Normal file
64
src/components/TestPlatform/Case/CaseReview.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="用例评审">
|
||||||
|
<el-form :model="form" label-width="120px" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用例ID">
|
||||||
|
<el-input v-model="caseId" disabled style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="评审人ID">
|
||||||
|
<el-input v-model="reviewersText" placeholder="多个 ID 用逗号分隔"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="form.comments" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="submitting" @click="submitReview">提交评审</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { submitCaseReview } from '@/api/caseApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CaseReview',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
caseId: this.$route.query.caseId || '',
|
||||||
|
reviewersText: '',
|
||||||
|
submitting: false,
|
||||||
|
form: {
|
||||||
|
comments: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitReview() {
|
||||||
|
const reviewer_ids = this.reviewersText.split(',').map(item => item.trim()).filter(Boolean)
|
||||||
|
this.submitting = true
|
||||||
|
submitCaseReview(this.projectId, this.caseId, {
|
||||||
|
reviewer_ids,
|
||||||
|
comments: this.form.comments
|
||||||
|
}).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '评审提交成功' })
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
82
src/components/TestPlatform/DataFactory/BuilderEditor.vue
Normal file
82
src/components/TestPlatform/DataFactory/BuilderEditor.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="造数器编辑">
|
||||||
|
<el-form :model="form" label-width="120px" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称">
|
||||||
|
<el-input v-model="form.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="类型">
|
||||||
|
<el-select v-model="form.builder_type">
|
||||||
|
<el-option label="流程编排" :value="1"></el-option>
|
||||||
|
<el-option label="SQL" :value="2"></el-option>
|
||||||
|
<el-option label="脚本" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述">
|
||||||
|
<el-input v-model="form.description" type="textarea" :rows="3"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="定义(JSON)">
|
||||||
|
<el-input v-model="definitionText" type="textarea" :rows="14"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="输入Schema(JSON)">
|
||||||
|
<el-input v-model="inputSchemaText" type="textarea" :rows="8"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createBuilder } from '@/api/dataFactoryApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BuilderEditor',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
saving: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
definitionText: '{\n "steps": [],\n "output": {}\n}',
|
||||||
|
inputSchemaText: '{\n "type": "object",\n "properties": {}\n}',
|
||||||
|
form: {
|
||||||
|
name: '',
|
||||||
|
builder_type: 1,
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm() {
|
||||||
|
let definition = {}
|
||||||
|
let input_schema = {}
|
||||||
|
try {
|
||||||
|
definition = JSON.parse(this.definitionText || '{}')
|
||||||
|
input_schema = JSON.parse(this.inputSchemaText || '{}')
|
||||||
|
} catch (e) {
|
||||||
|
this.$message({ type: 'error', message: 'JSON 格式错误' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.saving = true
|
||||||
|
createBuilder(this.projectId, Object.assign({}, this.form, { definition, input_schema })).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '造数器保存成功' })
|
||||||
|
this.$router.push({ path: '/test-platform/data-factory/builders', query: { projectId: this.projectId } })
|
||||||
|
}).finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
100
src/components/TestPlatform/DataFactory/BuilderList.vue
Normal file
100
src/components/TestPlatform/DataFactory/BuilderList.vue
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="造数工厂">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="goEditor()">新建造数器</el-button>
|
||||||
|
<el-button size="small" @click="goTasks">任务历史</el-button>
|
||||||
|
<el-button size="small" @click="goMock">Mock服务</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchList">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="margin-top: 16px;">
|
||||||
|
<el-table-column prop="name" label="名称" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="builder_type" label="类型" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="description" label="描述" min-width="220"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="240">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="goEditor(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" @click="execute(scope.row)">执行</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { executeBuilder, getBuilderList } from '@/api/dataFactoryApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BuilderList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getBuilderList(this.projectId, {
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
}).then(res => {
|
||||||
|
const data = (res && res.data) || res || []
|
||||||
|
this.tableData = data.items || data.list || data.data || data || []
|
||||||
|
this.total = data.total || data.totalCount || this.tableData.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goEditor(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/data-factory/editor', query: { projectId: this.projectId, builderId: row && row.id } })
|
||||||
|
},
|
||||||
|
goTasks() {
|
||||||
|
this.$router.push({ path: '/test-platform/data-factory/tasks', query: { projectId: this.projectId } })
|
||||||
|
},
|
||||||
|
goMock() {
|
||||||
|
this.$router.push({ path: '/test-platform/data-factory/mock', query: { projectId: this.projectId } })
|
||||||
|
},
|
||||||
|
execute(row) {
|
||||||
|
executeBuilder(this.projectId, row.id, { params: { count: 1 }, async: true }).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '造数任务已提交' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
style>
|
||||||
72
src/components/TestPlatform/DataFactory/MockService.vue
Normal file
72
src/components/TestPlatform/DataFactory/MockService.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="Mock 服务">
|
||||||
|
<el-form :model="form" label-width="120px" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Path">
|
||||||
|
<el-input v-model="form.path"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Method">
|
||||||
|
<el-select v-model="form.method">
|
||||||
|
<el-option label="GET" value="GET"></el-option>
|
||||||
|
<el-option label="POST" value="POST"></el-option>
|
||||||
|
<el-option label="PUT" value="PUT"></el-option>
|
||||||
|
<el-option label="DELETE" value="DELETE"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态码">
|
||||||
|
<el-input v-model="form.status_code"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="延迟(ms)">
|
||||||
|
<el-input v-model="form.delay_ms"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="响应体(JSON)">
|
||||||
|
<el-input v-model="responseBodyText" type="textarea" :rows="10"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="saving" @click="submitForm">创建 Mock</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-alert v-if="mockUrl" :title="'Mock 地址:' + mockUrl" type="success" :closable="false"></el-alert>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MockService',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
saving: false,
|
||||||
|
mockUrl: '',
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
responseBodyText: '{\n "code": 0,\n "message": "success"\n}',
|
||||||
|
form: {
|
||||||
|
path: '',
|
||||||
|
method: 'POST',
|
||||||
|
status_code: 200,
|
||||||
|
delay_ms: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm() {
|
||||||
|
this.$message({
|
||||||
|
type: 'warning',
|
||||||
|
message: '当前后端未提供 Mock 服务接口,页面暂为占位状态'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/components/TestPlatform/DataFactory/TaskHistory.vue
Normal file
55
src/components/TestPlatform/DataFactory/TaskHistory.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="造数任务历史">
|
||||||
|
<el-form :inline="true" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="任务ID">
|
||||||
|
<el-input v-model="taskId" style="width: 160px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchStatus">查询状态</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<json-viewer :value="taskResult"></json-viewer>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import JsonViewer from '@/components/TestPlatform/common/JsonViewer'
|
||||||
|
import { getDataTaskStatus } from '@/api/dataFactoryApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaskHistory',
|
||||||
|
components: { PageSection, JsonViewer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
taskId: this.$route.query.taskId || '',
|
||||||
|
taskResult: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchStatus() {
|
||||||
|
if (!this.taskId) {
|
||||||
|
this.$message({ type: 'warning', message: '请输入任务ID' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getDataTaskStatus(this.projectId, this.taskId).then(res => {
|
||||||
|
this.taskResult = (res && res.data) || res || {}
|
||||||
|
}).catch(() => {
|
||||||
|
this.taskResult = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
69
src/components/TestPlatform/Plan/PlanBuilder.vue
Normal file
69
src/components/TestPlatform/Plan/PlanBuilder.vue
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="计划构建">
|
||||||
|
<el-form :model="form" label-width="120px" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 200px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划名称">
|
||||||
|
<el-input v-model="form.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="版本">
|
||||||
|
<el-input v-model="form.version"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="负责人ID">
|
||||||
|
<el-input v-model="form.owner_id"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="环境ID">
|
||||||
|
<el-input v-model="form.environment_id"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述">
|
||||||
|
<el-input v-model="form.description" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="saving" @click="submitForm">保存计划</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createPlan } from '@/api/planApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlanBuilder',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
saving: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
form: {
|
||||||
|
name: '',
|
||||||
|
version: '',
|
||||||
|
owner_id: '',
|
||||||
|
environment_id: '',
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submitForm() {
|
||||||
|
this.saving = true
|
||||||
|
createPlan(this.projectId, this.form).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '计划创建成功' })
|
||||||
|
this.$router.push({ path: '/test-platform/plans', query: { projectId: this.projectId } })
|
||||||
|
}).finally(() => {
|
||||||
|
this.saving = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
114
src/components/TestPlatform/Plan/PlanExecute.vue
Normal file
114
src/components/TestPlatform/Plan/PlanExecute.vue
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="计划执行">
|
||||||
|
<el-form :inline="true" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划ID">
|
||||||
|
<el-input v-model="planId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchDetail">刷新</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<key-value-descriptions :items="summaryItems"></key-value-descriptions>
|
||||||
|
<el-divider></el-divider>
|
||||||
|
<el-form :model="executeForm" label-width="120px" size="small">
|
||||||
|
<el-form-item label="计划用例ID">
|
||||||
|
<el-input v-model="executeForm.planCaseId"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行状态">
|
||||||
|
<el-select v-model="executeForm.status">
|
||||||
|
<el-option label="通过" :value="1"></el-option>
|
||||||
|
<el-option label="失败" :value="2"></el-option>
|
||||||
|
<el-option label="阻塞" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="实际结果">
|
||||||
|
<el-input v-model="executeForm.actual_result" type="textarea" :rows="4"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="缺陷链接">
|
||||||
|
<el-input v-model="defectLinksText" placeholder="多个缺陷号用逗号分隔"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" :loading="submitting" @click="submitExecute">提交执行</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import KeyValueDescriptions from '@/components/TestPlatform/common/KeyValueDescriptions'
|
||||||
|
import { executePlanCase, getPlanDetail } from '@/api/planApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlanExecute',
|
||||||
|
components: { PageSection, KeyValueDescriptions },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
planId: this.$route.query.planId || '',
|
||||||
|
detail: {},
|
||||||
|
submitting: false,
|
||||||
|
defectLinksText: '',
|
||||||
|
executeForm: {
|
||||||
|
planCaseId: '',
|
||||||
|
status: 1,
|
||||||
|
actual_result: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
summaryItems() {
|
||||||
|
return [
|
||||||
|
{ label: '计划ID', value: this.detail.id },
|
||||||
|
{ label: '计划名称', value: this.detail.name },
|
||||||
|
{ label: '总用例数', value: this.detail.total_cases },
|
||||||
|
{ label: '已完成', value: this.detail.completed },
|
||||||
|
{ label: '通过率', value: this.detail.pass_rate }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchDetail() {
|
||||||
|
if (!this.planId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getPlanDetail(this.projectId, this.planId).then(res => {
|
||||||
|
this.detail = (res && res.data) || res || {}
|
||||||
|
}).catch(() => {
|
||||||
|
this.detail = {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitExecute() {
|
||||||
|
if (!this.executeForm.planCaseId) {
|
||||||
|
this.$message({ type: 'warning', message: '请输入计划用例ID' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitting = true
|
||||||
|
executePlanCase(this.projectId, this.planId, this.executeForm.planCaseId, {
|
||||||
|
status: this.executeForm.status,
|
||||||
|
actual_result: this.executeForm.actual_result,
|
||||||
|
defect_links: this.defectLinksText ? this.defectLinksText.split(',').map(item => item.trim()).filter(Boolean) : [],
|
||||||
|
attachments: []
|
||||||
|
}).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '执行结果已提交' })
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
106
src/components/TestPlatform/Plan/PlanList.vue
Normal file
106
src/components/TestPlatform/Plan/PlanList.vue
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="测试计划">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="goBuilder()">新建计划</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryForm.keyword" clearable></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable>
|
||||||
|
<el-option label="草稿" :value="0"></el-option>
|
||||||
|
<el-option label="进行中" :value="1"></el-option>
|
||||||
|
<el-option label="已完成" :value="2"></el-option>
|
||||||
|
<el-option label="已归档" :value="3"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchList">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="margin-top: 16px;">
|
||||||
|
<el-table-column prop="name" label="计划名称" min-width="180"></el-table-column>
|
||||||
|
<el-table-column prop="version" label="版本" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="owner_id" label="负责人" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="260">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="goExecute(scope.row)">执行</el-button>
|
||||||
|
<el-button type="text" @click="goProgress(scope.row)">进度</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { getPlanList } from '@/api/planApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlanList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
queryForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getPlanList(this.projectId, this.queryForm).then(res => {
|
||||||
|
const data = (res && res.data) || res || {}
|
||||||
|
this.tableData = data.items || data.list || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goBuilder() {
|
||||||
|
this.$router.push({ path: '/test-platform/plans/builder', query: { projectId: this.projectId } })
|
||||||
|
},
|
||||||
|
goExecute(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/plans/execute', query: { projectId: this.projectId, planId: row.id } })
|
||||||
|
},
|
||||||
|
goProgress(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/plans/progress', query: { projectId: this.projectId, planId: row.id } })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
73
src/components/TestPlatform/Plan/PlanProgress.vue
Normal file
73
src/components/TestPlatform/Plan/PlanProgress.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="计划进度">
|
||||||
|
<el-form :inline="true" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划ID">
|
||||||
|
<el-input v-model="planId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchProgress">刷新</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="8">
|
||||||
|
<page-section title="轮次汇总">
|
||||||
|
<json-viewer :value="progress.round_summary || []"></json-viewer>
|
||||||
|
</page-section>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<page-section title="人员负载">
|
||||||
|
<json-viewer :value="progress.assignee_load || []"></json-viewer>
|
||||||
|
</page-section>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<page-section title="每日趋势">
|
||||||
|
<json-viewer :value="progress.daily_trend || []"></json-viewer>
|
||||||
|
</page-section>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import JsonViewer from '@/components/TestPlatform/common/JsonViewer'
|
||||||
|
import { getPlanProgress } from '@/api/planApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PlanProgress',
|
||||||
|
components: { PageSection, JsonViewer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
planId: this.$route.query.planId || '',
|
||||||
|
progress: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchProgress() {
|
||||||
|
if (!this.planId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getPlanProgress(this.projectId, this.planId).then(res => {
|
||||||
|
this.progress = (res && res.data) || res || {}
|
||||||
|
}).catch(() => {
|
||||||
|
this.progress = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchProgress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
223
src/components/TestPlatform/Product/ProductList.vue
Normal file
223
src/components/TestPlatform/Product/ProductList.vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="产品管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="openCreate">新建产品</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="产品名称">
|
||||||
|
<el-input v-model="queryForm.name" placeholder="请输入产品名称" clearable @keyup.enter.native="handleSearch"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable placeholder="全部状态">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button @click="resetSearch">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="width: 100%; margin-top: 16px;">
|
||||||
|
<el-table-column prop="code" label="产品编码" min-width="120"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="产品名称" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="description" label="描述" min-width="220" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog :title="dialogMode === 'create' ? '新建产品' : '编辑产品'" :visible.sync="dialogVisible" width="520px" @close="resetDialogForm">
|
||||||
|
<el-form ref="productForm" :model="productForm" :rules="productRules" label-width="94px" size="small">
|
||||||
|
<el-form-item label="产品名称" prop="name">
|
||||||
|
<el-input v-model.trim="productForm.name" maxlength="64" placeholder="请输入产品名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="productForm.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述" prop="description">
|
||||||
|
<el-input v-model.trim="productForm.description" type="textarea" :rows="4" maxlength="255" show-word-limit placeholder="请输入描述"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="submitting" @click="submitForm">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createProduct, deleteProduct, getProductList, updateProduct } from '@/api/productApi'
|
||||||
|
|
||||||
|
const getDefaultForm = () => ({
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
status: 1,
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProductList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
submitting: false,
|
||||||
|
dialogVisible: false,
|
||||||
|
dialogMode: 'create',
|
||||||
|
queryForm: {
|
||||||
|
name: '',
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
productForm: getDefaultForm(),
|
||||||
|
productRules: {
|
||||||
|
name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
},
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getProductList({
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
name: this.queryForm.name,
|
||||||
|
status: this.queryForm.status
|
||||||
|
}).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.tableData = data.items || data.list || data.data || []
|
||||||
|
this.total = data.total || data.totalCount || this.tableData.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSearch() {
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
resetSearch() {
|
||||||
|
this.queryForm = {
|
||||||
|
name: '',
|
||||||
|
status: ''
|
||||||
|
}
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNo = val
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
openCreate() {
|
||||||
|
this.dialogMode = 'create'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.productForm = getDefaultForm()
|
||||||
|
if (this.$refs.productForm) {
|
||||||
|
this.$refs.productForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit(row) {
|
||||||
|
this.dialogMode = 'edit'
|
||||||
|
this.dialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.productForm = Object.assign(getDefaultForm(), row, { id: row.id })
|
||||||
|
if (this.$refs.productForm) {
|
||||||
|
this.$refs.productForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetDialogForm() {
|
||||||
|
this.productForm = getDefaultForm()
|
||||||
|
this.submitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.productForm) {
|
||||||
|
this.$refs.productForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
this.$refs.productForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.submitting = true
|
||||||
|
const request = this.dialogMode === 'create'
|
||||||
|
? createProduct(this.productForm)
|
||||||
|
: updateProduct(this.productForm)
|
||||||
|
request.then(res => {
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success(res.message || (this.dialogMode === 'create' ? '产品创建成功' : '产品更新成功'))
|
||||||
|
this.dialogVisible = false
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error((res && res.message) || (this.dialogMode === 'create' ? '产品创建失败' : '产品更新失败'))
|
||||||
|
}).finally(() => {
|
||||||
|
this.submitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认删除该产品吗?', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteProduct({ id: row.id }).then(() => {
|
||||||
|
this.$message.success('产品删除成功')
|
||||||
|
if (this.tableData.length === 1 && this.pageNo > 1) {
|
||||||
|
this.pageNo = this.pageNo - 1
|
||||||
|
}
|
||||||
|
this.fetchList()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/components/TestPlatform/Project/ProjectDetail.vue
Normal file
55
src/components/TestPlatform/Project/ProjectDetail.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="项目详情">
|
||||||
|
<key-value-descriptions :items="descriptionItems"></key-value-descriptions>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import KeyValueDescriptions from '@/components/TestPlatform/common/KeyValueDescriptions'
|
||||||
|
import { getProjectDetail } from '@/api/projectApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectDetail',
|
||||||
|
components: { PageSection, KeyValueDescriptions },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
detail: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
descriptionItems() {
|
||||||
|
return [
|
||||||
|
{ label: '项目ID', value: this.detail.id },
|
||||||
|
{ label: '项目标识', value: this.detail.key },
|
||||||
|
{ label: '项目名称', value: this.detail.name },
|
||||||
|
{ label: '部门', value: this.detail.department },
|
||||||
|
{ label: '状态', value: this.detail.status === 0 ? '禁用' : '启用' },
|
||||||
|
{ label: '描述', value: this.detail.description },
|
||||||
|
{ label: '扩展配置', value: this.detail.config }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchDetail() {
|
||||||
|
const projectId = this.$route.query.projectId || 1
|
||||||
|
getProjectDetail(projectId).then(res => {
|
||||||
|
this.detail = (res && res.data) || res || {}
|
||||||
|
}).catch(() => {
|
||||||
|
this.detail = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
247
src/components/TestPlatform/Project/ProjectList.vue
Normal file
247
src/components/TestPlatform/Project/ProjectList.vue
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="项目管理">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" @click="openCreate">新建项目</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small" @submit.native.prevent>
|
||||||
|
<el-form-item label="关键词">
|
||||||
|
<el-input v-model="queryForm.keyword" placeholder="项目名称" clearable @keyup.enter.native="fetchList"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryForm.status" clearable placeholder="全部状态">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchList">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="width: 100%; margin-top: 16px;">
|
||||||
|
<el-table-column prop="product_name" label="产品名称" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="key" label="项目编码" min-width="120"></el-table-column>
|
||||||
|
<el-table-column prop="name" label="项目名称" min-width="160"></el-table-column>
|
||||||
|
<el-table-column prop="description" label="描述" min-width="220" show-overflow-tooltip></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="100">
|
||||||
|
<template slot-scope="scope">{{ scope.row.status === 0 ? '禁用' : '启用' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="openEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" style="color: #F56C6C;" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
|
<el-button type="text" @click="goSettings(scope.row)">设置</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog :title="dialogMode === 'create' ? '新建项目' : '编辑项目'" :visible.sync="createDialogVisible" width="520px" @close="resetCreateForm">
|
||||||
|
<el-form ref="createForm" :model="createForm" :rules="createRules" label-width="94px" size="small">
|
||||||
|
<el-form-item label="项目名称" prop="name">
|
||||||
|
<el-input v-model.trim="createForm.name" maxlength="64" placeholder="请输入项目名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="所属产品" prop="productId">
|
||||||
|
<el-select v-model="createForm.productId" placeholder="请选择所属产品" filterable clearable style="width: 100%;">
|
||||||
|
<el-option
|
||||||
|
v-for="item in productOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="createForm.status" placeholder="请选择状态" style="width: 100%;">
|
||||||
|
<el-option label="启用" :value="1"></el-option>
|
||||||
|
<el-option label="禁用" :value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述" prop="description">
|
||||||
|
<el-input v-model.trim="createForm.description" type="textarea" :rows="4" maxlength="255" show-word-limit placeholder="请输入描述"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="createDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="createSubmitting" @click="submitCreate">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { createProject, deleteProject, getProjectList, updateProject } from '@/api/projectApi'
|
||||||
|
import { getProductList } from '@/api/productApi'
|
||||||
|
|
||||||
|
const getDefaultCreateForm = () => ({
|
||||||
|
id: undefined,
|
||||||
|
name: '',
|
||||||
|
productId: '',
|
||||||
|
status: 1,
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
createSubmitting: false,
|
||||||
|
createDialogVisible: false,
|
||||||
|
dialogMode: 'create',
|
||||||
|
queryForm: {
|
||||||
|
keyword: '',
|
||||||
|
status: ''
|
||||||
|
},
|
||||||
|
createForm: getDefaultCreateForm(),
|
||||||
|
createRules: {
|
||||||
|
name: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
|
||||||
|
productId: [{ required: true, message: '请选择所属产品', trigger: 'change' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
},
|
||||||
|
productOptions: [],
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getProjectList({
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
keyword: this.queryForm.keyword,
|
||||||
|
status: this.queryForm.status
|
||||||
|
}).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.tableData = data.items || data.list || data.data || []
|
||||||
|
this.total = data.total || data.totalCount || this.tableData.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSizeChange(val) {
|
||||||
|
this.pageSize = val
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
handleCurrentChange(val) {
|
||||||
|
this.pageNo = val
|
||||||
|
this.fetchList()
|
||||||
|
},
|
||||||
|
goSettings(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/projects/settings', query: { projectId: row.id } })
|
||||||
|
},
|
||||||
|
fetchProductOptions() {
|
||||||
|
return getProductList({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 1000,
|
||||||
|
status: 1
|
||||||
|
}).then(res => {
|
||||||
|
const data = res && res.data ? res.data : res || {}
|
||||||
|
this.productOptions = data.items || data.list || data.data || []
|
||||||
|
}).catch(() => {
|
||||||
|
this.productOptions = []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openCreate() {
|
||||||
|
this.dialogMode = 'create'
|
||||||
|
this.createForm = getDefaultCreateForm()
|
||||||
|
this.fetchProductOptions()
|
||||||
|
this.createDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.createForm) {
|
||||||
|
this.$refs.createForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEdit(row) {
|
||||||
|
this.dialogMode = 'edit'
|
||||||
|
this.fetchProductOptions()
|
||||||
|
this.createDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.createForm = Object.assign(getDefaultCreateForm(), row, {
|
||||||
|
id: row.id,
|
||||||
|
productId: row.productId || row.product_id || row.productId
|
||||||
|
})
|
||||||
|
if (this.$refs.createForm) {
|
||||||
|
this.$refs.createForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetCreateForm() {
|
||||||
|
this.createForm = getDefaultCreateForm()
|
||||||
|
this.createSubmitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.createForm) {
|
||||||
|
this.$refs.createForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitCreate() {
|
||||||
|
this.$refs.createForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.createSubmitting = true
|
||||||
|
const request = this.dialogMode === 'create'
|
||||||
|
? createProject(this.createForm)
|
||||||
|
: updateProject(this.createForm)
|
||||||
|
request.then(res => {
|
||||||
|
const message = (res && res.message) || ''
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success(message || (this.dialogMode === 'create' ? '项目创建成功' : '项目更新成功'))
|
||||||
|
this.createDialogVisible = false
|
||||||
|
this.pageNo = 1
|
||||||
|
this.fetchList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error(message || (this.dialogMode === 'create' ? '项目创建失败' : '项目更新失败'))
|
||||||
|
}).finally(() => {
|
||||||
|
this.createSubmitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleDelete(row) {
|
||||||
|
this.$confirm('确认删除该项目吗?', '提示', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
deleteProject({ id: row.id }).then(() => {
|
||||||
|
this.$message.success('项目删除成功')
|
||||||
|
if (this.tableData.length === 1 && this.pageNo > 1) {
|
||||||
|
this.pageNo = this.pageNo - 1
|
||||||
|
}
|
||||||
|
this.fetchList()
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
281
src/components/TestPlatform/Project/ProjectSettings.vue
Normal file
281
src/components/TestPlatform/Project/ProjectSettings.vue
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="项目设置">
|
||||||
|
<el-tabs value="members">
|
||||||
|
<el-tab-pane label="项目成员" name="members">
|
||||||
|
<div class="toolbar-wrap">
|
||||||
|
<el-button type="primary" size="small" @click="openMemberDialog">新增成员</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="members" border>
|
||||||
|
<el-table-column prop="user_id" label="用户ID"></el-table-column>
|
||||||
|
<el-table-column prop="role" label="角色"></el-table-column>
|
||||||
|
<el-table-column prop="joined_at" label="加入时间"></el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="memberPageNo"
|
||||||
|
:page-size="memberPageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="memberTotal"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleMemberSizeChange"
|
||||||
|
@current-change="handleMemberCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="环境配置" name="environments">
|
||||||
|
<div class="toolbar-wrap">
|
||||||
|
<el-button type="primary" size="small" @click="openEnvironmentDialog">新增环境</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="environments" border>
|
||||||
|
<el-table-column prop="name" label="环境"></el-table-column>
|
||||||
|
<el-table-column prop="variables" label="变量">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<json-viewer :value="scope.row.variables"></json-viewer>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="environmentPageNo"
|
||||||
|
:page-size="environmentPageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="environmentTotal"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleEnvironmentSizeChange"
|
||||||
|
@current-change="handleEnvironmentCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</page-section>
|
||||||
|
|
||||||
|
<el-dialog title="新增成员" :visible.sync="memberDialogVisible" width="520px" @close="resetMemberForm">
|
||||||
|
<el-form ref="memberForm" :model="memberForm" :rules="memberRules" label-width="94px" size="small">
|
||||||
|
<el-form-item label="用户ID" prop="user_id">
|
||||||
|
<el-input v-model.trim="memberForm.user_id" maxlength="64" placeholder="请输入用户ID"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角色" prop="role">
|
||||||
|
<el-input v-model.trim="memberForm.role" maxlength="64" placeholder="请输入角色"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="memberDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="memberSubmitting" @click="submitMember">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog title="新增环境" :visible.sync="environmentDialogVisible" width="520px" @close="resetEnvironmentForm">
|
||||||
|
<el-form ref="environmentForm" :model="environmentForm" :rules="environmentRules" label-width="94px" size="small">
|
||||||
|
<el-form-item label="环境名称" prop="name">
|
||||||
|
<el-input v-model.trim="environmentForm.name" maxlength="64" placeholder="请输入环境名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="变量JSON" prop="variablesText">
|
||||||
|
<el-input v-model.trim="environmentForm.variablesText" type="textarea" :rows="6" placeholder='请输入 JSON,例如 {"baseUrl":"https://test.com"}'></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button size="small" @click="environmentDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" size="small" :loading="environmentSubmitting" @click="submitEnvironment">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import JsonViewer from '@/components/TestPlatform/common/JsonViewer'
|
||||||
|
import { createEnvironment, createProjectMember, getProjectEnvironments, getProjectMembers } from '@/api/projectApi'
|
||||||
|
|
||||||
|
const getDefaultMemberForm = () => ({
|
||||||
|
user_id: '',
|
||||||
|
role: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const getDefaultEnvironmentForm = () => ({
|
||||||
|
name: '',
|
||||||
|
variablesText: '{}'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProjectSettings',
|
||||||
|
components: { PageSection, JsonViewer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
memberPageNo: 1,
|
||||||
|
memberPageSize: 10,
|
||||||
|
memberTotal: 0,
|
||||||
|
environmentPageNo: 1,
|
||||||
|
environmentPageSize: 10,
|
||||||
|
environmentTotal: 0,
|
||||||
|
members: [],
|
||||||
|
environments: [],
|
||||||
|
memberDialogVisible: false,
|
||||||
|
environmentDialogVisible: false,
|
||||||
|
memberSubmitting: false,
|
||||||
|
environmentSubmitting: false,
|
||||||
|
memberForm: getDefaultMemberForm(),
|
||||||
|
environmentForm: getDefaultEnvironmentForm(),
|
||||||
|
memberRules: {
|
||||||
|
user_id: [{ required: true, message: '请输入用户ID', trigger: 'blur' }],
|
||||||
|
role: [{ required: true, message: '请输入角色', trigger: 'blur' }]
|
||||||
|
},
|
||||||
|
environmentRules: {
|
||||||
|
name: [{ required: true, message: '请输入环境名称', trigger: 'blur' }],
|
||||||
|
variablesText: [{ required: true, message: '请输入变量JSON', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getProjectId() {
|
||||||
|
return this.$route.query.projectId || 1
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
const projectId = this.getProjectId()
|
||||||
|
getProjectMembers(projectId, {
|
||||||
|
pageNo: this.memberPageNo,
|
||||||
|
pageSize: this.memberPageSize
|
||||||
|
}).then(res => {
|
||||||
|
const data = (res && res.data) || res || []
|
||||||
|
this.members = data.items || data.list || data.data || data || []
|
||||||
|
this.memberTotal = data.total || data.totalCount || this.members.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.members = []
|
||||||
|
this.memberTotal = 0
|
||||||
|
})
|
||||||
|
getProjectEnvironments(projectId, {
|
||||||
|
pageNo: this.environmentPageNo,
|
||||||
|
pageSize: this.environmentPageSize
|
||||||
|
}).then(res => {
|
||||||
|
const data = (res && res.data) || res || []
|
||||||
|
this.environments = data.items || data.list || data.data || data || []
|
||||||
|
this.environmentTotal = data.total || data.totalCount || this.environments.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.environments = []
|
||||||
|
this.environmentTotal = 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openMemberDialog() {
|
||||||
|
this.memberDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.memberForm = getDefaultMemberForm()
|
||||||
|
if (this.$refs.memberForm) {
|
||||||
|
this.$refs.memberForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetMemberForm() {
|
||||||
|
this.memberForm = getDefaultMemberForm()
|
||||||
|
this.memberSubmitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.memberForm) {
|
||||||
|
this.$refs.memberForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitMember() {
|
||||||
|
this.$refs.memberForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.memberSubmitting = true
|
||||||
|
createProjectMember(Object.assign({ project_id: this.getProjectId() }, this.memberForm)).then(res => {
|
||||||
|
const message = (res && res.message) || ''
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success(message || '成员新增成功')
|
||||||
|
this.memberDialogVisible = false
|
||||||
|
this.memberPageNo = 1
|
||||||
|
this.fetchData()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error(message || '成员新增失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.memberSubmitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openEnvironmentDialog() {
|
||||||
|
this.environmentDialogVisible = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.environmentForm = getDefaultEnvironmentForm()
|
||||||
|
if (this.$refs.environmentForm) {
|
||||||
|
this.$refs.environmentForm.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
resetEnvironmentForm() {
|
||||||
|
this.environmentForm = getDefaultEnvironmentForm()
|
||||||
|
this.environmentSubmitting = false
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.environmentForm) {
|
||||||
|
this.$refs.environmentForm.resetFields()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitEnvironment() {
|
||||||
|
this.$refs.environmentForm.validate(valid => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let variables = {}
|
||||||
|
try {
|
||||||
|
variables = JSON.parse(this.environmentForm.variablesText || '{}')
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error('变量JSON格式不正确')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.environmentSubmitting = true
|
||||||
|
createEnvironment({
|
||||||
|
project_id: this.getProjectId(),
|
||||||
|
name: this.environmentForm.name,
|
||||||
|
variables
|
||||||
|
}).then(res => {
|
||||||
|
const message = (res && res.message) || ''
|
||||||
|
if (res && res.code === 20000) {
|
||||||
|
this.$message.success(message || '环境新增成功')
|
||||||
|
this.environmentDialogVisible = false
|
||||||
|
this.environmentPageNo = 1
|
||||||
|
this.fetchData()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$message.error(message || '环境新增失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.environmentSubmitting = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleMemberSizeChange(val) {
|
||||||
|
this.memberPageSize = val
|
||||||
|
this.memberPageNo = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
handleMemberCurrentChange(val) {
|
||||||
|
this.memberPageNo = val
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
handleEnvironmentSizeChange(val) {
|
||||||
|
this.environmentPageSize = val
|
||||||
|
this.environmentPageNo = 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
handleEnvironmentCurrentChange(val) {
|
||||||
|
this.environmentPageNo = val
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-wrap {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
104
src/components/TestPlatform/Report/ReportList.vue
Normal file
104
src/components/TestPlatform/Report/ReportList.vue
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="测试报告">
|
||||||
|
<template slot="extra">
|
||||||
|
<el-button type="primary" size="small" :loading="generating" @click="handleGenerate">生成报告</el-button>
|
||||||
|
</template>
|
||||||
|
<el-form :inline="true" :model="queryForm" size="small">
|
||||||
|
<el-form-item label="项目ID">
|
||||||
|
<el-input v-model="projectId" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划ID">
|
||||||
|
<el-input v-model="queryForm.plan_id" style="width: 120px;"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="fetchList">查询</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table v-loading="loading" :data="tableData" border style="margin-top: 16px;">
|
||||||
|
<el-table-column prop="name" label="报告名称" min-width="180"></el-table-column>
|
||||||
|
<el-table-column prop="report_type" label="类型" width="120"></el-table-column>
|
||||||
|
<el-table-column prop="generated_at" label="生成时间" min-width="160"></el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="goViewer(scope.row)">查看</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 16px; text-align: right;">
|
||||||
|
<el-pagination
|
||||||
|
:current-page="pageNo"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange">
|
||||||
|
</el-pagination>
|
||||||
|
</div>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import { generateReport, getReportList } from '@/api/reportApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ReportList',
|
||||||
|
components: { PageSection },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
generating: false,
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
queryForm: {
|
||||||
|
plan_id: this.$route.query.planId || ''
|
||||||
|
},
|
||||||
|
tableData: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchList() {
|
||||||
|
this.loading = true
|
||||||
|
getReportList(this.projectId, Object.assign({}, this.queryForm, {
|
||||||
|
pageNo: this.pageNo,
|
||||||
|
pageSize: this.pageSize
|
||||||
|
})).then(res => {
|
||||||
|
const data = (res && res.data) || res || {}
|
||||||
|
this.tableData = data.items || data.list || data.data || []
|
||||||
|
this.total = data.total || data.totalCount || this.tableData.length
|
||||||
|
}).catch(() => {
|
||||||
|
this.tableData = []
|
||||||
|
this.total = 0
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleGenerate() {
|
||||||
|
if (!this.queryForm.plan_id) {
|
||||||
|
this.$message({ type: 'warning', message: '请先输入计划ID' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.generating = true
|
||||||
|
generateReport(this.projectId, { plan_id: this.queryForm.plan_id }).then(() => {
|
||||||
|
this.$message({ type: 'success', message: '报告生成任务已提交' })
|
||||||
|
}).finally(() => {
|
||||||
|
this.generating = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
goViewer(row) {
|
||||||
|
this.$router.push({ path: '/test-platform/reports/viewer', query: { projectId: this.projectId, reportId: row.id } })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
58
src/components/TestPlatform/Report/ReportViewer.vue
Normal file
58
src/components/TestPlatform/Report/ReportViewer.vue
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-wrap">
|
||||||
|
<page-section title="报告查看">
|
||||||
|
<el-alert
|
||||||
|
v-if="!report.content"
|
||||||
|
title="当前无可展示内容,待后端返回 report.content HTML"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom: 16px;">
|
||||||
|
</el-alert>
|
||||||
|
<div class="report-html" v-html="report.content || ''"></div>
|
||||||
|
<el-divider></el-divider>
|
||||||
|
<json-viewer :value="report.summary || {}"></json-viewer>
|
||||||
|
</page-section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PageSection from '@/components/TestPlatform/common/PageSection'
|
||||||
|
import JsonViewer from '@/components/TestPlatform/common/JsonViewer'
|
||||||
|
import { getReportDetail } from '@/api/reportApi'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ReportViewer',
|
||||||
|
components: { PageSection, JsonViewer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projectId: this.$route.query.projectId || 1,
|
||||||
|
reportId: this.$route.query.reportId || '',
|
||||||
|
report: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchDetail() {
|
||||||
|
if (!this.reportId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getReportDetail(this.projectId, this.reportId).then(res => {
|
||||||
|
this.report = (res && res.data) || res || {}
|
||||||
|
}).catch(() => {
|
||||||
|
this.report = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-wrap {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.report-html {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
40
src/components/TestPlatform/common/JsonViewer.vue
Normal file
40
src/components/TestPlatform/common/JsonViewer.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<pre class="json-viewer">{{ formattedValue }}</pre>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'JsonViewer',
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [Object, Array, String, Number, Boolean],
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
formattedValue() {
|
||||||
|
if (typeof this.value === 'string') {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(this.value), null, 2)
|
||||||
|
} catch (e) {
|
||||||
|
return this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.stringify(this.value || {}, null, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.json-viewer {
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px;
|
||||||
|
background: #0f172a;
|
||||||
|
color: #e2e8f0;
|
||||||
|
border-radius: 6px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
37
src/components/TestPlatform/common/KeyValueDescriptions.vue
Normal file
37
src/components/TestPlatform/common/KeyValueDescriptions.vue
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<el-descriptions :column="column" border>
|
||||||
|
<el-descriptions-item
|
||||||
|
v-for="item in items"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label">
|
||||||
|
{{ formatValue(item.value) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'KeyValueDescriptions',
|
||||||
|
props: {
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
type: Number,
|
||||||
|
default: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatValue(value) {
|
||||||
|
if (value === null || typeof value === 'undefined' || value === '') {
|
||||||
|
return '-'
|
||||||
|
}
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
return JSON.stringify(value)
|
||||||
|
}
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
39
src/components/TestPlatform/common/PageSection.vue
Normal file
39
src/components/TestPlatform/common/PageSection.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="never" class="page-section">
|
||||||
|
<div slot="header" class="section-header">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<div v-if="$slots.extra" class="section-extra">
|
||||||
|
<slot name="extra"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section-body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageSection',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-section {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.section-extra {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<span class="ct-img-yhm"> </span>
|
<span class="ct-img-yhm"> </span>
|
||||||
<input
|
<input
|
||||||
id="username"
|
id="username"
|
||||||
v-model="username"
|
v-model.trim="username"
|
||||||
name="username"
|
name="username"
|
||||||
class="input_text"
|
class="input_text"
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
type="text"
|
type="text"
|
||||||
size="25"
|
size="25"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="学号">
|
placeholder="用户名"
|
||||||
|
@keyup.enter="handleLogin">
|
||||||
</div>
|
</div>
|
||||||
<div class="ct_input" style="height:60px;width: 254px">
|
<div class="ct_input" style="height:60px;width: 254px">
|
||||||
<span class="ct_img_mm"> </span>
|
<span class="ct_img_mm"> </span>
|
||||||
@@ -34,14 +35,13 @@
|
|||||||
type="password"
|
type="password"
|
||||||
size="25"
|
size="25"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder="密码">
|
placeholder="密码"
|
||||||
|
@keyup.enter="handleLogin">
|
||||||
</div>
|
</div>
|
||||||
<input class="btn_login" value="登录" @click="handleLogin">
|
<input class="btn_login" value="登录" @click="handleLogin">
|
||||||
</div>
|
</div>
|
||||||
<div class="account-oprate clearfix">
|
<div class="account-oprate clearfix">
|
||||||
<router-link :to="{ name: 'register' }">
|
<router-link :to="{ name: 'register' }" class="regist-btn">注册</router-link>
|
||||||
<a class="regist-btn">注册</a>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,31 +49,70 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Login } from '@/api/Userapi'
|
import { Login } from '@/api/Userapi'
|
||||||
|
import { getRoleList } from '@/api/rbacApi'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
msg: ' ',
|
msg: '',
|
||||||
username: null,
|
username: '',
|
||||||
password: null
|
password: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleLogin() {
|
handleLogin() {
|
||||||
|
if (!this.username || !this.password) {
|
||||||
|
this.msg = 'username、password 为必传参数'
|
||||||
|
return
|
||||||
|
}
|
||||||
Login({
|
Login({
|
||||||
studentId: this.username,
|
username: this.username,
|
||||||
password: this.password
|
password: this.password
|
||||||
}).then(data => {
|
}).then(res => {
|
||||||
if (data.code === 200 && data.success === 'true') {
|
if (res && res.code === 20000) {
|
||||||
localStorage.setItem('userinfo', JSON.stringify(data.data.userinfo))
|
const data = res.data || {}
|
||||||
localStorage.setItem('Token', data.data.token)
|
const user = {
|
||||||
this.$store.commit('SetRole', data.data.userinfo.userStatus)
|
id: data.id,
|
||||||
|
username: data.username,
|
||||||
|
realName: data.real_name,
|
||||||
|
mobile: data.mobile,
|
||||||
|
email: data.email,
|
||||||
|
avatar: data.avatar,
|
||||||
|
status: data.status,
|
||||||
|
lastLoginTime: data.last_login_time,
|
||||||
|
createdBy: data.created_by,
|
||||||
|
createdTime: data.created_time,
|
||||||
|
updatedTime: data.updated_time,
|
||||||
|
roleIds: data.role_ids || []
|
||||||
|
}
|
||||||
|
localStorage.setItem('authUser', JSON.stringify(user))
|
||||||
|
if (data.token) {
|
||||||
|
localStorage.setItem('accessToken', data.token)
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('accessToken')
|
||||||
|
}
|
||||||
|
this.$store.commit('SetCurrentUser', user)
|
||||||
|
this.$store.commit('SetRole', user.roleIds)
|
||||||
|
this.$store.commit('SetUserMenus', [])
|
||||||
|
this.loadUserMenus(user)
|
||||||
|
this.msg = ''
|
||||||
|
this.$message.success('登录成功')
|
||||||
this.$router.push({ path: '/effekt' })
|
this.$router.push({ path: '/effekt' })
|
||||||
} else {
|
} else {
|
||||||
this.msg = data.message
|
this.msg = (res && res.message) || '用户名或密码错误!'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
loadUserMenus(user) {
|
||||||
|
const roleId = user && user.roleIds && user.roleIds.length ? user.roleIds[0] : undefined
|
||||||
|
if (!roleId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getRoleList({ roleId }).then(res => {
|
||||||
|
const menus = res && Array.isArray(res.data) ? res.data : []
|
||||||
|
this.$store.commit('SetUserMenus', menus)
|
||||||
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<div class="location-title"><h1>注册</h1></div>
|
<div class="location-title"><h1>注册</h1></div>
|
||||||
|
|
||||||
<el-form ref="ruleForm" :model="ruleForm" status-icon :rules="rules" label-width="100px" class="demo-ruleForm">
|
<el-form ref="ruleForm" :model="ruleForm" status-icon :rules="rules" label-width="100px" class="demo-ruleForm">
|
||||||
<el-form-item label="学号" prop="studentId">
|
<el-form-item label="用户名" prop="username">
|
||||||
<el-input v-model="ruleForm.studentId" type="text" placeholder="学号" autocomplete="off"></el-input>
|
<el-input v-model.trim="ruleForm.username" type="text" placeholder="用户名" autocomplete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="密码" prop="password">
|
<el-form-item label="密码" prop="password">
|
||||||
<el-input v-model="ruleForm.password" type="password" placeholder="密码" autocomplete="off"></el-input>
|
<el-input v-model="ruleForm.password" type="password" placeholder="密码" autocomplete="off"></el-input>
|
||||||
@@ -14,22 +14,17 @@
|
|||||||
<el-form-item label="确认密码" prop="checkPass">
|
<el-form-item label="确认密码" prop="checkPass">
|
||||||
<el-input v-model="ruleForm.checkPass" type="password" placeholder="确认密码" autocomplete="off"></el-input>
|
<el-input v-model="ruleForm.checkPass" type="password" placeholder="确认密码" autocomplete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="姓名" prop="name">
|
<el-form-item label="手机号" prop="mobile">
|
||||||
<el-input v-model="ruleForm.name" placeholder="姓名" autocomplete="off"></el-input>
|
<el-input v-model.trim="ruleForm.mobile" placeholder="手机号" autocomplete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="性别" prop="gender">
|
<el-form-item label="邮箱" prop="email">
|
||||||
<el-input v-model="ruleForm.gender" placeholder="性别" autocomplete="off"></el-input>
|
<el-input v-model.trim="ruleForm.email" type="text" placeholder="邮箱" autocomplete="off"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="学院" prop="faculty">
|
<el-form-item class="register-actions">
|
||||||
<el-input v-model="ruleForm.faculty" type="text" placeholder="学院" autocomplete="off"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="年级" prop="grade">
|
|
||||||
<el-input v-model="ruleForm.grade" type="text" placeholder="年级" autocomplete="off"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button class="enter-btn" type="primary" :disabled="!select" @click="submitForm('ruleForm')">
|
<el-button class="enter-btn" type="primary" :disabled="!select" @click="submitForm('ruleForm')">
|
||||||
立即注册
|
立即注册
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button class="login-link-btn" type="text" @click="goLogin">去登录</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,9 +56,9 @@ export default {
|
|||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const validateStudentId = (rule, value, callback) => {
|
const validateUsername = (rule, value, callback) => {
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
callback(new Error('请输入学号'))
|
callback(new Error('请输入用户名'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
callback()
|
callback()
|
||||||
@@ -72,22 +67,16 @@ export default {
|
|||||||
return {
|
return {
|
||||||
select: true,
|
select: true,
|
||||||
ruleForm: {
|
ruleForm: {
|
||||||
studentId: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
checkPass: '',
|
checkPass: '',
|
||||||
name: '',
|
mobile: '',
|
||||||
gender: '',
|
email: ''
|
||||||
faculty: '',
|
|
||||||
grade: ''
|
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
studentId: [{ required: true, validator: validateStudentId, trigger: 'blur' }],
|
username: [{ required: true, validator: validateUsername, trigger: 'blur' }],
|
||||||
password: [{ required: true, validator: validatePass, trigger: 'blur' }],
|
password: [{ required: true, validator: validatePass, trigger: 'blur' }],
|
||||||
checkPass: [{ required: true, validator: validatePass2, trigger: 'blur' }],
|
checkPass: [{ required: true, validator: validatePass2, trigger: 'blur' }]
|
||||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
|
||||||
gender: [{ required: true, message: '请输入性别', trigger: 'blur' }],
|
|
||||||
faculty: [{ required: true, message: '请输入学院', trigger: 'blur' }],
|
|
||||||
grade: [{ required: true, message: '请输入年级', trigger: 'blur' }]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -99,18 +88,17 @@ export default {
|
|||||||
},
|
},
|
||||||
handleRegister() {
|
handleRegister() {
|
||||||
Register({
|
Register({
|
||||||
studentId: this.ruleForm.studentId,
|
username: this.ruleForm.username,
|
||||||
password: this.ruleForm.password,
|
password: this.ruleForm.password,
|
||||||
name: this.ruleForm.name,
|
mobile: this.ruleForm.mobile,
|
||||||
gender: this.ruleForm.gender,
|
email: this.ruleForm.email,
|
||||||
faculty: this.ruleForm.faculty,
|
createdBy: 1
|
||||||
grade: this.ruleForm.grade
|
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
if (data.code === 200 && data.success === 'true') {
|
if (data && data.id) {
|
||||||
this.open('注册成功')
|
this.open('注册成功')
|
||||||
this.$router.push({ name: 'login' })
|
this.$router.push({ name: 'login' })
|
||||||
} else {
|
} else {
|
||||||
this.open(data.message)
|
this.open(data.message || '注册失败')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -120,6 +108,9 @@ export default {
|
|||||||
this.handleRegister()
|
this.handleRegister()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
goLogin() {
|
||||||
|
this.$router.push({ name: 'login' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,4 +131,20 @@ export default {
|
|||||||
float: left;
|
float: left;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.register-actions .el-form-item__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-link-wrap {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-link-btn {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -67,6 +67,146 @@ export default new Router({
|
|||||||
components: {
|
components: {
|
||||||
Manage: (resolve) => require(['@/components/EffektHome'], resolve)
|
Manage: (resolve) => require(['@/components/EffektHome'], resolve)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/projects',
|
||||||
|
name: 'ProjectList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Project/ProjectList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/products',
|
||||||
|
name: 'ProductList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Product/ProductList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/projects/detail',
|
||||||
|
name: 'ProjectDetail',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Project/ProjectDetail'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/projects/settings',
|
||||||
|
name: 'ProjectSettings',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Project/ProjectSettings'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/cases',
|
||||||
|
name: 'CaseList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Case/CaseList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/cases/editor',
|
||||||
|
name: 'CaseEditor',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Case/CaseEditor'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/cases/review',
|
||||||
|
name: 'CaseReview',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Case/CaseReview'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/plans',
|
||||||
|
name: 'PlanList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Plan/PlanList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/plans/builder',
|
||||||
|
name: 'PlanBuilder',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Plan/PlanBuilder'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/plans/execute',
|
||||||
|
name: 'PlanExecute',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Plan/PlanExecute'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/plans/progress',
|
||||||
|
name: 'PlanProgress',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Plan/PlanProgress'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/reports',
|
||||||
|
name: 'ReportList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Report/ReportList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/reports/viewer',
|
||||||
|
name: 'ReportViewer',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/Report/ReportViewer'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/data-factory/builders',
|
||||||
|
name: 'BuilderList',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/DataFactory/BuilderList'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/data-factory/editor',
|
||||||
|
name: 'BuilderEditor',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/DataFactory/BuilderEditor'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/data-factory/tasks',
|
||||||
|
name: 'TaskHistory',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/DataFactory/TaskHistory'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/test-platform/data-factory/mock',
|
||||||
|
name: 'MockService',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/TestPlatform/DataFactory/MockService'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/roles',
|
||||||
|
name: 'RoleManage',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/System/RoleManage'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/users',
|
||||||
|
name: 'UserManage',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/System/UserManage'], resolve)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/menus',
|
||||||
|
name: 'MenuManage',
|
||||||
|
components: {
|
||||||
|
Manage: (resolve) => require(['@/components/System/MenuManage'], resolve)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { Message } from 'element-ui';
|
|||||||
import router from '../router/index'
|
import router from '../router/index'
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
// baseURL: 'http://10.250.0.252:5010', // api 的 base_url
|
// baseURL: 'http://10.250.0.252:5010', // api 的 base_url
|
||||||
baseURL: '', // api 的 base_url
|
// baseURL: '', // api 的 base_url
|
||||||
// baseURL: 'http://192.168.11.46:5010', // api 的 base_url
|
baseURL: '/it/api', // api 的 base_url
|
||||||
|
|
||||||
timeout: 90000 // request timeout
|
timeout: 90000 // request timeout
|
||||||
})
|
})
|
||||||
@@ -12,7 +12,10 @@ const service = axios.create({
|
|||||||
// 请求拦截 设置统一header
|
// 请求拦截 设置统一header
|
||||||
service.interceptors.request.use(
|
service.interceptors.request.use(
|
||||||
config => {
|
config => {
|
||||||
config.headers.Token=localStorage.getItem('Token')
|
const accessToken = localStorage.getItem('accessToken')
|
||||||
|
if (accessToken) {
|
||||||
|
config.headers.accessToken = accessToken
|
||||||
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
|||||||
@@ -4,14 +4,27 @@ import vue from 'vue'
|
|||||||
vue.use(vuex)
|
vue.use(vuex)
|
||||||
|
|
||||||
const state={
|
const state={
|
||||||
userRole:null
|
userRole:null,
|
||||||
|
currentUser: JSON.parse(localStorage.getItem('authUser') || 'null'),
|
||||||
|
userMenus: JSON.parse(localStorage.getItem('userMenus') || '[]')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mutations={
|
const mutations={
|
||||||
SetRole(state,val){
|
SetRole(state,val){
|
||||||
state.userRole=val
|
state.userRole=val
|
||||||
|
},
|
||||||
|
SetCurrentUser(state, user) {
|
||||||
|
state.currentUser = user
|
||||||
|
},
|
||||||
|
SetUserMenus(state, menus) {
|
||||||
|
state.userMenus = menus || []
|
||||||
|
localStorage.setItem('userMenus', JSON.stringify(state.userMenus))
|
||||||
|
},
|
||||||
|
ClearCurrentUser(state) {
|
||||||
|
state.currentUser = null
|
||||||
|
state.userRole = null
|
||||||
|
state.userMenus = []
|
||||||
|
localStorage.removeItem('userMenus')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user