feat(系统设置): 插件管理联调本地

This commit is contained in:
xinxin.wu 2023-08-08 14:29:46 +08:00 committed by fit2-zhao
parent 35c697674c
commit 4cf99a31bc
8 changed files with 139 additions and 224 deletions

View File

@ -1,2 +1,2 @@
export const GetAllOrgUrl = '/system/organization/list-all';
export const GetAllOrgUrl = '/system/organization/option/all';
export const Other = '';

View File

@ -7,14 +7,16 @@
</a-col>
<a-col :span="5" :offset="9">
<a-select v-model="searchKeys.scene">
<a-option v-for="item of sceneList" :key-="item.value" :value="item.value">{{ item.label }}</a-option>
<a-option v-for="item of sceneList" :key="item.value" :value="item.value">{{ t(item.label) }}</a-option>
</a-select>
</a-col>
<a-col :span="5">
<a-input-search
v-model="searchKeys.name"
:max-length="250"
:placeholder="t('system.plugin.searchPlugin')"
@search="searchHanlder"
@press-enter="searchHanlder"
></a-input-search>
</a-col>
</a-row>
@ -31,7 +33,7 @@
<template #columns>
<a-table-column fixed="left" :title="t('system.plugin.tableColunmName')">
<template #cell="{ record }">
{{ record.name }} <span class="text-[--color-text-4]">({{ record.pluginForms.length }})</span>
{{ record.name }} <span class="text-[--color-text-4]">({{ (record.pluginForms || []).length }})</span>
</template>
</a-table-column>
<a-table-column :title="t('system.plugin.tableColunmDescription')" data-index="description" />
@ -63,11 +65,11 @@
{{ org.name }}
</a-tag>
<a-tag
v-show="record.organizations.length > 2"
v-show="(record.organizations || []).length > 2"
class="mr-[4px] border-[rgb(var(--primary-5))] bg-transparent !text-[rgb(var(--primary-5))]"
bordered
>
+{{ record.organizations.length - 2 }}
+{{ (record.organizations || []).length - 2 }}
</a-tag>
</template>
</a-table-column>
@ -93,16 +95,16 @@
<MsButton v-if="record.enable" @click="disableHandler(record)">{{
t('system.plugin.tableDisable')
}}</MsButton>
<MsButton v-else>{{ t('system.plugin.tableEnable') }}</MsButton>
<MsButton v-else @click="enableHandler(record)">{{ t('system.plugin.tableEnable') }}</MsButton>
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
</template>
</a-table-column>
</template>
<template #expand-icon="{ record, expanded }">
<span v-if="record.pluginForms.length && !expanded" class="collapsebtn"
<span v-if="(record.pluginForms || []).length && !expanded" class="collapsebtn"
><icon-plus :style="{ 'font-size': '12px' }"
/></span>
<span v-else-if="record.pluginForms.length && expanded" class="expand"
<span v-else-if="(record.pluginForms || []).length && expanded" class="expand"
><icon-minus class="text-[rgb(var(--primary-6))]" :style="{ 'font-size': '12px' }"
/></span>
</template>
@ -111,18 +113,29 @@
>{{ t('system.plugin.totalNum') }}<span class="mx-2">{{ totalNum }}</span
>{{ t('system.plugin.dataList') }}</div
>
<UploadModel :visible="uploadVisible" @cancel="uploadVisible = false" @success="okHandler" @brash="loadData()" />
<UpdatePluginModal ref="updateModalRef" v-model:visible="updateVisible" @success="loadData()" />
<scriptDetailDrawer v-model:visible="showDrawer" :value="detailYaml" :config="config" />
<UploadModel
v-model:visible="uploadVisible"
:originize-list="originizeList"
@success="okHandler"
@brash="loadData()"
/>
<UpdatePluginModal
ref="updateModalRef"
v-model:visible="updateVisible"
:originize-list="originizeList"
@success="loadData()"
/>
<scriptDetailDrawer v-model:visible="showDrawer" :value="detailYaml" :config="config" :read-only="true" />
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, reactive, h } from 'vue';
import { ref, onBeforeMount, reactive, h } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import { getPluginList, deletePluginReq, updatePlugin, getScriptDetail } from '@/api/modules/setting/pluginManger';
import { getAllOrgList } from '@/api/modules/setting/orgnization';
import MsButton from '@/components/pure/ms-button/index.vue';
import UploadModel from './uploadModel.vue';
import UpdatePluginModal from './updatePluginModal.vue';
@ -130,9 +143,16 @@
import scriptDetailDrawer from './scriptDetailDrawer.vue';
import { useCommandComponent } from '@/hooks/useCommandComponent';
import useModal from '@/hooks/useModal';
import { Message, TableData } from '@arco-design/web-vue';
import { Message, TableData, SelectOptionData } from '@arco-design/web-vue';
import useVisit from '@/hooks/useVisit';
import type { PluginForms, PluginList, PluginItem, Options, DrawerConfig } from '@/models/setting/plugin';
import type {
PluginForms,
PluginList,
PluginItem,
Options,
DrawerConfig,
UpdatePluginModel,
} from '@/models/setting/plugin';
import dayjs from 'dayjs';
import TableExpand from './tableExpand.vue';
@ -166,15 +186,15 @@
const { openModal } = useModal();
const sceneList = ref([
{
label: '全部',
label: 'system.plugin.all',
value: '',
},
{
label: '接口测试',
label: 'system.plugin.apiTest',
value: 'API',
},
{
label: '项目管理',
label: 'system.plugin.proMangement',
value: 'PLATFORM',
},
]);
@ -189,7 +209,8 @@
try {
const result = await getPluginList();
data.value = result;
totalNum.value = result.length;
filterData.value = result;
totalNum.value = (result || []).length;
} catch (error) {
console.log(error);
data.value = [];
@ -217,10 +238,8 @@
deletePluginReq(record.id);
Message.success(t('system.plugin.deletePluginSuccess'));
loadData();
return true;
} catch (error) {
console.log(error);
return false;
}
},
hideCancel: false,
@ -273,26 +292,41 @@
},
onBeforeOk: async () => {
try {
await updatePlugin({ enable: !record.enable });
const params: UpdatePluginModel = {
id: record.id,
enable: !record.enable,
};
await updatePlugin(params);
Message.success(t('system.plugin.disablePluginSuccess'));
loadData();
return true;
} catch (error) {
console.log(error);
return false;
}
},
hideCancel: false,
});
};
const enableHandler = async (record: PluginItem) => {
try {
const params: UpdatePluginModel = {
id: record.id,
enable: !record.enable,
};
await updatePlugin(params);
Message.success(t('system.plugin.disablePluginSuccess'));
loadData();
} catch (error) {
console.log(error);
}
};
const detailScript = async (record: PluginItem, item: PluginForms) => {
showDrawer.value = true;
config.value = {
pluginId: record.pluginId as string,
pluginId: record.id as string,
title: item.name,
};
try {
const result = await getScriptDetail(record.pluginId as string, item.id);
const result = await getScriptDetail(record.id as string, item.id);
detailYaml.value = result || '';
} catch (error) {
console.log(error);
@ -303,167 +337,26 @@
width: 54,
expandedRowRender: (record: TableData) => {
if (record.pluginForms && record.pluginForms.length > 0) {
return h(TableExpand, { record, onMessageEvent: (recordItem, item) => detailScript(recordItem, item) });
return h(
// @ts-ignore
TableExpand,
{
record,
onMessageEvent: (recordItem: PluginItem, item: PluginForms) => detailScript(recordItem, item),
},
null
);
}
},
});
const handleExpand = (rowKey: string | number) => {
Object.assign(expandedRowKeys, [rowKey]);
};
onMounted(() => {
data.value = [
{
id: 'string1',
name: '插件一',
pluginId: 'string',
fileName: 'string',
createTime: 0,
updateTime: 3084234,
createUser: '创建人',
enable: true,
global: true,
xpack: true,
description: 'string',
scenario: 'API',
pluginForms: [
{
id: '111',
name: '步骤一',
},
{
id: '222',
name: '步骤二',
},
{
id: '333',
name: '步骤三',
},
{
id: '444',
name: '步骤四',
},
{
id: '555',
name: '步骤五',
},
{
id: '666',
name: '步骤六',
},
],
organizations: [
{
id: 'string',
num: 0,
name: 'string',
},
],
},
{
id: 'string2',
name: '插件二',
pluginId: 'string',
fileName: 'string',
createTime: 0,
updateTime: 3084234,
createUser: '创建人',
enable: true,
global: true,
xpack: true,
description: 'string',
scenario: 'PLATFORM',
pluginForms: [],
organizations: [
{
id: 'string',
num: 0,
name: 'string',
},
],
},
{
id: 'string3',
name: '插件3',
pluginId: 'string',
fileName: 'string',
createTime: 0,
updateTime: 3084234,
createUser: '创建人',
enable: true,
global: true,
xpack: true,
description: 'string',
scenario: 'PLATFORM',
pluginForms: [
{
id: '111',
name: '步骤一',
},
],
organizations: [
{
id: 'string',
num: 0,
name: 'string',
},
],
},
{
id: 'string4',
name: '插件4',
pluginId: 'string',
fileName: 'string',
createTime: 0,
updateTime: 3084234,
createUser: '创建人',
enable: true,
global: true,
xpack: true,
description: 'string',
scenario: 'API',
pluginForms: [
{
id: '111',
name: '步骤一',
},
{
id: '222',
name: '步骤二',
},
],
organizations: [
{
id: 'string',
num: 0,
name: 'string',
},
],
},
{
id: 'string5',
name: '插件5',
pluginId: 'string',
fileName: 'string',
createTime: 0,
updateTime: 3084234,
createUser: '创建人',
enable: true,
global: true,
xpack: true,
description: 'string',
scenario: 'PLATFORM',
pluginForms: [],
organizations: [
{
id: 'string',
num: 0,
name: 'string',
},
],
},
];
const originizeList = ref<SelectOptionData>([]);
onBeforeMount(async () => {
loadData();
filterData.value = [...data.value];
originizeList.value = await getAllOrgList();
});
</script>

View File

@ -5,6 +5,7 @@
:mask="false"
:footer="false"
:title="t('system.plugin.showScriptTitle', { name: props.config.title })"
@close="handleClose"
>
<MsCodeEditor
v-model:model-value="jobDefinition"
@ -12,6 +13,7 @@
width="100%"
height="calc(100vh - 155px)"
theme="MS-text"
:read-only="props.readOnly"
/>
</MsDrawer>
</template>
@ -27,6 +29,8 @@
visible: boolean;
value: string;
config: DrawerConfig;
defaultVal?: string | null;
readOnly?: boolean;
}>();
const emit = defineEmits(['update:value', 'update:visible']);
@ -41,6 +45,14 @@
showScriptDrawer.value = val;
}
);
watch(
() => props.value,
(val) => {
if (val) {
jobDefinition.value = val;
}
}
);
watch(
() => showScriptDrawer.value,
@ -48,6 +60,9 @@
emit('update:visible', val);
}
);
function handleClose() {
emit('update:value', jobDefinition.value);
}
</script>
<style lang="less" scoped></style>

