提交 6c90045f 编写于 作者: aaronchen2k2k's avatar aaronchen2k2k

zentao site admin

上级 f76f80ca
......@@ -3,6 +3,7 @@ package controller
import (
commConsts "github.com/aaronchen2k/deeptest/internal/comm/consts"
serverDomain "github.com/aaronchen2k/deeptest/internal/server/modules/v1/domain"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/service"
"github.com/kataras/iris/v12"
)
......@@ -16,16 +17,14 @@ func NewSiteCtrl() *SiteCtrl {
return &SiteCtrl{}
}
// List 分页列表
func (c *SiteCtrl) List(ctx iris.Context) {
projectPath := ctx.URLParam("currProject")
if projectPath == "" {
ctx.JSON(c.SuccessResp(make([]serverDomain.TestReportSummary, 0)))
var req serverDomain.ReqPaginate
if err := ctx.ReadQuery(&req); err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
}
data, err := c.SiteService.List(projectPath)
data, err := c.SiteService.Paginate(req)
if err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
......@@ -34,35 +33,59 @@ func (c *SiteCtrl) List(ctx iris.Context) {
ctx.JSON(c.SuccessResp(data))
}
// Get 详情
func (c *SiteCtrl) Get(ctx iris.Context) {
projectPath := ctx.URLParam("currProject")
id, err := ctx.Params().GetInt("id")
if err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
}
seq := ctx.Params().Get("seq")
if seq == "" {
c.ErrResp(commConsts.ParamErr, "seq")
po, err := c.SiteService.Get(uint(id))
if err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
}
ctx.JSON(c.SuccessResp(po))
}
func (c *SiteCtrl) Create(ctx iris.Context) {
req := model.Site{}
if err := ctx.ReadJSON(&req); err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
}
exec, err := c.SiteService.Get(projectPath, seq)
id, err := c.SiteService.Create(req)
if err != nil {
c.ErrResp(commConsts.Failure, err.Error())
return
}
ctx.JSON(c.SuccessResp(iris.Map{"id": id}))
}
func (c *SiteCtrl) Update(ctx iris.Context) {
req := model.Site{}
if err := ctx.ReadJSON(&req); err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
}
err := c.SiteService.Update(req)
if err != nil {
c.ErrResp(commConsts.Failure, err.Error())
return
}
ctx.JSON(c.SuccessResp(exec))
ctx.JSON(c.SuccessResp(iris.Map{"id": req.ID}))
}
// Delete 删除
func (c *SiteCtrl) Delete(ctx iris.Context) {
projectPath := ctx.URLParam("currProject")
seq := ctx.Params().Get("seq")
if seq == "" {
c.ErrResp(commConsts.ParamErr, "seq")
id, err := ctx.Params().GetInt("id")
if err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
}
err := c.SiteService.Delete(projectPath, seq)
err = c.SiteService.Delete(uint(id))
if err != nil {
ctx.JSON(c.ErrResp(commConsts.Failure, err.Error()))
return
......
package serverDomain
import (
"github.com/aaronchen2k/deeptest/internal/pkg/domain"
)
type ProjectReqPaginate struct {
domain.PaginateReq
Keywords string `json:"keywords"`
Enabled string `json:"enabled"`
}
......@@ -20,9 +20,11 @@ func (m *SiteModule) Party() module.WebModule {
handler := func(index iris.Party) {
index.Use(middleware.InitCheck())
index.Get("/", m.SiteCtrl.List).Name = "执行列表"
index.Get("/{seq:string}", m.SiteCtrl.Get).Name = "执行详情"
index.Delete("/{seq:string}", m.SiteCtrl.Delete).Name = "删除执行"
index.Get("/", m.SiteCtrl.List).Name = "列表"
index.Get("/{id:int}", m.SiteCtrl.Get).Name = "详情"
index.Post("/{id:int}", m.SiteCtrl.Create).Name = "保存"
index.Put("/{id:int}", m.SiteCtrl.Update).Name = "保存"
index.Delete("/{id:int}", m.SiteCtrl.Delete).Name = "删除"
}
return module.NewModule("/sites", handler)
}
......@@ -3,9 +3,14 @@ package repo
import (
"errors"
"fmt"
"github.com/aaronchen2k/deeptest/internal/pkg/domain"
commonUtils "github.com/aaronchen2k/deeptest/internal/pkg/lib/common"
logUtils "github.com/aaronchen2k/deeptest/internal/pkg/lib/log"
"github.com/aaronchen2k/deeptest/internal/server/core/dao"
serverDomain "github.com/aaronchen2k/deeptest/internal/server/modules/v1/domain"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/fatih/color"
"go.uber.org/zap"
"gorm.io/gorm"
)
......@@ -17,15 +22,40 @@ func NewSiteRepo() *SiteRepo {
return &SiteRepo{}
}
func (r *SiteRepo) List() (sites []model.Site, err error) {
err = r.DB.Model(&model.Site{}).
Where("NOT deleted").
Find(&sites).Error
func (r *SiteRepo) Paginate(req serverDomain.ReqPaginate) (data domain.PageData, err error) {
var count int64
db := r.DB.Model(&model.Site{}).Where("NOT deleted")
if req.Keywords != "" {
db = db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", req.Keywords))
}
if req.Enabled != "" {
db = db.Where("disabled = ?", commonUtils.IsDisable(req.Enabled))
}
err = db.Count(&count).Error
if err != nil {
logUtils.Errorf("count project error", zap.String("error:", err.Error()))
return
}
pos := make([]*model.Site, 0)
err = db.
Scopes(dao.PaginateScope(req.Page, req.PageSize, req.Order, req.Field)).
Find(&pos).Error
if err != nil {
logUtils.Errorf("query project error", zap.String("error:", err.Error()))
return
}
data.Populate(pos, count, req.Page, req.PageSize)
return
}
func (r *SiteRepo) FindById(id uint) (po model.Site, err error) {
func (r *SiteRepo) Get(id uint) (po model.Site, err error) {
err = r.DB.Model(&model.Site{}).
Where("id = ?", id).
Where("NOT deleted").
......@@ -55,27 +85,26 @@ func (r *SiteRepo) Create(project model.Site) (id uint, err error) {
return
}
func (r *SiteRepo) Update(id uint, project model.Site) error {
po, err := r.FindDuplicate(project.Name, project.Url, id)
func (r *SiteRepo) Update(site model.Site) error {
po, err := r.FindDuplicate(site.Name, site.Url, site.ID)
if po.ID != 0 {
return errors.New(fmt.Sprintf("站点%s(%s)已存在", project.Name, project.Url))
return errors.New(fmt.Sprintf("站点%s(%s)已存在", site.Name, site.Url))
}
err = r.DB.Model(&model.Site{}).Where("id = ?", id).Updates(&project).Error
err = r.DB.Model(&model.Site{}).Where("id = ?", site.ID).Updates(&site).Error
if err != nil {
logUtils.Errorf(color.RedString("update project failed, error: %s.", err.Error()))
logUtils.Errorf(color.RedString("update site failed, error: %s.", err.Error()))
return err
}
return nil
}
func (r *SiteRepo) DeleteByPath(pth string) (err error) {
err = r.DB.Where("path = ?", pth).
Delete(&model.Site{}).
Error
func (r *SiteRepo) Delete(id uint) (err error) {
err = r.DB.Model(&model.Site{}).Where("id = ?", id).
Updates(map[string]interface{}{"deleted": true}).Error
if err != nil {
logUtils.Errorf(color.RedString("delete project failed, error: %s.", err.Error()))
logUtils.Errorf("delete site by id error", zap.String("error:", err.Error()))
return
}
......
package service
import (
commConsts "github.com/aaronchen2k/deeptest/internal/comm/consts"
commDomain "github.com/aaronchen2k/deeptest/internal/comm/domain"
analysisUtils "github.com/aaronchen2k/deeptest/internal/comm/helper/analysis"
fileUtils "github.com/aaronchen2k/deeptest/internal/pkg/lib/file"
"github.com/aaronchen2k/deeptest/internal/pkg/domain"
serverDomain "github.com/aaronchen2k/deeptest/internal/server/modules/v1/domain"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/repo"
"github.com/jinzhu/copier"
"path/filepath"
)
type SiteService struct {
ProjectRepo *repo.ProjectRepo `inject:""`
SiteRepo *repo.SiteRepo `inject:""`
}
func NewSiteService() *SiteService {
return &SiteService{}
}
func (s *SiteService) List(projectPath string) (ret []serverDomain.TestReportSummary, err error) {
reportFiles := analysisUtils.ListReport(projectPath)
for _, seq := range reportFiles {
var summary serverDomain.TestReportSummary
report, err1 := analysisUtils.ReadReportByProjectSeq(projectPath, seq)
if err1 != nil { // ignore wrong json result
continue
}
copier.Copy(&summary, report)
summary.Seq = seq
ret = append(ret, summary)
}
func (s *SiteService) Paginate(req serverDomain.ReqPaginate) (ret domain.PageData, err error) {
ret, err = s.SiteRepo.Paginate(req)
return
}
func (s *SiteService) Get(projectPath string, seq string) (report commDomain.ZtfReport, err error) {
return analysisUtils.ReadReportByProjectSeq(projectPath, seq)
func (s *SiteService) Get(id uint) (site model.Site, err error) {
return s.SiteRepo.Get(id)
}
func (s *SiteService) Delete(projectPath string, seq string) (err error) {
dir := filepath.Join(projectPath, commConsts.LogDirName)
func (s *SiteService) Create(site model.Site) (uint, error) {
return s.SiteRepo.Create(site)
}
di := filepath.Join(dir, seq)
err = fileUtils.RmDir(di)
func (s *SiteService) Update(site model.Site) error {
return s.SiteRepo.Update(site)
}
return
func (s *SiteService) Delete(id uint) error {
return s.SiteRepo.Delete(id)
}
export default {
'index-layout.menu.script': 'Script',
'index-layout.menu.script.list': 'Script List',
'index-layout.menu.script.view': 'View Script',
'index-layout.menu.script.edit': 'Edit Script',
'index-layout.menu.execution': 'Execution',
'index-layout.menu.execution.history': 'Execution History',
'index-layout.menu.execution.result.func': 'Functional Test Result',
'index-layout.menu.execution.result.unit': 'Unit Test Result',
'index-layout.menu.execution.execCase': 'Execute By Case',
'index-layout.menu.execution.execModule': 'Execute By Module',
'index-layout.menu.execution.execSuite': 'Execute By Suite',
'index-layout.menu.execution.execTask': 'Execute By Task',
'index-layout.menu.execution.execUnit': 'Execute UnitTest',
'index-layout.menu.config': 'Configuration',
'index-layout.menu.sync': 'Synchronization',
};
export default {
'index-layout.menu.script': '脚本',
'index-layout.menu.script.list': '脚本列表',
'index-layout.menu.script.view': '查看脚本',
'index-layout.menu.script.edit': '编辑脚本',
'index-layout.menu.execution': '执行',
'index-layout.menu.execution.history': '执行历史',
'index-layout.menu.execution.result.func': '功能测试结果',
'index-layout.menu.execution.result.unit': '单元测试结果',
'index-layout.menu.execution.execCase': '执行用例',
'index-layout.menu.execution.execModule': '执行模块',
'index-layout.menu.execution.execSuite': '执行套件',
'index-layout.menu.execution.execTask': '执行任务',
'index-layout.menu.execution.execUnit': '执行单元测试',
'index-layout.menu.config': '配置',
'index-layout.menu.sync': '同步',
};
......@@ -4,13 +4,13 @@ import BlankLayout from '@/layouts/BlankLayout.vue';
const IndexLayoutRoutes: Array<RoutesDataItem> = [
{
icon: 'script',
title: 'index-layout.menu.script',
title: 'script',
path: '/script',
redirect: '/script/list',
component: BlankLayout,
children: [
{
title: 'index-layout.menu.script.list',
title: 'script.list',
path: 'list',
component: () => import('@/views/script/index/main.vue'),
hidden: true,
......@@ -20,62 +20,62 @@ const IndexLayoutRoutes: Array<RoutesDataItem> = [
{
icon: 'execution',
title: 'index-layout.menu.execution',
title: 'execution',
path: '/exec',
redirect: '/exec/history',
component: BlankLayout,
children: [
{
title: 'index-layout.menu.execution.history',
title: 'execution.history',
path: 'history',
component: () => import('@/views/exec/history/index.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.result.func',
title: 'execution.result.func',
path: 'history/func/:seq',
component: () => import('@/views/exec/history/result-func.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.result.unit',
title: 'execution.result.unit',
path: 'history/unit/:seq',
component: () => import('@/views/exec/history/result-unit.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution',
title: 'execution',
path: 'run',
component: BlankLayout,
hidden: true,
children: [
{
title: 'index-layout.menu.execution.execCase',
title: 'execution.execCase',
path: 'case/:seq/:scope',
component: () => import('@/views/exec/exec/case.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.execModule',
title: 'execution.execModule',
path: 'module/:productId/:moduleId/:seq/:scope',
component: () => import('@/views/exec/exec/module.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.execSuite',
title: 'execution.execSuite',
path: 'suite/:productId/:suiteId/:seq/:scope',
component: () => import('@/views/exec/exec/suite.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.execTask',
title: 'execution.execTask',
path: 'task/:productId/:taskId/:seq/:scope',
component: () => import('@/views/exec/exec/task.vue'),
hidden: true,
},
{
title: 'index-layout.menu.execution.execUnit',
title: 'execution.execUnit',
path: 'unit',
component: () => import('@/views/exec/exec/unit.vue'),
hidden: true,
......@@ -87,14 +87,14 @@ const IndexLayoutRoutes: Array<RoutesDataItem> = [
{
icon: 'config',
title: 'index-layout.menu.config',
title: 'zentao_config',
path: '/config',
component: () => import('@/views/config/index.vue'),
},
{
icon: 'sync',
title: 'index-layout.menu.sync',
title: 'sync',
path: '/sync',
component: () => import('@/views/sync/index.vue'),
},
......@@ -108,11 +108,17 @@ const IndexLayoutRoutes: Array<RoutesDataItem> = [
hidden: true,
children: [
{
title: 'index-layout.menu.script.list',
title: 'zentao_site',
path: 'list',
component: () => import('@/views/site/index.vue'),
hidden: true,
},
{
title: 'edit_site',
path: 'edit/:id',
component: () => import('@/views/site/edit.vue'),
hidden: true,
},
],
},
......
......@@ -23,8 +23,13 @@ export default {
'step': 'Step',
'status': 'Status',
'all': 'All',
'only_failed': 'Only Failed',
'edit_config': 'Eidt Config',
'only_failed': 'Only Failed',
'zentao_site': 'Zentao Site',
'edit_site': 'Edit Site',
'zentao_config': 'Site Config',
'edit_config': 'Edit Config',
'sync': 'Synchronization',
'zentao_url': 'ZenTao URL',
'username': 'User Name',
'password': 'Password',
......@@ -33,6 +38,21 @@ export default {
'interpreter_path': 'Interpreter Path',
'script_lang': 'Script Language',
'script': 'Script',
'script.list': 'Script List',
'script.view': 'View Script',
'script.edit': 'Edit Script',
'execution': 'Execution',
'execution.history': 'Execution History',
'execution.result.func': 'Functional Test Result',
'execution.result.unit': 'Unit Test Result',
'execution.execCase': 'Execute By Case',
'execution.execModule': 'Execute By Module',
'execution.execSuite': 'Execute By Suite',
'execution.execTask': 'Execute By Task',
'execution.execUnit': 'Execute UnitTest',
'duration': 'Duration',
'duration_sec': 'Duration(sec)',
'sec': 'Sec',
......
......@@ -23,8 +23,12 @@ export default {
'step': '步骤',
'status': '状态',
'all': '所有',
'only_failed': '仅失败用例',
'only_failed': '仅失败用例',
'zentao_site': '禅道站点',
'edit_site': '编辑站点',
'zentao_config': '站点配置',
'sync': '同步',
'edit_config': '修改配置',
'zentao_url': '禅道地址',
'username': '用户名',
......@@ -35,6 +39,21 @@ export default {
'interpreter_path': '解析器路径',
'script_lang': '脚本语言',
'script': '脚本',
'script.list': '脚本列表',
'script.view': '查看脚本',
'script.edit': '编辑脚本',
'execution': '执行',
'execution.history': '执行历史',
'execution.result.func': '功能测试结果',
'execution.result.unit': '单元测试结果',
'execution.execCase': '执行用例',
'execution.execModule': '执行模块',
'execution.execSuite': '执行套件',
'execution.execTask': '执行任务',
'execution.execUnit': '执行单元测试',
'duration': '耗时',
'duration_sec': '耗时(秒)',
'sec': '',
......
<template>
<div v-if="!currProject.path">
<a-empty :image="simpleImage" :description="t('pls_create_project')"/>
</div>
<a-card v-if="currProject.path" :title="t('edit_config')">
<a-card :title="t('edit_site')">
<a-form :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item :label="t('zentao_url')" v-bind="validateInfos.url">
<a-input v-model:value="model.url"
......@@ -28,25 +24,19 @@
</template>
<script lang="ts">
import {defineComponent, ref, reactive, computed, watch, ComputedRef, Ref, toRaw, toRef} from "vue";
import {defineComponent, ref, reactive, computed, Ref, toRaw, toRef} from "vue";
import {useRouter} from "vue-router";
import { useI18n } from "vue-i18n";
import { Props, validateInfos } from 'ant-design-vue/lib/form/useForm';
import {message, Form, notification, Empty} from 'ant-design-vue';
import _ from "lodash";
import { validateInfos } from 'ant-design-vue/lib/form/useForm';
import {Form, notification} from 'ant-design-vue';
const useForm = Form.useForm;
import {useStore} from "vuex";
import {ProjectData} from "@/store/project";
import CreateInterpreterForm from './interpreter/create.vue';
import UpdateInterpreterForm from './interpreter/update.vue';
import IconSvg from "@/components/IconSvg/index";
import {StateType} from "@/views/site/store";
interface SiteFormSetupData {
t: (key: string | number) => string;
currProject: ComputedRef;
currSiteRef: Ref
model: Ref
rules: any
labelCol: any
......@@ -55,25 +45,6 @@ interface SiteFormSetupData {
validateInfos: validateInfos
submitForm: () => void;
resetFields: () => void;
languages: Ref
languageMap: Ref
interpreters: Ref<[]>
interpreter: Ref
createSubmitLoading: Ref<boolean>;
createFormVisible: Ref<boolean>;
setCreateFormVisible: (val: boolean) => void;
addInterpreter: () => void;
createSubmit: (values: any, resetFields: (newValues?: Props | undefined) => void) => Promise<void>;
updateSubmitLoading: Ref<boolean>;
updateFormVisible: Ref<boolean>;
editInterpreter: (item) => void;
deleteInterpreter: (item) => void;
updateFormCancel: () => void;
updateSubmit: (values: any, resetFields: (newValues?: Props | undefined) => void) => Promise<void>;
simpleImage: any
}
export default defineComponent({
......@@ -82,12 +53,16 @@ export default defineComponent({
},
setup(props): SiteFormSetupData {
const { t } = useI18n();
const router = useRouter();
const store = useStore<{ project: ProjectData }>();
const currSiteRef = computed<any>(() => store.state.project.currSite);
const currProject = computed<any>(() => store.state.project.currProject);
const store = useStore<{ Site: StateType }>();
const model = computed(() => store.state.Site.detailResult);
let model = reactive<any>(currSiteRef.value);
const get = async (id: number): Promise<void> => {
await store.dispatch('Site/get', id);
}
const id = +router.currentRoute.value.params.id
get(id)
const rules = reactive({
url: [
......@@ -119,7 +94,7 @@ export default defineComponent({
validate()
.then(() => {
console.log(model);
store.dispatch('project/saveSite', model).then((json) => {
store.dispatch('Site/save', model).then((json) => {
if (json.code === 0) {
notification.success({
message: t('save_success'),
......@@ -141,8 +116,6 @@ export default defineComponent({
t,
labelCol: { span: 4 },
wrapperCol: { span: 14 },
currSiteRef,
currProject,
model,
rules,
......@@ -150,7 +123,6 @@ export default defineComponent({
validate,
validateInfos,
submitForm,
simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
}
}
......
......@@ -4,7 +4,7 @@
<template #title>
<div class="t-card-toolbar">
<div class="left">
禅道站点列表
{{t('zentao_site')}}
</div>
<div class="right">
<a-select @change="onSearch" v-model:value="queryParams.enabled" :options="statusArr" class="status-select">
......@@ -105,6 +105,7 @@ interface SiteListSetupData {
loading: Ref<boolean>;
getList: (curr) => void
edit: (id) => void;
removeLoading: Ref<string[]>;
remove: (item) => void;
......@@ -199,6 +200,11 @@ export default defineComponent({
getList(1);
})
const edit = (id) => {
console.log('edit')
router.push(`/site/edit/${id}`)
}
const removeLoading = ref<string[]>([]);
const remove = (item) => {
Modal.confirm({
......@@ -228,6 +234,7 @@ export default defineComponent({
models,
loading,
getList,
edit,
removeLoading,
remove,
......
......@@ -11,12 +11,20 @@ export async function list(params?: QueryParams): Promise<any> {
});
}
export async function get(seq: string): Promise<any> {
export async function get(seq: number): Promise<any> {
return request({
url: `/${apiPath}/${seq}`
});
}
export async function save(params: any): Promise<any> {
return request({
url: `/${apiPath}`,
method: params.id? 'PUT': 'POST',
data: params,
});
}
export async function remove(seq: string): Promise<any> {
return request({
url: `/${apiPath}/${seq}`,
......
......@@ -3,7 +3,7 @@ import { StoreModuleType } from "@/utils/store";
import { ResponseData } from '@/utils/request';
import { QueryParams, QueryResult } from '@/types/data.d';
import {
list, get, remove,
list, get, save, remove,
} from './service';
export interface StateType {
......@@ -20,6 +20,7 @@ export interface ModuleType extends StoreModuleType<StateType> {
actions: {
list: Action<StateType, StateType>;
get: Action<StateType, StateType>;
save: Action<StateType, StateType>;
delete: Action<StateType, StateType>;
};
}
......@@ -64,11 +65,18 @@ const StoreModel: ModuleType = {
return false;
}
},
async get({ commit }, payload: string ) {
async get({ commit }, id: number ) {
let data = {}
if (id) {
const response: ResponseData = await get(id);
data = response.data;
}
commit('setDetailResult',data);
return true;
},
async save({ commit }, payload) {
try {
const response: ResponseData = await get(payload);
const { data } = response;
commit('setDetailResult',data);
await save(payload);
return true;
} catch (error) {
return false;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册