提交 28a7a592 编写于 作者: aaronchen2k2k's avatar aaronchen2k2k

manage interpreters

上级 a95b67ce
......@@ -3,7 +3,6 @@ package controller
import (
commConsts "github.com/aaronchen2k/deeptest/internal/comm/consts"
"github.com/aaronchen2k/deeptest/internal/pkg/domain"
i118Utils "github.com/aaronchen2k/deeptest/internal/pkg/lib/i118"
)
type BaseCtrl struct {
......@@ -26,11 +25,11 @@ func (c *BaseCtrl) ErrResp(err commConsts.ResponseCode, msg string) (ret domain.
}
func (c *BaseCtrl) ErrMsg(err commConsts.ResponseCode, msg string) (ret string) {
ret = i118Utils.Sprintf(err.Message)
if ret != "" {
ret += ": "
}
//ret = i118Utils.Sprintf(err.Message)
//
//if ret != "" {
// ret += ": "
//}
ret += msg
......
package controller
import (
commConsts "github.com/aaronchen2k/deeptest/internal/comm/consts"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/service"
"github.com/kataras/iris/v12"
)
type InterpreterCtrl struct {
InterpreterService *service.InterpreterService `inject:""`
BaseCtrl
}
func NewInterpreterCtrl() *InterpreterCtrl {
return &InterpreterCtrl{}
}
func (c *InterpreterCtrl) List(ctx iris.Context) {
data, err := c.InterpreterService.List()
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
ctx.JSON(c.SuccessResp(data))
}
func (c *InterpreterCtrl) Get(ctx iris.Context) {
id, err := ctx.Params().GetInt("id")
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
po, err := c.InterpreterService.Get(uint(id))
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
ctx.JSON(c.SuccessResp(po))
}
func (c *InterpreterCtrl) Create(ctx iris.Context) {
req := model.Interpreter{}
if err := ctx.ReadJSON(&req); err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
}
id, err := c.InterpreterService.Create(req)
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
ctx.JSON(c.SuccessResp(iris.Map{"id": id}))
}
func (c *InterpreterCtrl) Update(ctx iris.Context) {
req := model.Interpreter{}
if err := ctx.ReadJSON(&req); err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
}
err := c.InterpreterService.Update(req)
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
ctx.JSON(c.SuccessResp(iris.Map{"id": req.ID}))
}
func (c *InterpreterCtrl) Delete(ctx iris.Context) {
id, err := ctx.Params().GetInt("id")
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
err = c.InterpreterService.Delete(uint(id))
if err != nil {
ctx.JSON(c.ErrResp(commConsts.CommErr, err.Error()))
return
}
ctx.JSON(c.SuccessResp(nil))
}
......@@ -13,12 +13,12 @@ import (
type IndexModule struct {
FileModule *index.FileModule `inject:""`
SiteModule *index.SiteModule `inject:""`
ZentaoModule *index.ZentaoModule `inject:""`
SiteModule *index.SiteModule `inject:""`
ZentaoModule *index.ZentaoModule `inject:""`
ConfigModule *index.ConfigModule `inject:""`
SyncModule *index.SyncModule `inject:""`
WorkspaceModule *index.WorkspaceModule `inject:""`
InterpreterModule *index.InterpreterModule `inject:""`
SyncModule *index.SyncModule `inject:""`
WorkspaceModule *index.WorkspaceModule `inject:""`
TestFilterModule *index.TestFilterModule `inject:""`
TestScriptModule *index.TestScriptModule `inject:""`
......@@ -45,9 +45,9 @@ func (m *IndexModule) Party() module.WebModule {
modules := []module.WebModule{
m.FileModule.Party(),
m.SiteModule.Party(),
m.ZentaoModule.Party(),
m.ConfigModule.Party(),
m.SiteModule.Party(),
m.InterpreterModule.Party(),
m.SyncModule.Party(),
m.WorkspaceModule.Party(),
......
package index
import (
"github.com/aaronchen2k/deeptest/internal/server/core/module"
"github.com/aaronchen2k/deeptest/internal/server/middleware"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/controller"
"github.com/kataras/iris/v12"
)
type InterpreterModule struct {
InterpreterCtrl *controller.InterpreterCtrl `inject:""`
}
func NewInterpreterModule() *InterpreterModule {
return &InterpreterModule{}
}
// Party 执行
func (m *InterpreterModule) Party() module.WebModule {
handler := func(index iris.Party) {
index.Use(middleware.InitCheck())
index.Get("/", m.InterpreterCtrl.List).Name = "列表"
index.Get("/{id:int}", m.InterpreterCtrl.Get).Name = "详情"
index.Post("/", m.InterpreterCtrl.Create).Name = "新建"
index.Put("/{id:int}", m.InterpreterCtrl.Update).Name = "更新"
index.Delete("/{id:int}", m.InterpreterCtrl.Delete).Name = "删除"
}
return module.NewModule("/interpreters", handler)
}
package model
type Interpreter struct {
BaseModel
Lang string `json:"lang"`
Path string `json:"path"`
}
func (Interpreter) TableName() string {
return "biz_interpreter"
}
......@@ -2,7 +2,8 @@ package model
var (
Models = []interface{}{
&Workspace{},
&Interpreter{},
&Site{},
&Workspace{},
}
)
package repo
import (
"errors"
"fmt"
logUtils "github.com/aaronchen2k/deeptest/internal/pkg/lib/log"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/fatih/color"
"go.uber.org/zap"
"gorm.io/gorm"
)
type InterpreterRepo struct {
DB *gorm.DB `inject:""`
}
func NewInterpreterRepo() *InterpreterRepo {
return &InterpreterRepo{}
}
func (r *InterpreterRepo) List() (pos []model.Interpreter, err error) {
db := r.DB.Model(&model.Interpreter{}).Where("NOT deleted")
err = db.Find(&pos).Error
return
}
func (r *InterpreterRepo) Get(id uint) (po model.Interpreter, err error) {
err = r.DB.Model(&model.Interpreter{}).
Where("id = ?", id).
Where("NOT deleted").
First(&po).Error
if err != nil {
logUtils.Errorf(color.RedString("find interpreter by id failed, error: %s.", err.Error()))
return
}
return
}
func (r *InterpreterRepo) Create(interpreter model.Interpreter) (id uint, err error) {
po, err := r.FindDuplicate(interpreter.Lang, 0)
if po.ID != 0 {
return 0, errors.New(fmt.Sprintf("%s运行环境已存在", interpreter.Lang))
}
err = r.DB.Model(&model.Interpreter{}).Create(&interpreter).Error
if err != nil {
logUtils.Errorf(color.RedString("create interpreter failed, error: %s.", err.Error()))
return 0, err
}
id = interpreter.ID
return
}
func (r *InterpreterRepo) Update(interpreter model.Interpreter) error {
po, err := r.FindDuplicate(interpreter.Lang, interpreter.ID)
if po.ID != 0 {
return errors.New(fmt.Sprintf("%s运行环境已存在", interpreter.Lang))
}
err = r.DB.Model(&model.Interpreter{}).Where("id = ?", interpreter.ID).Updates(&interpreter).Error
if err != nil {
logUtils.Errorf(color.RedString("update interpreter failed, error: %s.", err.Error()))
return err
}
return nil
}
func (r *InterpreterRepo) Delete(id uint) (err error) {
err = r.DB.Model(&model.Interpreter{}).Where("id = ?", id).
Updates(map[string]interface{}{"deleted": true}).Error
if err != nil {
logUtils.Errorf("delete interpreter by id error", zap.String("error:", err.Error()))
return
}
return
}
func (r *InterpreterRepo) FindDuplicate(lang string, id uint) (po model.Interpreter, err error) {
db := r.DB.Model(&model.Interpreter{}).
Where("NOT deleted").
Where("lang = ?", lang)
if id != 0 {
db.Where("id != ?", id)
}
err = db.First(&po).Error
return
}
package service
import (
"errors"
"fmt"
fileUtils "github.com/aaronchen2k/deeptest/internal/pkg/lib/file"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/model"
"github.com/aaronchen2k/deeptest/internal/server/modules/v1/repo"
)
type InterpreterService struct {
InterpreterRepo *repo.InterpreterRepo `inject:""`
}
func NewInterpreterService() *InterpreterService {
return &InterpreterService{}
}
func (s *InterpreterService) List() (ret []model.Interpreter, err error) {
ret, err = s.InterpreterRepo.List()
return
}
func (s *InterpreterService) Get(id uint) (site model.Interpreter, err error) {
return s.InterpreterRepo.Get(id)
}
func (s *InterpreterService) Create(site model.Interpreter) (id uint, err error) {
if !fileUtils.FileExist(site.Path) {
err = errors.New(fmt.Sprintf("可执行文件%s不存在", site.Path))
return
}
id, err = s.InterpreterRepo.Create(site)
return
}
func (s *InterpreterService) Update(site model.Interpreter) (err error) {
if !fileUtils.FileExist(site.Path) {
err = errors.New(fmt.Sprintf("可执行文件%s不存在", site.Path))
return
}
err = s.InterpreterRepo.Update(site)
return
}
func (s *InterpreterService) Delete(id uint) error {
return s.InterpreterRepo.Delete(id)
}
......@@ -60,6 +60,7 @@ export default defineComponent({
}
const setEnv = (): void => {
console.log('setEnv')
router.push(`/interpreter/list`)
}
const setLang = (): void => {
console.log('setLang')
......
......@@ -115,7 +115,7 @@ const IndexLayoutRoutes: Array<RoutesDataItem> = [
{
icon: 'empty',
title: 'empty',
title: 'site',
path: '/site',
redirect: '/site/list',
component: BlankLayout,
......@@ -142,6 +142,22 @@ const IndexLayoutRoutes: Array<RoutesDataItem> = [
],
},
{
icon: 'empty',
title: 'interpreter',
path: '/interpreter',
redirect: '/interpreter/list',
component: BlankLayout,
hidden: true,
children: [
{
title: 'interpreter',
path: 'list',
component: () => import('@/views/interpreter/index.vue'),
hidden: true,
},
],
},
];
export default IndexLayoutRoutes;
\ No newline at end of file
......@@ -36,10 +36,11 @@ export default {
'zentao_url': '禅道地址',
'username': '用户名',
'password': '密码',
'create_interpreter': '新建解析器',
'edit_interpreter': '编辑解析器',
'interpreter': '解析器',
'interpreter_path': '解析器路径',
'interpreter': '运行环境',
'create_interpreter': '新建运行环境',
'edit_interpreter': '编辑运行环境',
'interpreter_path': '可执行文件路径',
'script_lang': '脚本语言',
'script': '脚本',
......@@ -160,5 +161,5 @@ export default {
'pls_username': '请输入用户名',
'pls_password': '请输入密码',
'pls_input_lang': '请输入语言',
'pls_input_interpreter_path': '请输入解析器可执行文件路径',
'pls_input_interpreter_path': '请输入可执行文件路径',
};
\ No newline at end of file
import moment from "moment";
import {AutoTestTools, ScriptLanguages, TestTools, BuildTools} from "@/utils/const";
import {Ref} from "vue";
import {Config, Interpreter} from "@/views/config/data";
export function getInterpretersFromConfig(currConfig: any): any {
const interpreters: any[] = []
export function getInterpreters(): any {
const languages: string[] = []
const languageMap = {}
ScriptLanguages.forEach(item => {
const lang = item.toLowerCase()
languages.push(lang)
languageMap[lang] = item
if (currConfig && currConfig[lang] && currConfig[lang].trim() != '') {
interpreters.push({ lang: lang, val: currConfig[lang] })
} else {
languages.push(lang)
}
})
return {interpreters: interpreters, languages: languages, languageMap: languageMap}
}
export function setInterpreter(config: Ref<Config>, interpreters: Ref<Interpreter[]>): Ref<Config> {
interpreters.value.forEach((item, i) => {
config[item.lang] = item.val
})
return config
return {languages: languages, languageMap: languageMap}
}
export function getUnitTestFrameworks(): any {
......
export interface Config {
language: string
url: string;
username: string;
password: string;
javascript: string
lua: string
perl: string
php: string
python: string
ruby: string
tcl: string
autoit: string
version: string
isWin: boolean
}
export interface Interpreter {
lang: string;
val: string;
}
<template>
<div v-if="!currWorkspace.path">
<a-empty :image="simpleImage" :description="t('pls_create_workspace')"/>
</div>
<a-card v-if="currWorkspace.path" :title="t('edit_config')">
<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"
@blur="validate('url', { trigger: 'blur' }).catch(() => {})" placeholder="https://zentao.site.com" />
</a-form-item>
<a-form-item :label="t('username')" v-bind="validateInfos.username">
<a-input v-model:value="model.username"
@blur="validate('username', { trigger: 'blur' }).catch(() => {})" placeholder="" />
</a-form-item>
<a-form-item :label="t('password')" v-bind="validateInfos.password">
<a-input v-model:value="model.password"
@blur="validate('password', { trigger: 'blur' }).catch(() => {})" placeholder="" />
</a-form-item>
<a-form-item v-if="currWorkspace.type === 'func' && currConfigRef.isWin" label="执行器">
<div>
<a-row class="interpreter-header">
<a-col :span="4" class="t-center t-bord">{{t('script_lang')}}</a-col>
<a-col :span="18" class="t-center t-bord">{{t('interpreter')}}</a-col>
<a-col :span="2" class="t-center t-bord">
<a-button @click="addInterpreter" type="link" size="small">{{ t('create') }}</a-button>
</a-col>
</a-row>
<a-row v-for="item in interpreters" :key="item.lang" class="interpreter-item">
<a-col :span="4" class="t-bord">
{{languageMap[item.lang]}}
</a-col>
<a-col :span="18" class="t-bord">
{{item.val}}
</a-col>
<a-col :span="2" class="t-center t-bord">
<icon-svg @click="editInterpreter(item)" type="edit" class="t-icon"></icon-svg> &nbsp;
<icon-svg @click="deleteInterpreter(item)" type="close" class="t-icon"></icon-svg>
</a-col>
</a-row>
</div>
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button type="primary" @click.prevent="submitForm">{{t('save')}}</a-button> &nbsp;
<a-button style="margin-left: 10px" @click="resetFields">{{t('reset')}}</a-button>
</a-form-item>
</a-form>
<create-interpreter-form
v-if="createFormVisible===true"
:visible="createFormVisible"
:onCancel="() => setCreateFormVisible(false)"
:onSubmitLoading="createSubmitLoading"
:onSubmit="createSubmit"
:languages="languages"
:languageMap="languageMap"
/>
<update-interpreter-form
v-if="updateFormVisible===true"
:visible="updateFormVisible"
:model="interpreter"
:onCancel="() => updateFormCancel()"
:onSubmitLoading="updateSubmitLoading"
:onSubmit="updateSubmit"
:languageMap="languageMap"
/>
</a-card>
</template>
<script lang="ts">
import {defineComponent, ref, reactive, computed, watch, ComputedRef, Ref, toRaw, toRef} from "vue";
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";
const useForm = Form.useForm;
import {Config, Interpreter} from './data.d';
import {useStore} from "vuex";
import {WorkspaceData} from "@/store/workspace";
import CreateInterpreterForm from './interpreter/create.vue';
import UpdateInterpreterForm from './interpreter/update.vue';
import {
getInterpretersFromConfig,
setInterpreter,
} from "@/utils/testing";
import IconSvg from "@/components/IconSvg/index";
import throttle from "lodash.throttle";
interface ConfigFormSetupData {
t: (key: string | number) => string;
currWorkspace: ComputedRef;
currConfigRef: Ref
model: Partial<Config>
rules: any
labelCol: any
wrapperCol: any
validate: any
validateInfos: validateInfos
submitForm: () => void;
resetFields: () => void;
languages: Ref
languageMap: Ref
interpreters: Ref<Interpreter[]>
interpreter: Ref<Interpreter>
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({
name: 'ConfigForm',
components: {
IconSvg, CreateInterpreterForm, UpdateInterpreterForm,
},
setup(props): ConfigFormSetupData {
const { t } = useI18n();
const store = useStore<{ workspace: WorkspaceData }>();
const currConfigRef = computed<any>(() => store.state.workspace.currConfig);
const currWorkspace = computed<any>(() => store.state.workspace.currWorkspace);
let interpreter = reactive<any>({} as Interpreter)
let interpreters = ref<any>([] as Interpreter[])
let languages = ref<any>({})
let languageMap = ref<any>({})
const getInterpreters = throttle((currConfig) => {
const data = getInterpretersFromConfig(currConfig)
interpreters.value = data.interpreters
languages.value = data.languages
languageMap.value = data.languageMap
}, 600)
getInterpreters(currConfigRef.value)
let model = reactive<any>(currConfigRef.value);
watch(currConfigRef,()=> {
console.log('watch currConfigRef', currConfigRef)
_.extend(model, currConfigRef.value)
getInterpreters(currConfigRef.value)
}, {deep: true})
const rules = reactive({
url: [
{
required: true,
trigger: 'blur',
validator: async (rule: any, value: string) => {
if (value === '' || !value) {
throw new Error(t('pls_zentao_url'));
}
const regx = /(http?|https):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/i
if (!regx.test(value)) {
throw new Error(t('wrong_url'));
}
}
},
],
username: [
{ required: true, message: t('pls_username'), trigger: 'blur' },
],
password: [
{ required: true, message: t('pls_password'), trigger: 'blur' },
],
});
const { resetFields, validate, validateInfos } = useForm(model, rules);
const submitForm = () => {
validate()
.then(() => {
setInterpreter(model, interpreters)
console.log(model);
store.dispatch('workspace/saveConfig', model).then((json) => {
if (json.code === 0) {
notification.success({
message: t('save_success'),
});
} else {
notification.error({
message: t('save_fail'),
description: json.msg,
});
}
})
})
.catch(err => {
console.log('error', err);
});
};
// 新建解析器
const createFormVisible = ref<boolean>(false);
const setCreateFormVisible = (val: boolean) => {
createFormVisible.value = val;
};
const createSubmitLoading = ref<boolean>(false);
const addInterpreter = () => {
interpreter.value = {}
setCreateFormVisible(true)
}
const createSubmit = async (values: any, resetFields: (newValues?: Props | undefined) => void) => {
// createSubmitLoading.value = true;
currConfigRef.value[values.lang] = values.val
// createSubmitLoading.value = false;
setCreateFormVisible(false)
}
// 更新解析器
const updateFormVisible = ref<boolean>(false);
const setUpdateFormVisible = (val: boolean) => {
updateFormVisible.value = val;
}
const updateFormCancel = () => {
setUpdateFormVisible(false);
}
const updateSubmitLoading = ref<boolean>(false);
const editInterpreter = (item) => {
interpreter.value = item
setUpdateFormVisible(true)
}
const updateSubmit = async (values: any, resetFields: (newValues?: Props | undefined) => void) => {
currConfigRef.value[values.lang] = values.val
setUpdateFormVisible(false)
}
const deleteInterpreter = (item) => {
delete currConfigRef.value[item.lang]
}
return {
t,
labelCol: { span: 4 },
wrapperCol: { span: 14 },
currConfigRef,
currWorkspace,
model,
rules,
resetFields,
validate,
validateInfos,
submitForm,
languages,
languageMap,
interpreters,
interpreter,
setCreateFormVisible,
createSubmitLoading,
createFormVisible,
addInterpreter,
createSubmit,
updateSubmitLoading,
updateFormVisible,
updateFormCancel,
editInterpreter,
deleteInterpreter,
updateSubmit,
simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
}
}
})
</script>
<style lang="less" scoped>
.interpreter-header {
margin: 5px 30px;
padding-bottom: 6px;
border-bottom: 1px solid #f0f0f0;
}
.interpreter-item {
margin: 5px 30px;
}
</style>
<template>
<a-modal
:destroy-on-close="true"
:mask-closable="false"
:title="t('create_interpreter')"
:visible="visible"
:onCancel="onCancel"
width="600px"
>
<template #footer>
<a-button key="submit" type="primary" :loading="onSubmitLoading" @click="onFinish">{{t('save')}}</a-button>
<a-button key="back" @click="onCancel">{{t('cancel')}}</a-button>
</template>
<a-form :labelCol="{ span: 4 }" :wrapper-col="{span:20}">
<a-form-item :label="t('script_lang')" v-bind="validateInfos.lang">
<a-select v-model:value="modelRef.lang">
<a-select-option key="" value="">&nbsp;</a-select-option>
<a-select-option v-for="item in languages" :key="item" :value="item">{{languageMap[item]}}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="t('interpreter_path')" v-bind="validateInfos.value">
<a-input v-model:value="modelRef.val" placeholder="" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts">
import { defineComponent, PropType, reactive, Ref } from "vue";
import { useI18n } from "vue-i18n";
import { Props, validateInfos } from 'ant-design-vue/lib/form/useForm';
import { message, Form } from 'ant-design-vue';
import {Interpreter} from "@/views/config/data";
const useForm = Form.useForm;
interface CreateInterpreterFormSetupData {
t: (key: string | number) => string;
modelRef: Ref<Interpreter>
validateInfos: validateInfos;
onFinish: () => Promise<void>;
}
export default defineComponent({
name: 'CreateInterpreterForm',
props: {
visible: {
type: Boolean,
required: true
},
languages: {
required: true
},
languageMap: {
required: true
},
onCancel: {
type: Function,
required: true
},
onSubmitLoading: {
type: Boolean,
required: true
},
onSubmit: {
type: Function as PropType<(values: any, resetFields: (newValues?: Props | undefined) => void) => void>,
required: true
}
},
components: {
},
setup(props): CreateInterpreterFormSetupData {
const { t } = useI18n();
const modelRef = reactive<any>({lang: '', val: ''} as Interpreter)
const rulesRef = reactive({
lang: [ { required: true, message: t('pls_input_lang') } ],
val: [ { required: true, message: t('pls_input_interpreter_path') } ],
});
const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef);
const onFinish = async () => {
validate()
.then(() => {
props.onSubmit(modelRef, resetFields);
})
}
return {
t,
modelRef,
validateInfos,
onFinish
}
}
})
</script>
\ No newline at end of file
<template>
<a-modal
:destroy-on-close="true"
:mask-closable="false"
:title="t('edit_interpreter')"
:visible="visible"
:onCancel="onCancel"
width="600px"
>
<template #footer>
<a-button key="submit" type="primary" :loading="onSubmitLoading" @click="onFinish">{{t('save')}}</a-button>
<a-button key="back" @click="() => onCancel()">{{t('cancel')}}</a-button>
</template>
<a-form :labelCol="{ span: 4 }" :wrapper-col="{span:20}">
<a-form-item :label="t('script_lang')" v-bind="validateInfos.lang">
{{languageMap[modelRef.lang]}}
</a-form-item>
<a-form-item :label="t('interpreter_path')" v-bind="validateInfos.val">
<a-input v-model:value="modelRef.val" placeholder=""/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts">
import {defineComponent, onMounted, PropType, reactive, Ref} from "vue";
import {useI18n} from "vue-i18n";
import {Props, validateInfos} from 'ant-design-vue/lib/form/useForm';
import {Form, message} from 'ant-design-vue';
import {Interpreter} from "@/views/config/data";
const useForm = Form.useForm;
interface UpdateInterpreterFormSetupData {
t: (key: string | number) => string;
modelRef: Ref<Interpreter>;
validateInfos: validateInfos;
onFinish: () => Promise<void>;
}
export default defineComponent({
name: 'UpdateInterpreterForm',
props: {
visible: {
type: Boolean,
required: true
},
model: {
type: Object as PropType<any>,
required: true
},
languageMap: {
required: true
},
onCancel: {
type: Function,
required: true
},
onSubmitLoading: {
type: Boolean,
required: true
},
onSubmit: {
type: Function as PropType<(values: any, resetFields: (newValues?: Props | undefined) => void) => void>,
required: true
}
},
components: {},
setup(props): UpdateInterpreterFormSetupData {
const {t} = useI18n();
let modelRef = reactive<any>({
lang: props.model.value.lang || '',
val: props.model.value.val || '',
});
const rulesRef = reactive({
lang: [{required: true, message: 'pls_input_lang'}],
val: [{required: true, message: t('pls_input_interpreter_path')}],
});
const {resetFields, validate, validateInfos} = useForm(modelRef, rulesRef);
const onFinish = async () => {
try {
const fieldsValue = await validate<any>();
props.onSubmit(fieldsValue, resetFields);
} catch (error) {
message.warning(t('app.global.form.validatefields.catch'));
}
};
return {
t,
modelRef,
validateInfos,
onFinish
}
}
})
</script>
\ No newline at end of file
export default {
'edit_config': 'Eidt Config',
'zentao_url': 'ZenTao URL',
'username': 'User Name',
'password': 'Password',
'pls_zentao_url': 'Please input ZenTao URL',
'pls_username': 'Please input user name.',
'pls_password': 'Please input password.',
'create_interpreter': 'Create Interpreter',
'edit_interpreter': 'Edit Interpreter',
'interpreter_path': 'Interpreter Path',
'script_lang': 'Script Language',
'pls_input_lang': 'Please input language.',
'pls_input_interpreter_path': 'Please input interpreter path.',
};
import {Interpreter} from "@/views/config/data";
export default {
'edit_config': '修改配置',
'zentao_url': '禅道地址',
'username': '用户名',
'password': '密码',
'pls_zentao_url': '请输入禅道地址',
'pls_username': '请输入用户名',
'pls_password': '请输入密码',
'create_interpreter': '新建解析器',
'edit_interpreter': '编辑解析器',
'interpreter': '解析器',
'interpreter_path': '解析器路径',
'script_lang': '脚本语言',
'pls_input_lang': '请输入语言',
'pls_input_interpreter_path': '请输入解析器可执行文件路径',
};
import {Ref} from "vue";
import {Config, Interpreter} from './data.d';
import {ScriptLanguages} from "@/utils/const";
<template>
<a-form :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item :label="t('script_lang')" v-bind="validateInfos.lang">
<a-select v-model:value="modelRef.lang">
<a-select-option key="" value="">&nbsp;</a-select-option>
<a-select-option v-for="item in languages" :key="item" :value="item">{{ languageMap[item] }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="t('interpreter_path')" v-bind="validateInfos.path">
<a-input-search v-if="isElectron" v-model:value="modelRef.path" @search="selectDir" spellcheck="false"
@blur="validate('path', { trigger: 'blur' }).catch(() => {})">
<template #enterButton>
<a-button>选择</a-button>
</template>
</a-input-search>
<a-input v-if="!isElectron" v-model:value="modelRef.path" spellcheck="false"
@blur="validate('path', { trigger: 'blur' }).catch(() => {})"/>
</a-form-item>
<a-form-item :wrapper-col="{ span: wrapperCol.span, offset: labelCol.span }">
<a-button type="primary" @click.prevent="save">{{ t('save') }}</a-button> &nbsp;
<a-button style="margin-left: 10px" @click="reset">{{ t('reset') }}</a-button>
</a-form-item>
</a-form>
</template>
<script lang="ts">
import {defineComponent, reactive, ref, Ref, PropType} from "vue";
import {useI18n} from "vue-i18n";
import {validateInfos} from 'ant-design-vue/lib/form/useForm';
import {Form} from 'ant-design-vue';
import {saveInterpreter} from "@/views/interpreter/service";
import {getInterpreters} from "@/utils/testing";
const useForm = Form.useForm;
interface EditInterpreterFormSetupData {
t: (key: string | number) => string;
validate: any
validateInfos: validateInfos;
save: () => Promise<void>;
reset: () => Promise<void>;
modelRef: Ref;
languages: Ref<[]>,
languageMap: Ref,
isElectron: Ref<boolean>;
labelCol: any
wrapperCol: any
}
export default defineComponent({
name: 'EditInterpreterForm',
props: {
model: {
type: Object as PropType<any>,
required: true
},
onClose: {
type: Function,
required: true
},
},
components: {},
setup(props): EditInterpreterFormSetupData {
const {t} = useI18n();
const isElectron = ref(!!window.require)
let languages = ref<any>({})
let languageMap = ref<any>({})
const data = getInterpreters()
languages.value = data.languages
languageMap.value = data.languageMap
let modelRef = ref<any>({
id: props.model.value.id,
lang: props.model.value.lang || '',
path: props.model.value.path || '',
});
const rulesRef = reactive({
lang: [{required: true, message: t('pls_input_lang')}],
path: [{required: true, message: t('pls_input_interpreter_path')}],
});
const {resetFields, validate, validateInfos} = useForm(modelRef, rulesRef);
const save = async () => {
validate()
.then(() => {
saveInterpreter(modelRef.value).then((json) => {
if (json.code === 0) {
props.onClose()
}
})
})
}
const reset = async () => {
resetFields()
}
return {
t,
isElectron,
validate,
validateInfos,
modelRef,
save,
reset,
languages,
languageMap,
labelCol: {span: 6},
wrapperCol: {span: 18},
}
}
})
</script>
\ No newline at end of file
<template>
<a-card>
<template #title>
<div class="t-card-toolbar">
<div class="left">
{{t('interpreter')}}
</div>
</div>
</template>
<template #extra>
<a-button type="primary" @click="create()">
<template #icon><PlusCircleOutlined /></template>
{{t('create_interpreter')}}
</a-button>
</template>
<a-table
row-key="id"
:columns="columns"
:data-source="interpreters"
:loading="loading"
:pagination="false"
>
<template #lang="{ record }">
{{languageMap[record.lang]}}
</template>
<template #createdAt="{ record }">
<span v-if="record.createdAt">{{ momentUtc(record.createdAt) }}</span>
</template>
<template #action="{ record }">
<a-button @click="() => edit(record)" type="link" size="small">{{ t('edit') }}</a-button>
<a-button @click="() => remove(record)" type="link" size="small">{{ t('delete') }}
</a-button>
</template>
</a-table>
<a-modal
:title="formTitle"
v-if="formVisible"
:visible="true"
:onCancel="onCancel"
width="600px"
:destroy-on-close="true"
:mask-closable="false"
>
<EditInterpreterForm
:model="interpreter"
:onClose="onSave"
/>
<template #footer><span></span></template>
</a-modal>
</a-card>
</template>
<script lang="ts">
import {defineComponent, ref, reactive, computed, watch, ComputedRef, Ref, toRaw, toRef} from "vue";
import { useI18n } from "vue-i18n";
import { PlusCircleOutlined } from '@ant-design/icons-vue';
import {message, Empty} from 'ant-design-vue';
import EditInterpreterForm from './component/edit.vue';
import {getInterpreters} from "@/utils/testing";
import {listInterpreter, removeInterpreter} from "@/views/interpreter/service";
import {momentUtcDef} from "@/utils/datetime";
interface InterpreterListSetupData {
t: (key: string | number) => string;
columns: Ref<any[]>;
loading: Ref<boolean>;
languageMap: Ref,
interpreters: Ref<[]>
interpreter: Ref
formTitle: ComputedRef<string>
formVisible: Ref<boolean>
setFormVisible: (val) => void;
create: () => void;
edit: (item) => void;
remove: (item) => void;
onSave: () => void;
onCancel: () => void;
simpleImage: any
momentUtc: (tm) => string;
}
export default defineComponent({
name: 'InterpreterList',
components: {
EditInterpreterForm, PlusCircleOutlined,
},
setup(props): InterpreterListSetupData {
const {t, locale} = useI18n();
const momentUtc = momentUtcDef
let languageMap = ref<any>({})
const data = getInterpreters()
languageMap.value = data.languageMap
let interpreters = ref<any>([])
let interpreter = reactive<any>({})
const formTitle = computed(() => {
console.log('interpreter.id', interpreter.id)
return interpreter.value.id ? t('edit_interpreter') : t('create_interpreter')
})
watch(locale, () => {
console.log('watch locale', locale)
setColumns()
}, {deep: true})
const columns = ref([] as any[])
const setColumns = () => {
columns.value = [
{
title: t('no'),
dataIndex: 'index',
width: 80,
customRender: ({text, index}: { text: any; index: number }) => index + 1,
},
{
title: t('lang'),
dataIndex: 'lang',
slots: {customRender: 'lang'},
},
{
title: t('interpreter_path'),
dataIndex: 'path',
},
{
title: t('create_time'),
dataIndex: 'createdAt',
slots: {customRender: 'createdAt'},
},
{
title: t('opt'),
key: 'action',
width: 260,
slots: {customRender: 'action'},
},
]
}
setColumns()
const loading = ref<boolean>(true);
const list = () => {
loading.value = true;
listInterpreter().then((json => {
console.log('---', json)
if (json.code === 0) {
interpreters.value = json.data
}
}))
loading.value = false;
}
list()
const formVisible = ref(false)
const setFormVisible = (val: boolean) => {
formVisible.value = val;
};
const create = () => {
interpreter.value = {}
setFormVisible(true)
}
const edit = (item) => {
interpreter.value = item
setFormVisible(true)
}
const onSave = async () => {
console.log('onSave')
setFormVisible(false)
list()
}
const onCancel = async () => {
console.log('onCancel')
setFormVisible(false)
}
const remove = async (item) => {
await removeInterpreter(item.id)
list()
}
return {
t,
momentUtc,
languageMap,
columns,
loading,
interpreters,
interpreter,
formTitle,
formVisible,
setFormVisible,
create,
edit,
remove,
onSave,
onCancel,
simpleImage: Empty.PRESENTED_IMAGE_SIMPLE,
}
}
})
</script>
<style lang="less" scoped>
.interpreter-header {
margin: 5px 30px;
padding-bottom: 6px;
border-bottom: 1px solid #f0f0f0;
}
.interpreter-item {
margin: 5px 30px;
}
</style>
import {QueryParams} from "@/types/data";
import request from "@/utils/request";
const apiPath = 'interpreters';
export async function listInterpreter(params?: QueryParams): Promise<any> {
return request({
url: `/${apiPath}`,
method: 'get',
params,
});
}
export async function getInterpreter(seq: number): Promise<any> {
return request({
url: `/${apiPath}/${seq}`
});
}
export async function saveInterpreter(params: any): Promise<any> {
return request({
url: `/${apiPath}` + (params.id ? `/${params.id}` : ''),
method: params.id? 'PUT': 'POST',
data: params,
});
}
export async function removeInterpreter(id: number): Promise<any> {
return request({
url: `/${apiPath}/${id}`,
method: 'delete',
});
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册