View File

@ -1,10 +1,15 @@
<template>
<a-modal v-model:visible="updateVisible" width="680px" title-align="start" class="ms-modal-form ms-modal-medium">
<template #title> {{ t('system.plugin.updateTitle', { name: form.name }) }}</template>
<template #title> {{ t('system.plugin.updateTitle', { name: title }) }}</template>
<div class="form">
<a-form ref="UpdateFormRef" :model="form" layout="vertical">
<a-form-item field="name" :label="t('system.plugin.name')" asterisk-position="end">
<a-input v-model="form.name" :placeholder="t('system.plugin.defaultJarNameTip')" allow-clear />
<a-input
v-model="form.name"
:placeholder="t('system.plugin.defaultJarNameTip')"
:max-length="250"
allow-clear
/>
</a-form-item>
<a-form-item field="global" :label="t('system.plugin.appOrganize')" asterisk-position="end">
<a-radio-group v-model="form.global">
@ -25,7 +30,7 @@
:placeholder="t('system.plugin.selectOriginize')"
allow-clear
>
<a-option v-for="item of originizeList" :key="item.value" :value="item.value">{{ item.label }}</a-option>
<a-option v-for="item of originizeList" :key="item.id" :value="item.id">{{ item.name }}</a-option>
</a-select>
</a-form-item>
<a-form-item field="description" :label="t('system.plugin.description')" asterisk-position="end">
@ -43,8 +48,8 @@
</template>
<script setup lang="ts">
import { ref, watchEffect, watch, nextTick, reactive } from 'vue';
import { FormInstance, Message, ValidatedError } from '@arco-design/web-vue';
import { ref, watchEffect, watch } from 'vue';
import { FormInstance, Message, ValidatedError, SelectOptionData } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n';
import type { UpdatePluginModel, PluginItem } from '@/models/setting/plugin';
import { updatePlugin } from '@/api/modules/setting/pluginManger';
@ -52,6 +57,7 @@
const { t } = useI18n();
const props = defineProps<{
visible: boolean;
originizeList: SelectOptionData;
}>();
const emits = defineEmits<{
(e: 'success'): void;
@ -59,16 +65,7 @@
}>();
const confirmLoading = ref<boolean>(false);
const UpdateFormRef = ref<FormInstance | null>(null);
const originizeList = ref([
{
label: '组织一',
value: '1',
},
{
label: '组织二',
value: '2',
},
]);
const title = ref<string>('');
const form = ref<UpdatePluginModel>({
name: '',
global: '',
@ -91,7 +88,11 @@
updateVisible.value = false;
};
const open = (record: PluginItem) => {
form.value = { ...record };
title.value = record.name as string;
form.value = {
...record,
organizationIds: (record.organizations || []).map((item) => item.id),
};
};
const handleOk = () => {

View File

@ -9,6 +9,7 @@
<a-input
v-model="form.name"
size="small"
:max-length="250"
:placeholder="t('system.plugin.defaultJarNameTip')"
allow-clear
/>
@ -84,10 +85,10 @@
<div>
<a-space>
<a-button type="secondary" @click="handleCancel">{{ t('system.plugin.pluginCancel') }}</a-button>
<a-button type="secondary" :loading="saveLoading" @click="saveAndAddPlugin">{{
<a-button type="secondary" :disabled="isDisabled" :loading="saveLoading" @click="saveAndAddPlugin">{{
t('system.plugin.saveAndAdd')
}}</a-button>
<a-button type="primary" :loading="confirmLoading" @click="saveConfirm">{{
<a-button type="primary" :disabled="isDisabled" :loading="confirmLoading" @click="saveConfirm">{{
t('system.plugin.pluginConfirm')
}}</a-button>
</a-space>
@ -98,23 +99,23 @@
</template>
<script setup lang="ts">
import { ref, watchEffect, onMounted } from 'vue';
import { ref, watchEffect, computed, watch } from 'vue';
import MsUpload from '@/components/pure/ms-upload/index.vue';
import type { FormInstance, ValidatedError, SelectOptionData, FileItem } from '@arco-design/web-vue';
import { addPlugin } from '@/api/modules/setting/pluginManger';
import { Message } from '@arco-design/web-vue';
import { getAllOrgList } from '@/api/modules/setting/orgnization';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const emits = defineEmits<{
(e: 'cancel'): void;
(event: 'update:visible', visible: boolean): void;
(e: 'success'): void;
(e: 'brash'): void;
}>();
const props = defineProps<{
visible: boolean;
originizeList: SelectOptionData;
}>();
const pluginVisible = ref(false);
const fileName = ref<string>('');
@ -129,19 +130,33 @@
enable: true,
global: true,
};
const form = ref({ ...initForm });
const originizeList = ref<SelectOptionData>([]);
watchEffect(() => {
pluginVisible.value = props.visible;
const isDisabled = computed(() => {
return !(fileList.value.length > 0);
});
const form = ref({ ...initForm });
// const originizeList = ref<SelectOptionData>([]);
const resetForm = () => {
form.value = { ...initForm };
fileList.value = [];
};
watchEffect(() => {
pluginVisible.value = props.visible;
});
watch(
() => pluginVisible.value,
(val) => {
emits('update:visible', val);
if (!val) {
resetForm();
}
}
);
const handleCancel = () => {
pluginVisible.value = false;
pluginFormRef.value?.resetFields();
resetForm();
emits('cancel');
};
const confirmHandler = async (flag: string) => {
try {
@ -166,7 +181,6 @@
emits('brash');
} catch (error) {
console.log(error);
return false;
} finally {
confirmLoading.value = false;
saveLoading.value = false;
@ -195,9 +209,9 @@
watchEffect(() => {
fileName.value = fileList.value[0]?.name as string;
});
onMounted(async () => {
originizeList.value = await getAllOrgList();
});
// onBeforeMount(async () => {
// // originizeList.value = await getAllOrgList();
// });
</script>
<style scoped lang="less">

View File

@ -6,7 +6,7 @@
{{ t('system.plugin.alertDescribe') }}
<a class="mx-1" href="javascript:;">{{ t('system.plugin.viewTable') }}</a
>{{ t('system.plugin.downAddress') }}
<a class="mx-1" href="javascript:;">{{ t('system.plugin.goDownload') }} </a>
<a class="mx-1" href="https://github.com/metersphere" target="_blank">{{ t('system.plugin.goDownload') }} </a>
</div>
</a-alert>
<div class="mt-4">

View File

@ -99,4 +99,7 @@ export default {
'system.resourcePool.usePerformance': 'Performance test',
'system.resourcePool.useAPI': 'API test',
'system.resourcePool.useUI': ' UI test',
'system.plugin.all': 'All',
'system.plugin.apiTest': 'API Test',
'system.plugin.proMangement': 'Project Management',
};

View File

@ -31,9 +31,6 @@ export default {
'system.plugin.supportFormat': '只支持JAR格式文件文件大小不超过50M',
'system.plugin.interfaceTestDescribe': '协议类的插件建议选择接口测试',
'system.plugin.projectMangerDescribe': '项目管理平台类建议选择项目管理',
'system.resourcePool.disablePoolConfirm': '确认禁用',
'system.resourcePool.disablePoolCancel': '取消',
'system.resourcePool.disablePoolSuccess': '禁用成功',
'system.plugin.deletePluginTip': '确认删除 `{name}` 这个插件吗?',
'system.plugin.disablePluginTip': '确认禁用 `{name}` 这个插件吗?',
'system.resourcePool.deletePoolContentUsed': '该资源池已被使用,删除后相关测试会立即停止,请谨慎操作!',
@ -83,15 +80,7 @@ export default {
'system.plugin.secneProManger': '项目管理',
'system.plugin.sizeExceedTip': '文件大小超出限制!',
'system.plugin.showScriptTitle': '查看脚本({name})',
'system.resourcePool.detailDesc': '描述',
'system.resourcePool.detailUrl': '当前站点 URL',
'system.resourcePool.detailRange': '可用范围',
'system.resourcePool.detailUse': '用途',
'system.resourcePool.detailMirror': '镜像',
'system.resourcePool.detailJMHeap': 'JMeter HEAP',
'system.resourcePool.detailType': '类型',
'system.resourcePool.detailResources': '已添加资源',
'system.resourcePool.usePerformance': '性能测试',
'system.resourcePool.useAPI': '接口测试',
'system.resourcePool.useUI': ' UI 测试',
'system.plugin.all': '全部',
'system.plugin.apiTest': '接口测试',
'system.plugin.proMangement': '项目管理',
};