feat(系统设置): 服务集成本地联调和插件&服务集成联动调试

This commit is contained in:
xinxin.wu 2023-08-18 10:42:23 +08:00 committed by 刘瑞斌
parent 6bd1f650b0
commit cab07f4836
11 changed files with 369 additions and 264 deletions

View File

@ -30,8 +30,8 @@ export function getValidate(id: string) {
return MSR.get({ url: GetValidateServiceUrl, params: id });
}
// 内部校验测试连接 注:不同的平台对应的不同的字段
export function postValidate(data: any) {
return MSR.post({ url: PostValidateServiceUrl, data });
export function postValidate(data: any, pluginId: string) {
return MSR.post({ url: PostValidateServiceUrl, data, params: pluginId });
}
// 前端配置脚本
export function configScript(pluginId: string) {

View File

@ -3,6 +3,6 @@ export const AddServiceUrl = '/service/integration/add';
export const UpdateServiceUrl = '/service/integration/update';
export const ResetServiceUrl = '/service/integration/delete';
export const GetValidateServiceUrl = '/service/integration/validate';
export const PostValidateServiceUrl = '/service/integration/validate';
export const PostValidateServiceUrl = '/service/integration/validate/';
export const ConfigServiceScriptUrl = '/service/integration/script/';
export const getLogoUrl = '/plugin/image';

View File

@ -1,4 +1,4 @@
export interface ServiceItem {
export type ServiceItem = Partial<{
id?: string;
pluginId: string; // 插件id
title: string;
@ -8,16 +8,31 @@ export interface ServiceItem {
logo: string;
organizationId: string; // 组织id
configuration?: null; // 配置项
}
}>;
export type ServiceList = ServiceItem[];
// 创建和更新服务
export interface AddOrUpdateServiceModel {
export type AddOrUpdateServiceModel = Partial<{
id?: string;
pluginId: string;
enable: boolean;
enable: boolean | undefined;
organizationId: string;
configuration: any;
configuration?: any;
}>;
export interface SkipTitle {
name: string;
src: string;
active: boolean; // 是否激活
}
export interface StepListType {
id: string;
icon: string;
title: string;
skipTitle: SkipTitle[];
step: string;
description: string;
}

View File

@ -6,14 +6,14 @@
:ok-text="t('organization.member.Confirm')"
:cancel-text="t('organization.member.Cancel')"
>
<template #title> 标题 </template>
<template #title> {{ title }} </template>
<div>
<MsFormCreate v-model:api="fApi" :rule="formRules" :option="options" />
</div>
<template #footer>
<div class="flex justify-between">
<div class="flex flex-row items-center justify-center">
<a-switch size="small" />
<a-switch v-model="isEnable" :disabled="isDisabled" size="small" />
<a-tooltip>
<template #content>
<div class="text-sm">{{ t('organization.service.statusEnableTip') }}</div>
@ -24,8 +24,12 @@
</div>
<a-space>
<a-button type="secondary" @click="handleCancel">{{ t('organization.service.Cancel') }}</a-button>
<a-button type="outline">{{ t('organization.service.testLink') }}</a-button>
<a-button type="primary" @click="saveHandler">{{ t('organization.service.Confirm') }}</a-button>
<a-button type="outline" :loading="testLoading" @click="testLink">{{
t('organization.service.testLink')
}}</a-button>
<a-button type="primary" :loading="loading" @click="saveHandler">{{
t('organization.service.Confirm')
}}</a-button>
</a-space>
</div>
</template>
@ -36,50 +40,28 @@
import { ref, watchEffect, watch } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import { configScript, addOrUpdate, postValidate } from '@/api/modules/setting/serviceIntegration';
import { Message } from '@arco-design/web-vue';
import type { ServiceItem, AddOrUpdateServiceModel } from '@/models/setting/serviceIntegration';
import useLoading from '@/hooks/useLoading';
import { useUserStore } from '@/store';
const { t } = useI18n();
const userStore = useUserStore();
const lastOrganizationId = userStore.$state?.lastOrganizationId as string;
const emits = defineEmits<{
(event: 'update:visible', visible: boolean): void;
(event: 'success'): void;
}>();
const props = defineProps<{
visible: boolean;
rule: any;
}>();
const detailVisible = ref<boolean>(false);
const { loading, setLoading } = useLoading(false);
const fApi = ref<any>({});
const formRules = ref([]);
watchEffect(() => {
detailVisible.value = props.visible;
formRules.value = props.rule;
});
watch(
() => detailVisible.value,
(val) => {
emits('update:visible', val);
}
);
const handleCancel = () => {
detailVisible.value = false;
};
const submit = () => {
fApi.value?.submit((formData: FormData) => {
console.log(formData, 'formData');
});
};
const saveHandler = () => {
fApi.value?.validate((valid: any, fail: any) => {
if (valid) {
submit();
} else {
console.log(fail);
}
});
};
const options = ref({
resetBtn: false,
submitBtn: false,
@ -93,8 +75,118 @@
},
wrap: {
'asterisk-position': 'end',
'validate-trigger': ['change'],
},
});
const formRules = ref<any>([]);
const title = ref<string>('');
const formItem = ref<any>({});
watchEffect(() => {
detailVisible.value = props.visible;
});
watch(
() => detailVisible.value,
(val) => {
emits('update:visible', val);
}
);
const testLoading = ref<boolean>(false);
//
const isDisabled = ref<boolean>(false);
//
const isConfigOrigin = ref<boolean | undefined>(false);
//
const isEnable = ref<boolean | undefined>(false);
// id
const pluginId = ref<string>('');
const type = ref<string>('');
const handleCancel = () => {
fApi.value.clearValidateState();
detailVisible.value = false;
};
const submit = () => {
fApi.value?.submit(async (formData: FormData) => {
setLoading(true);
try {
const params: AddOrUpdateServiceModel = {
id: type.value === 'edit' ? formItem.value.id : undefined,
pluginId: pluginId.value,
enable: isEnable.value,
organizationId: lastOrganizationId,
configuration: { ...formData },
};
const message =
type.value === 'edit' ? t('organization.service.updateSuccess') : t('organization.service.configSuccess');
await addOrUpdate(params, type.value);
Message.success(message);
handleCancel();
emits('success');
} catch (error) {
console.log(error);
} finally {
setLoading(false);
}
});
};
const saveHandler = () => {
fApi.value?.validate((valid: any, fail: any) => {
if (valid) {
submit();
} else {
console.log(fail);
}
});
};
//
const getPluginScript = async (cuurentPluginId: string) => {
try {
const result = await configScript(cuurentPluginId);
formRules.value = [...result];
if (type.value === 'edit') {
fApi.value.nextTick(() => {
fApi.value.setValue({ ...formItem.value.configuration });
fApi.value.refresh();
});
}
} catch (error) {
console.log(error);
}
};
//
const testLink = async () => {
testLoading.value = true;
try {
const formValue = {
...fApi.value.formData(),
};
await postValidate(formValue, pluginId.value);
if (!isConfigOrigin.value) isDisabled.value = false;
Message.success(t('organization.service.successMessage'));
} catch (error) {
console.log(error);
} finally {
testLoading.value = false;
}
};
// &
const addOrEdit = (serviceItem: ServiceItem) => {
type.value = serviceItem.config ? 'edit' : 'create';
isConfigOrigin.value = serviceItem.config;
isEnable.value = serviceItem.enable;
isDisabled.value = !serviceItem.config;
pluginId.value = serviceItem.pluginId as string;
formItem.value = { ...serviceItem };
getPluginScript(pluginId.value);
};
defineExpose({
addOrEdit,
title,
});
</script>
<style scoped></style>

View File

@ -1,14 +1,15 @@
<template>
<MsCard simple class="mb-[16px]" auto-height>
<MsCard simple class="mb-[16px]" auto-height :loading="loading">
<div class="outer-wrapper">
<div class="mb-[16px] flex justify-between">
<div class="font-medium text-[var(--color-text-000)]">{{ t('organization.service.serviceIntegration') }}</div>
<div class="font-medium text-[var(--color-text-000)]">{{ t('organization.service.integrationList') }}</div>
<div>
<a-input-search
v-model="keyword"
:placeholder="t('organization.member.searchMember')"
class="w-[230px]"
:placeholder="t('organization.service.searchService')"
:max-length="250"
@search="searchHandler"
@press-enter="searchHandler"
/>
</div>
</div>
@ -16,17 +17,19 @@
<a-scrollbar
:style="{
overflow: 'auto',
height: `calc(100vh - ${collapseHeight} - 220px)`,
height: `calc(100vh - ${collapseHeight} - 230px)`,
}"
>
<div class="list">
<div v-for="item of data" :key="item.id" class="item">
<div v-for="item of filterList" :key="item.id" class="item">
<div class="flex">
<span class="icon float-left mr-2 h-[40px] w-[40px]">{{ item.name }}</span>
<span class="icon float-left mr-2 h-[40px] w-[40px] rounded">
<img class="rounded" :src="`http://172.16.200.18:8081${item.logo}`" alt="log" />
</span>
<div class="flex flex-col justify-start">
<p>
<span class="mr-4 font-semibold">TAPD</span>
<span v-if="!item.isConfig" class="ms-enable">{{ t('organization.service.unconfigured') }}</span>
<span class="mr-4 font-semibold">{{ item.title }}</span>
<span v-if="!item.config" class="ms-enable">{{ t('organization.service.unconfigured') }}</span>
<span
v-else
class="ms-enable active"
@ -37,30 +40,37 @@
>{{ t('organization.service.configured') }}</span
>
</p>
<p class="mt-2 text-sm text-[var(--color-text-4)]">一站式敏捷研发协作云平台</p>
<p class="mt-2 text-sm text-[var(--color-text-4)]">{{ item.description }}</p>
</div>
</div>
<div class="flex justify-between">
<a-space>
<a-tooltip v-if="!item.isConfig" :content="t('organization.service.unconfiguredTip')" position="tl">
<a-tooltip v-if="!item.config" :content="t('organization.service.unconfiguredTip')" position="tl">
<span>
<a-button
v-if="!item.config"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
:disabled="!item.isConfig"
:disabled="!item.config"
@click="getValidateHandler(item)"
>{{ t('organization.service.testLink') }}</a-button
></span
>
</a-tooltip>
<a-button v-else type="outline" class="arco-btn-outline--secondary" size="mini">{{
t('organization.service.testLink')
}}</a-button>
<a-button
v-else
type="outline"
class="arco-btn-outline--secondary"
size="mini"
@click="getValidateHandler(item)"
>{{ t('organization.service.testLink') }}</a-button
>
<a-button type="outline" class="arco-btn-outline--secondary" size="mini" @click="editHandler(item)">{{
t('organization.service.edit')
}}</a-button>
<a-button
v-if="item.isConfig"
v-if="item.config"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
@ -69,10 +79,16 @@
>
</a-space>
<span>
<a-tooltip v-if="!item.isConfig" :content="t('organization.service.unconfiguredTip')" position="br">
<span><a-switch size="small" :disabled="true" /></span>
<a-tooltip v-if="!item.config" :content="t('organization.service.unconfiguredTip')" position="br">
<span
><a-switch
v-model="item.enable"
size="small"
:disabled="true"
@change="(v) => changeStatus(v, item.id)"
/></span>
</a-tooltip>
<a-switch v-else size="small" />
<a-switch v-else v-model="item.enable" size="small" @change="(v) => changeStatus(v, item.id)" />
</span>
</div>
</div>
@ -81,163 +97,118 @@
</div>
</div>
</MsCard>
<ConfigModal v-model:visible="serviceVisible" :rule="createRules" />
<ConfigModal ref="ConfigRef" v-model:visible="serviceVisible" @success="loadList()" />
</template>
<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue';
import { ref, onBeforeMount } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsCard from '@/components/pure/ms-card/index.vue';
import { getServiceList, getValidate, resetService, addOrUpdate } from '@/api/modules/setting/serviceIntegration';
import ConfigModal from './conifgModal.vue';
import { useUserStore } from '@/store';
import useModal from '@/hooks/useModal';
import Message from '@arco-design/web-vue/es/message';
import { characterLimit } from '@/utils';
import type { ServiceList, ServiceItem } from '@/models/setting/serviceIntegration';
const { t } = useI18n();
const { openModal } = useModal();
const props = defineProps<{
const userStore = useUserStore();
const lastOrganizationId = userStore.$state?.lastOrganizationId as string;
defineProps<{
collapseHeight: string;
}>();
const keyword = ref('');
const data = ref([
{
id: '1-1-1-1',
name: 'xxx',
enable: true,
isConfig: false,
},
{
id: '2-2-2-2',
name: 'xxx',
enable: false,
isConfig: true,
},
{
id: '3-3-3-3',
name: 'xxx',
enable: false,
},
{
id: 'xxxx',
name: 'xxx',
enable: false,
},
{
id: 'xxxx',
name: 'xxx',
enable: false,
},
{
id: '3-3-3-3',
name: 'xxx',
enable: false,
},
{
id: '3-3-3-3',
name: 'xxx',
enable: false,
},
{
id: 'xxxx',
name: 'xxx',
enable: false,
},
{
id: 'xxxx',
name: 'xxx',
enable: false,
},
{
id: '3-3-3-3',
name: 'xxx',
enable: false,
},
{
id: '3-3-3-3',
name: 'xxx',
enable: false,
},
]);
const filterList = ref<ServiceList>([]);
const data = ref<ServiceList>([]);
const loading = ref<boolean>(false);
//
const loadList = async () => {
loading.value = true;
try {
const result = await getServiceList(lastOrganizationId);
data.value = result;
filterList.value = result;
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
};
const searchHandler = () => {
filterList.value = data.value.filter((item) => item.title?.includes(keyword.value));
};
const serviceVisible = ref<boolean>(false);
let createRules = reactive([]);
const ConfigRef = ref();
const editHandler = (item: any) => {
//
const editHandler = (serviceItem: ServiceItem) => {
serviceVisible.value = true;
ConfigRef.value.addOrEdit(serviceItem);
ConfigRef.value.title = serviceItem.title;
};
//
const resetHandler = async (serviceItem: ServiceItem) => {
openModal({
type: 'error',
title: t('organization.service.resetServiceTip', { name: characterLimit(serviceItem.title) }),
content: t('organization.service.resetServiceContentTip'),
okText: t('organization.service.confirmReset'),
cancelText: t('organization.service.Cancel'),
okButtonProps: {
status: 'normal',
},
onBeforeOk: async () => {
try {
await resetService(serviceItem.id as string);
Message.success(t('organization.service.resetConfigTip'));
loadList();
} catch (error) {
console.log(error);
}
},
hideCancel: false,
});
};
const resetHandler = (item: any) => {};
onMounted(() => {
setTimeout(() => {
const result = JSON.stringify([
{
type: 'input',
field: 'address',
title: 'JIRA地址',
value: 'JIRA地址',
validate: [{ type: 'string', required: true, message: 'JIRA地址不能为空' }],
},
{
type: 'radio',
title: '认证方式',
field: 'method',
value: '1',
options: [
{
value: '1',
label: 'Auth',
},
{
value: '2',
label: 'Token',
},
],
props: {
type: 'button',
},
wrap: {
tooltip: 'info提示',
},
control: [
{
value: '1',
rule: [
{
type: 'input',
field: 'info',
title: 'JIRA-账号',
value: '账号',
validate: [{ type: 'string', required: true, message: 'JIRA账号不能为空' }],
wrap: {},
},
{
type: 'PassWord',
value: '',
field: 'password',
title: 'JIRA密码',
validate: [{ type: 'string', required: true, message: 'JIRA密码不能为空' }],
props: {
placeholder: '请输入密码',
},
},
],
},
{
value: '2',
rule: [
{
type: 'input',
field: 'token',
title: 'Token',
value: 'token 账号',
validate: [{ type: 'string', required: true, message: 'JIRA账号不能为空' }],
wrap: {},
},
],
},
],
},
]);
createRules = JSON.parse(result);
}, 1000);
//
const getValidateHandler = async (serviceItem: ServiceItem) => {
loading.value = true;
try {
await getValidate(serviceItem.id as string);
Message.success(t('organization.service.testLinkStatusTip'));
loadList();
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
};
//
const changeStatus = async (value: string | number | boolean, id: string | undefined) => {
loading.value = true;
const message = value ? 'organization.service.enableSuccess' : 'organization.service.closeSuccess';
try {
await addOrUpdate({ enable: value as boolean, id }, 'edit');
Message.success(t(message));
loadList();
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
};
onBeforeMount(() => {
loadList();
});
</script>
@ -245,7 +216,6 @@
.ms-card-wrap {
overflow: hidden;
padding: 8px;
min-width: 1150px;
height: calc(100% - 58px) !important;
min-height: 300px;
border-radius: var(--border-radius-small);
@ -262,7 +232,6 @@
height: 144px;
border-radius: 4px;
background: white;
flex-basis: calc(25% - 16px);
@apply flex flex-col justify-between;
.icon {
border: 1px solid var(--color-text-n9);
@ -273,27 +242,20 @@
@apply px-2 py-1 text-xs;
}
}
@media screen and (max-width: 992px) {
.item {
flex-basis: calc(50% - 16px);
}
}
@media screen and (min-width: 800px) {
.item {
flex-basis: calc(50% - 16px);
}
@media screen and (min-width: 1000px) and (max-width: 1440px) {
.item {
flex-basis: calc(33.3% - 16px);
}
}
@media screen and (min-width: 1160px) {
.item {
flex-basis: calc(33.3% - 16px);
}
@media screen and (max-width: 1600px) and (min-width: 1800px) {
.item {
flex-basis: calc(25% - 16px);
}
}
@media screen and (min-width: 1800px) {
.item {
flex-basis: calc(25% - 16px);
}
}
@media screen and (min-width: 1800px) {
.item {
flex-basis: calc(25% - 16px);
}
}
}

View File

@ -4,17 +4,26 @@
<a-collapse :bordered="false" expand-icon-position="right" @change="changeHandler">
<a-collapse-item key="1" class="font-medium" :header="t('organization.service.headerTip')">
<template #expand-icon="{ active }">
<span v-if="active" class="text-[rgb(var(--primary-6))]">{{ t('organization.service.packUp') }}</span>
<span v-else class="text-[rgb(var(--primary-6))]">{{ t('organization.service.expand') }}</span>
<span v-if="active" class="float-right -mr-4 text-[rgb(var(--primary-6))]">{{
t('organization.service.packUp')
}}</span>
<span v-else class="float-right -mr-4 text-[rgb(var(--primary-6))]">{{
t('organization.service.expand')
}}</span>
</template>
<div class="flex w-[100%] flex-row justify-between text-sm font-normal">
<div v-for="(item, index) in cardContent" :key="item.id" class="item" :class="`ms-item-${index}`">
<span>
<div
v-for="(item, index) in cardContent"
:key="item.id"
class="item mt-4 p-[16px]"
:class="`ms-item-${index}`"
>
<span class="mr-3">
<svg-icon width="64px" height="46px" :name="item.icon" />
</span>
<div class="flex h-[100%] flex-1 flex-col justify-between p-4">
<div class="flex justify-between">
<span class="leading-6">{{ t(item.title) }}</span>
<div class="flex h-[100%] flex-1 flex-col justify-between">
<div class="flex items-center justify-between">
<span class="font-normal">{{ t(item.title) }}</span>
<span>
<a-button
v-for="links of item.skipTitle"
@ -24,13 +33,14 @@
type="text"
:href="links.src"
target="_blank"
@click="jumpHandler(links)"
>
{{ t(links.name) }}
</a-button>
</span>
</div>
<div class="text-xs text-[var(--color-text-4)]">
{{ t(item.description) }}
<div class="one-line-text w-[400px]"> {{ t(item.description) }}</div>
</div>
</div>
</div>
@ -47,9 +57,12 @@
import { useI18n } from '@/hooks/useI18n';
import MsCard from '@/components/pure/ms-card/index.vue';
import ServiceList from './components/serviceList.vue';
import { useRouter } from 'vue-router';
import type { StepListType, SkipTitle } from '@/models/setting/serviceIntegration';
const { t } = useI18n();
const cardContent = ref([
const router = useRouter();
const cardContent = ref<StepListType[]>([
{
id: '1001',
icon: 'configplugin',
@ -57,11 +70,13 @@
skipTitle: [
{
name: 'organization.service.developmentDoc',
src: '',
src: 'https://github.com/metersphere/metersphere-platform-plugin/wiki/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97',
active: false,
},
{
name: 'organization.service.downPlugin',
src: 'https://github.com/metersphere/metersphere-platform-plugin',
active: false,
},
],
step: '@/assets/images/ms_plugindownload.jpg',
@ -75,6 +90,7 @@
{
name: 'organization.service.jumpPlugin',
src: '',
active: true,
},
],
step: '@/assets/images/ms_configplugin.jpg',
@ -88,6 +104,13 @@
isCollapse.value = activeKey.length > 0;
collapseHeight.value = activeKey.length > 0 ? '158px' : '72px';
};
const jumpHandler = (links: SkipTitle) => {
if (links.active)
router.push({
name: 'settingSystemPluginManger',
});
};
</script>
<style scoped lang="less">
@ -99,9 +122,10 @@
}
.item {
width: calc(50% - 10px);
height: 78px;
border: 1px solid #ffffff;
box-shadow: 0 0 7px rgb(120 56 135 / 10%);
@apply flex h-20 items-center rounded-md;
@apply flex items-center rounded-md;
}
.ms-item-0 {
background: url('@/assets/images/ms_plugindownload.jpg') no-repeat center / cover;

View File

@ -8,15 +8,15 @@ export default {
'organization.service.integrationList': 'Integration of List',
'organization.service.packUp': 'Pack Up',
'organization.service.expand': 'Expand',
'organization.service.downloadPluginOrDev': 'Download plug-ins or develop plug-ins',
'organization.service.downloadPluginOrDev': 'Download & develop',
'organization.service.configPlugin': 'Configuring the plugin',
'organization.service.downPlugin': 'Download the plugin',
'organization.service.developmentDoc': 'Plug-in development documentation',
'organization.service.downPlugin': 'Download',
'organization.service.developmentDoc': 'development documentation',
'organization.service.description':
'Download third-party project management platform plug-ins that need to be integrated; you can also develop your own relevant project management platform plug-ins',
'organization.service.configDescription':
'Downloaded or developed plug-ins need to be uploaded to plug-in management, upload, you can configure the current page',
'organization.service.jumpPlugin': 'JUMP to plug-in management',
'organization.service.jumpPlugin': 'Jump to plugin',
'organization.service.testLink': 'Test link',
'organization.service.testLinkStatusTip': 'Test connection successful!',
'organization.service.unconfigured': 'Unconfigured',
@ -35,21 +35,19 @@ export default {
'organization.service.tableColunmUsergroup': 'UserGroup',
'organization.service.tableColunmStatus': 'Status',
'organization.service.tableColunmActions': 'Actions',
'organization.service.service': 'Member',
'organization.service.selectMemberScope': 'Select the service you want to add. Multiple selection is supported',
'organization.service.selectProjectScope': 'Select the project you want to add. Multiple selection is supported',
'organization.service.selectMemberEmptyTip': 'The service can not be empty',
'organization.service.selectProjectEmptyTip': 'The project can not be empty',
'organization.service.selectUserEmptyTip': 'The user group can not be empty',
'organization.service.Confirm': 'Confirm',
'organization.service.Cancel': 'Cancel',
'organization.service.deleteMemberTip': 'Are you sure to remove the user `{name}` ?',
'system.user.deleteUserTip': 'Are you sure to delete the user `{name}` ?',
'system.user.deleteUserTip': 'Are you sure to delete the user {name} ?',
'organization.service.deleteMemberConfirm': 'Delete',
'organization.service.deleteMemberCancel': 'Cancel',
'organization.service.deleteMemberSuccess': 'Delete successful',
'organization.service.batchModalSuccess': 'Successfully added',
'organization.service.batchUpdateSuccess': 'Successfully updated',
'organization.service.project': 'Project',
'organization.service.selectUserScope': 'Please select a user group for the above members',
'organization.service.confirmReset': 'Reset',
'organization.service.resetServiceTip': 'Confirm reset {name} this service integration?',
'organization.service.resetServiceContentTip':
'After the Reset, the integration information will be cleared, the project can not integrate with the platform and the platform default template is not available, be careful!',
'organization.service.searchService': 'Search by plug-in name',
'organization.service.successMessage': 'The test connection was successful',
'organization.service.enableSuccess': 'Enable successfully',
'organization.service.closeSuccess': 'Disable successfully',
'organization.service.configSuccess': 'Configuration successfully',
'organization.service.updateSuccess': 'Update successfully',
};

View File

@ -31,20 +31,18 @@ export default {
'organization.service.tableColunmUsergroup': '用户组',
'organization.service.tableColunmStatus': '状态',
'organization.service.tableColunmActions': '操作',
'organization.service.service': '成员',
'organization.service.selectMemberScope': '请选择需要添加的成员支持多选',
'organization.service.selectProjectScope': '请选择需要添加的项目支持多选',
'organization.service.selectMemberEmptyTip': '成员不能为空',
'organization.service.selectProjectEmptyTip': '项目不能为空',
'organization.service.selectUserEmptyTip': '用户组不能为空',
'organization.service.Confirm': '确定',
'organization.service.Cancel': '取消',
'organization.service.deleteMemberTip': '确认移除 `{name}` 这个成员吗?',
'organization.service.deleteMemberConfirm': '确认删除',
'organization.service.deleteMemberCancel': '取消',
'organization.service.deleteMemberSuccess': '删除成功',
'organization.service.batchModalSuccess': '添加成功',
'organization.service.batchUpdateSuccess': '更新成功',
'organization.service.project': '项目',
'organization.service.selectUserScope': '请为以上成员选择用户组',
'organization.service.confirmReset': '确认重置',
'organization.service.resetServiceTip': '确认重置 {name} 这个服务集成吗?',
'organization.service.resetServiceContentTip':
'重置后,集成信息将被清空,项目无法与该平台集成且该平台默认模版不可用,谨慎操作!',
'organization.service.searchService': '通过插件名称搜索',
'organization.service.successMessage': '测试连接成功',
'organization.service.enableSuccess': '启用成功',
'organization.service.closeSuccess': '禁用成功',
'organization.service.configSuccess': '配置成功',
'organization.service.updateSuccess': '更新成功',
};

View File

@ -32,12 +32,22 @@
@expand="handleExpand"
>
<template #columns>
<a-table-column :width="300" fixed="left" :title="t('system.plugin.tableColumnsName')" :ellipsis="true">
<a-table-column
:width="300"
fixed="left"
:title="t('system.plugin.tableColumnsName')"
:ellipsis="true"
:tooltip="true"
>
<template #cell="{ record }">
{{ record.name }} <span class="text-[--color-text-4]">({{ (record.pluginForms || []).length }})</span>
</template>
</a-table-column>
<a-table-column :title="t('system.plugin.tableColumnsDescription')" data-index="description" />
<a-table-column
:title="t('system.plugin.tableColumnsDescription')"
data-index="description"
:ellipsis="true"
/>
<a-table-column :title="t('system.plugin.tableColumnsStatus')">
<template #cell="{ record }">
<div v-if="record.enable" class="flex items-center">

View File

@ -63,6 +63,8 @@
<MsUpload
v-model:file-list="fileList"
accept="jar"
:max-size="50"
size-unit="MB"
main-text="system.user.importModalDragtext"
:sub-text="t('system.plugin.supportFormat')"
:show-file-list="false"

View File

@ -22,7 +22,9 @@
<div>
<a-space>
<a-button type="primary" @click="continueAdd">{{ t('system.plugin.continueUpload') }}</a-button>
<a-button type="outline">{{ t('system.plugin.ServiceIntegration') }}</a-button>
<a-button type="outline" @click="router.push({ name: 'settingOrganizationService' })">{{
t('system.plugin.ServiceIntegration')
}}</a-button>
<a-button type="secondary">{{ t('system.plugin.backPluginList') }}</a-button>
</a-space>
</div>
@ -38,8 +40,10 @@
import { useDialog } from '@/hooks/useDialog';
import useVisit from '@/hooks/useVisit';
import { useI18n } from '@/hooks/useI18n';
import { useRouter } from 'vue-router';
const { t } = useI18n();
const router = useRouter();
const visitedKey = 'doNotShowAgain';
const { addVisited } = useVisit(visitedKey);
const props = defineProps<{