feat(系统管理): 权限&修改bug&补充xpack

This commit is contained in:
xinxin.wu 2024-01-25 20:08:04 +08:00 committed by Craftsman
parent 43f8bfd268
commit 379f46c795
40 changed files with 381 additions and 91 deletions

View File

@ -27,6 +27,7 @@ import {
DetailCaseUrl, DetailCaseUrl,
DownloadExcelTemplateUrl, DownloadExcelTemplateUrl,
DownloadFileUrl, DownloadFileUrl,
dragSortUrl,
EditorUploadFileUrl, EditorUploadFileUrl,
exportExcelCheckUrl, exportExcelCheckUrl,
FollowerCaseUrl, FollowerCaseUrl,
@ -83,6 +84,7 @@ import type {
DeleteCaseType, DeleteCaseType,
DemandItem, DemandItem,
DetailCase, DetailCase,
DragCase,
ImportExcelType, ImportExcelType,
ModulesTreeType, ModulesTreeType,
OperationFile, OperationFile,
@ -401,4 +403,9 @@ export function importExcelCase(data: { request: ImportExcelType; fileList: File
return MSR.uploadFile({ url: importExcelCaseUrl }, { request: data.request, fileList: data.fileList }, ''); return MSR.uploadFile({ url: importExcelCaseUrl }, { request: data.request, fileList: data.fileList }, '');
} }
// 拖拽排序
export function dragSort(data: DragCase) {
return MSR.post({ url: dragSortUrl, data });
}
export default {}; export default {};

View File

@ -143,3 +143,5 @@ export const PreviewEditorImageUrl = '/attachment/download/file';
export const exportExcelCheckUrl = '/functional/case/pre-check/excel'; export const exportExcelCheckUrl = '/functional/case/pre-check/excel';
// 导入excel文件 // 导入excel文件
export const importExcelCaseUrl = '/functional/case/import/excel'; export const importExcelCaseUrl = '/functional/case/import/excel';
// 用例拖拽排序
export const dragSortUrl = '/functional/case/edit/pos';

View File

@ -313,6 +313,7 @@
showTooltip: true, showTooltip: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
}, },
@ -321,6 +322,7 @@
dataIndex: 'name', dataIndex: 'name',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
width: 300, width: 300,

View File

@ -188,6 +188,7 @@
showTooltip: true, showTooltip: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
}, },
@ -196,6 +197,7 @@
dataIndex: 'name', dataIndex: 'name',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
width: 300, width: 300,
@ -206,6 +208,7 @@
slotName: 'method', slotName: 'method',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
}, },

View File

@ -145,6 +145,7 @@
dataIndex: 'createTime', dataIndex: 'createTime',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showInTable: true, showInTable: true,
width: 300, width: 300,

View File

@ -11,6 +11,7 @@
</div> </div>
<a-tooltip :content="t('ms.personal.maxTip')" position="right" :disabled="apiKeyList.length < 5"> <a-tooltip :content="t('ms.personal.maxTip')" position="right" :disabled="apiKeyList.length < 5">
<a-button <a-button
v-permission="['SYSTEM_PERSONAL_API_KEY:READ+ADD']"
type="outline" type="outline"
class="w-[60px]" class="w-[60px]"
:disabled="apiKeyList.length >= 5" :disabled="apiKeyList.length >= 5"
@ -63,12 +64,18 @@
</div> </div>
<div class="flex items-center justify-between px-[16px]"> <div class="flex items-center justify-between px-[16px]">
<MsTableMoreAction :list="actions" trigger="click" @select="handleMoreActionSelect($event, item)"> <MsTableMoreAction :list="actions" trigger="click" @select="handleMoreActionSelect($event, item)">
<a-button size="mini" type="outline" class="arco-btn-outline--secondary"> <a-button
v-permission="['SYSTEM_PERSONAL_API_KEY:READ+UPDATE']"
size="mini"
type="outline"
class="arco-btn-outline--secondary"
>
{{ t('common.setting') }} {{ t('common.setting') }}
</a-button> </a-button>
</MsTableMoreAction> </MsTableMoreAction>
<a-switch <a-switch
v-model:model-value="item.enable" v-model:model-value="item.enable"
v-permission="['SYSTEM_PERSONAL_API_KEY:READ+UPDATE']"
size="small" size="small"
:before-change="() => handleBeforeEnableChange(item)" :before-change="() => handleBeforeEnableChange(item)"
type="line" type="line"
@ -77,7 +84,9 @@
</div> </div>
<div v-if="apiKeyList.length === 0" class="col-span-2 flex w-full items-center justify-center p-[44px]"> <div v-if="apiKeyList.length === 0" class="col-span-2 flex w-full items-center justify-center p-[44px]">
{{ t('ms.personal.nodata') }} {{ t('ms.personal.nodata') }}
<MsButton type="text" class="ml-[8px]" @click="newApiKey">{{ t('common.new') }}</MsButton> <MsButton v-permission="['SYSTEM_PERSONAL_API_KEY:READ+ADD']" type="text" class="ml-[8px]" @click="newApiKey">{{
t('common.new')
}}</MsButton>
</div> </div>
</a-spin> </a-spin>
</div> </div>

View File

@ -87,6 +87,7 @@
name: 'apiKey', name: 'apiKey',
title: t('ms.personal.apiKey'), title: t('ms.personal.apiKey'),
level: 2, level: 2,
permission: ['SYSTEM_PERSONAL_API_KEY:READ'],
}, },
{ {
name: 'local', name: 'local',

View File

@ -21,8 +21,13 @@
import usePermission from '@/hooks/usePermission'; import usePermission from '@/hooks/usePermission';
import appClientMenus from '@/router/app-menus'; import appClientMenus from '@/router/app-menus';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import { listenerRouteChange } from '@/utils/route-listener'; import { listenerRouteChange } from '@/utils/route-listener';
import { RouteEnum } from '@/enums/routeEnum';
const licenseStore = useLicenseStore();
const copyRouters = cloneDeep(appClientMenus) as RouteRecordRaw[]; const copyRouters = cloneDeep(appClientMenus) as RouteRecordRaw[];
const permission = usePermission(); const permission = usePermission();
const appStore = useAppStore(); const appStore = useAppStore();
@ -79,9 +84,17 @@
(item) => name && item?.name && (name as string).includes(item.name as string) (item) => name && item?.name && (name as string).includes(item.name as string)
); );
} }
appStore.setTopMenus(
currentParent?.children?.filter((item) => permission.accessRouter(item) && item.meta?.isTopMenu) const filterMenuTopRouter = currentParent?.children
); ?.filter((item: any) => permission.accessRouter(item) && item.meta?.isTopMenu)
.filter((item: any) => {
if (item.name === RouteEnum.SETTING_SYSTEM_AUTHORIZED_MANAGEMENT) {
return licenseStore.hasLicense();
}
return true;
});
appStore.setTopMenus(filterMenuTopRouter);
setCurrentTopMenu(name as string); setCurrentTopMenu(name as string);
return; return;
} }

View File

@ -4,7 +4,7 @@
<div v-if="props.title" class="mb-2 font-medium">{{ props.title }}</div> <div v-if="props.title" class="mb-2 font-medium">{{ props.title }}</div>
<div class="menu"> <div class="menu">
<div <div
v-for="(item, index) of props.menuList" v-for="(item, index) of innerMenuList"
:key="item.name" :key="item.name"
class="menu-item px-2" class="menu-item px-2"
:class="{ :class="{
@ -26,6 +26,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { hasAnyPermission } from '@/utils/permission';
const props = defineProps<{ const props = defineProps<{
title?: string; title?: string;
defaultKey?: string; defaultKey?: string;
@ -33,6 +35,7 @@
title: string; title: string;
level: number; level: number;
name: string; name: string;
permission?: string[];
}[]; }[];
activeClass?: string; activeClass?: string;
}>(); }>();
@ -42,6 +45,10 @@
const currentKey = ref(props.defaultKey); const currentKey = ref(props.defaultKey);
const innerMenuList = computed(() => {
return props.menuList.filter((item: any) => hasAnyPermission(item.permission));
});
watch( watch(
() => props.defaultKey, () => props.defaultKey,
(val) => { (val) => {

View File

@ -1,7 +1,7 @@
<template> <template>
<MsCard class="mb-[16px]" :title="props.title" hide-back hide-footer auto-height no-content-padding no-bottom-radius> <MsCard class="mb-[16px]" :title="props.title" hide-back hide-footer auto-height no-content-padding no-bottom-radius>
<a-tabs v-model:active-key="innerTab" class="no-content"> <a-tabs v-model:active-key="innerTab" class="no-content">
<a-tab-pane v-for="item of tabList" :key="item.key" :title="item.title" /> <a-tab-pane v-for="item of permissionTabList" :key="item.key" :title="item.title" />
</a-tabs> </a-tabs>
</MsCard> </MsCard>
</template> </template>
@ -11,15 +11,21 @@
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import { hasAnyPermission } from '@/utils/permission';
const props = defineProps<{ const props = defineProps<{
activeTab: string; activeTab: string;
title?: string; title?: string;
tabList: { key: string; title: string }[]; tabList: { key: string; title: string; permission?: string[] }[];
}>(); }>();
const emit = defineEmits(['update:activeTab']); const emit = defineEmits(['update:activeTab']);
const innerTab = ref(props.activeTab); const innerTab = ref(props.activeTab);
const permissionTabList = computed(() => {
return props.tabList.filter((item: any) => hasAnyPermission(item.permission));
});
watch( watch(
() => props.activeTab, () => props.activeTab,
(val) => { (val) => {

View File

@ -324,3 +324,10 @@ export interface ValidateInfo {
successCount: number; successCount: number;
errorMessages: errorMessagesType[]; errorMessages: errorMessagesType[];
} }
// 拖拽排序
export interface DragCase {
projectId: string;
targetId: string;
moveMode: 'BEFORE' | 'AFTER' | 'APPEND'[]; // 拖拽类型
moveId: string;
}

View File

@ -26,6 +26,7 @@ export interface SkipTitle {
name: string; name: string;
src: string; src: string;
active: boolean; // 是否激活 active: boolean; // 是否激活
disabled: boolean;
} }
export interface StepListType { export interface StepListType {

View File

@ -192,7 +192,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/fileManagement/index.vue'), component: () => import('@/views/project-management/fileManagement/index.vue'),
meta: { meta: {
locale: 'menu.projectManagement.fileManagement', locale: 'menu.projectManagement.fileManagement',
roles: ['*'], roles: ['PROJECT_FILE_MANAGEMENT:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },
@ -203,7 +203,7 @@ const ProjectManagement: AppRouteRecordRaw = {
component: () => import('@/views/project-management/messageManagement/index.vue'), component: () => import('@/views/project-management/messageManagement/index.vue'),
meta: { meta: {
locale: 'menu.projectManagement.messageManagement', locale: 'menu.projectManagement.messageManagement',
roles: ['*'], roles: ['PROJECT_MESSAGE:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },

View File

@ -34,7 +34,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/user/index.vue'), component: () => import('@/views/setting/system/user/index.vue'),
meta: { meta: {
locale: 'menu.settings.system.user', locale: 'menu.settings.system.user',
roles: ['*'], roles: ['SYSTEM_USER:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },
@ -64,7 +64,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/config/index.vue'), component: () => import('@/views/setting/system/config/index.vue'),
meta: { meta: {
locale: 'menu.settings.system.parameter', locale: 'menu.settings.system.parameter',
roles: ['*'], roles: ['SYSTEM_PARAMETER_SETTING_BASE:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },
@ -74,7 +74,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/resourcePool/index.vue'), component: () => import('@/views/setting/system/resourcePool/index.vue'),
meta: { meta: {
locale: 'menu.settings.system.resourcePool', locale: 'menu.settings.system.resourcePool',
roles: ['*'], roles: ['SYSTEM_TEST_RESOURCE_POOL:READ'],
isTopMenu: true, isTopMenu: true,
}, },
}, },
@ -84,7 +84,7 @@ const Setting: AppRouteRecordRaw = {
component: () => import('@/views/setting/system/resourcePool/detail.vue'), component: () => import('@/views/setting/system/resourcePool/detail.vue'),
meta: { meta: {
locale: 'menu.settings.system.resourcePoolDetail', locale: 'menu.settings.system.resourcePoolDetail',
roles: ['*'], roles: ['SYSTEM_TEST_RESOURCE_POOL:READ'],
breadcrumbs: [ breadcrumbs: [
{ {
name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL, name: SettingRouteEnum.SETTING_SYSTEM_RESOURCE_POOL,

View File

@ -24,8 +24,7 @@ const useLicenseStore = defineStore('license', {
if (!result || !result.status || !result.license || !result.license.count) { if (!result || !result.status || !result.license || !result.license.count) {
return; return;
} }
// this.setLicenseStatus(result.status); this.setLicenseStatus(result.status);
this.setLicenseStatus('fail');
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }

View File

@ -107,6 +107,7 @@
dataIndex: 'createUser', dataIndex: 'createUser',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
showDrag: true, showDrag: true,
@ -116,6 +117,7 @@
dataIndex: 'updateUser', dataIndex: 'updateUser',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
showInTable: true, showInTable: true,
@ -125,6 +127,7 @@
dataIndex: 'updateTime', dataIndex: 'updateTime',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
showInTable: true, showInTable: true,

View File

@ -4,6 +4,7 @@
v-model:visible="showDrawerVisible" v-model:visible="showDrawerVisible"
:width="1200" :width="1200"
:footer="false" :footer="false"
:mask="false"
:title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.id, name: detailInfo?.name })" :title="t('caseManagement.featureCase.caseDetailTitle', { id: detailInfo?.id, name: detailInfo?.name })"
:detail-id="props.detailId" :detail-id="props.detailId"
:detail-index="props.detailIndex" :detail-index="props.detailIndex"
@ -11,7 +12,6 @@
:pagination="props.pagination" :pagination="props.pagination"
:table-data="props.tableData" :table-data="props.tableData"
:page-change="props.pageChange" :page-change="props.pageChange"
:mask-closable="false"
@loaded="loadedCase" @loaded="loadedCase"
> >
<template #titleLeft> <template #titleLeft>
@ -534,6 +534,15 @@
const changeHandler = debounce(() => { const changeHandler = debounce(() => {
tabDetailRef.value.handleOK(); tabDetailRef.value.handleOK();
}, 300); }, 300);
watch(
() => props.detailId,
(val) => {
if (val) {
updateSuccess();
}
}
);
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -25,12 +25,17 @@
v-if="showType === 'list'" v-if="showType === 'list'"
v-bind="propsRes" v-bind="propsRes"
ref="tableRef" ref="tableRef"
filter-icon-align-left
class="mt-4" class="mt-4"
:action-config="tableBatchActions" :action-config="tableBatchActions"
@selected-change="handleTableSelect" @selected-change="handleTableSelect"
v-on="propsEvent" v-on="propsEvent"
@batch-action="handleTableBatch" @batch-action="handleTableBatch"
@change="changeHandler"
> >
<template #num="{ record, rowIndex }">
<span class="flex w-full" @click="showCaseDetail(record.id, rowIndex)">{{ record.num }}</span>
</template>
<template #name="{ record, rowIndex }"> <template #name="{ record, rowIndex }">
<a-button type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</a-button> <a-button type="text" class="px-0" @click="showCaseDetail(record.id, rowIndex)">{{ record.name }}</a-button>
</template> </template>
@ -78,8 +83,11 @@
</a-tooltip> </a-tooltip>
</template> </template>
<!-- 渲染自定义字段开始TODO --> <!-- 渲染自定义字段开始TODO -->
<!-- <template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }"> <template v-for="item in customFieldsColumns" :key="item.slotName" #[item.slotName]="{ record }">
</template> --> <a-tooltip :content="getTableFields(record.customFields, item)" position="top" :mouse-enter-delay="100" mini>
<div>{{ getTableFields(record.customFields, item) }}</div>
</a-tooltip>
</template>
<!-- 渲染自定义字段结束 --> <!-- 渲染自定义字段结束 -->
<template #operation="{ record }"> <template #operation="{ record }">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" @click="operateCase(record, 'edit')">{{ <MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" @click="operateCase(record, 'edit')">{{
@ -186,7 +194,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue'; import { Message, TableChangeExtra, TableData } from '@arco-design/web-vue';
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter'; import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type'; import { FilterFormItem, FilterResult, FilterType } from '@/components/pure/ms-advance-filter/type';
@ -213,6 +221,7 @@
batchDeleteCase, batchDeleteCase,
batchMoveToModules, batchMoveToModules,
deleteCaseRequest, deleteCaseRequest,
dragSort,
getCaseDefaultFields, getCaseDefaultFields,
getCaseDetail, getCaseDetail,
getCaseList, getCaseList,
@ -230,12 +239,13 @@
CaseModuleQueryParams, CaseModuleQueryParams,
CustomAttributes, CustomAttributes,
DemandItem, DemandItem,
DragCase,
} from '@/models/caseManagement/featureCase'; } from '@/models/caseManagement/featureCase';
import type { TableQueryParams } from '@/models/common'; import type { TableQueryParams } from '@/models/common';
import { CaseManagementRouteEnum } from '@/enums/routeEnum'; import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum'; import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
import { getCaseLevels, getReviewStatusClass, getStatusText } from './utils'; import { getCaseLevels, getReviewStatusClass, getStatusText, getTableFields } from './utils';
import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface'; import { LabelValue } from '@arco-design/web-vue/es/tree-select/interface';
const { openModal } = useModal(); const { openModal } = useModal();
@ -332,16 +342,19 @@
const columns: MsTableColumn = [ const columns: MsTableColumn = [
{ {
title: 'caseManagement.featureCase.tableColumnID', 'title': 'caseManagement.featureCase.tableColumnID',
dataIndex: 'num', 'slotName': 'num',
width: 200, 'dataIndex': 'num',
showInTable: true, 'width': 200,
sortable: { 'showInTable': true,
'sortable': {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, 'filter-icon-align-left': true,
ellipsis: true, 'showTooltip': true,
showDrag: false, 'ellipsis': true,
'showDrag': false,
}, },
{ {
title: 'caseManagement.featureCase.tableColumnName', title: 'caseManagement.featureCase.tableColumnName',
@ -353,6 +366,7 @@
editType: ColumnEditTypeEnum.INPUT, editType: ColumnEditTypeEnum.INPUT,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
ellipsis: true, ellipsis: true,
showDrag: false, showDrag: false,
@ -411,6 +425,7 @@
dataIndex: 'updateTime', dataIndex: 'updateTime',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showInTable: true, showInTable: true,
width: 200, width: 200,
@ -972,12 +987,13 @@
async function getDefaultFields() { async function getDefaultFields() {
const result = await getCaseDefaultFields(currentProjectId.value); const result = await getCaseDefaultFields(currentProjectId.value);
initDefaultFields.value = result.customFields; initDefaultFields.value = result.customFields;
customFieldsColumns = initDefaultFields.value.map((item: any) => { customFieldsColumns = initDefaultFields.value
.filter((item: any) => !item.internal)
.map((item: any) => {
return { return {
title: item.fieldName, title: item.fieldName,
slotName: item.fieldId as string, slotName: item.fieldId as string,
dataIndex: item.fieldId, dataIndex: item.fieldId,
showTooltip: true,
showInTable: true, showInTable: true,
showDrag: true, showDrag: true,
width: 300, width: 300,
@ -1072,7 +1088,6 @@
...detailResult, ...detailResult,
moduleId: value, moduleId: value,
customFields: getCustomMaps(detailResult), customFields: getCustomMaps(detailResult),
tags: JSON.parse(detailResult.tags),
}, },
fileList: [], fileList: [],
}; };
@ -1104,6 +1119,36 @@
initData(); initData();
}; };
//
async function changeHandler(data: TableData[], extra: TableChangeExtra, currentData: TableData[]) {
if (extra && extra.dragTarget?.id) {
const params: DragCase = {
projectId: currentProjectId.value,
targetId: '',
moveMode: 'BEFORE',
moveId: extra.dragTarget.id as string,
};
const index = currentData.findIndex((item: any) => item.raw.id === extra.dragTarget?.id);
if (index > -1 && currentData[index + 1].raw) {
params.moveMode = 'AFTER';
params.targetId = currentData[index + 1].raw.id;
} else if (index > -1 && !currentData[index + 1].raw) {
if (index > -1 && currentData[index - 1].raw) {
params.moveMode = 'BEFORE';
params.targetId = currentData[index - 1].raw.id;
}
}
try {
await dragSort(params);
Message.success(t('caseManagement.featureCase.sortSuccess'));
initData();
} catch (error) {
console.log(error);
}
}
}
onBeforeMount(() => { onBeforeMount(() => {
if (route.query.id) { if (route.query.id) {
showCaseDetail(route.query.id as string, 0); showCaseDetail(route.query.id as string, 0);

View File

@ -237,6 +237,7 @@
showInTable: true, showInTable: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
ellipsis: true, ellipsis: true,
@ -251,6 +252,7 @@
width: 300, width: 300,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
ellipsis: true, ellipsis: true,
showDrag: false, showDrag: false,
@ -325,6 +327,7 @@
showInTable: true, showInTable: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
showDrag: true, showDrag: true,
@ -343,6 +346,7 @@
dataIndex: 'updateTime', dataIndex: 'updateTime',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showInTable: true, showInTable: true,
width: 200, width: 200,

View File

@ -68,6 +68,7 @@
dataIndex: 'reviewName', dataIndex: 'reviewName',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
width: 300, width: 300,

View File

@ -255,6 +255,7 @@
showTooltip: true, showTooltip: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
}, },
@ -263,6 +264,7 @@
dataIndex: 'name', dataIndex: 'name',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showTooltip: true, showTooltip: true,
width: 300, width: 300,

View File

@ -132,4 +132,31 @@ export function getCaseLevels(customFields: CustomAttributes[]): CaseLevel {
); );
} }
// 处理自定义字段
export function getTableFields(customFields: any, itemDataIndex: any) {
const multipleExcludes = ['MULTIPLE_SELECT', 'CHECKBOX', 'MULTIPLE_MEMBER'];
const selectExcludes = ['MEMBER', 'RADIO', 'SELECT'];
const currentColumnData = customFields.find((item: any) => itemDataIndex.dataIndex === item.fieldId);
// 处理多选项
if (multipleExcludes.includes(currentColumnData.type)) {
const selectValue = JSON.parse(currentColumnData.defaultValue);
return currentColumnData.options
.filter((item: any) => selectValue.includes(item.value))
.map((it: any) => it.text)
.join(',');
}
if (currentColumnData.type === 'MULTIPLE_INPUT') {
// 处理标签形式
return JSON.parse(currentColumnData.defaultValue).join('');
}
if (selectExcludes.includes(currentColumnData.type)) {
return currentColumnData.options
.filter((item: any) => currentColumnData.defaultValue === item.value)
.map((it: any) => it.text)
.join();
}
return currentColumnData.defaultValue;
}
export default {}; export default {};

View File

@ -250,4 +250,5 @@ export default {
'caseManagement.featureCase.cancelDependencyContent': 'Cancel after impact test plan related statistics', 'caseManagement.featureCase.cancelDependencyContent': 'Cancel after impact test plan related statistics',
'caseManagement.featureCase.associatedSuccess': 'Associated with success', 'caseManagement.featureCase.associatedSuccess': 'Associated with success',
'caseManagement.featureCase.defectSource': 'defect Source', 'caseManagement.featureCase.defectSource': 'defect Source',
'caseManagement.featureCase.sortSuccess': 'Sort successfully',
}; };

View File

@ -245,4 +245,5 @@ export default {
'caseManagement.featureCase.cancelDependencyContent': '取消后,影响测试计划相关统计', 'caseManagement.featureCase.cancelDependencyContent': '取消后,影响测试计划相关统计',
'caseManagement.featureCase.associatedSuccess': '关联成功', 'caseManagement.featureCase.associatedSuccess': '关联成功',
'caseManagement.featureCase.defectSource': '缺陷来源', 'caseManagement.featureCase.defectSource': '缺陷来源',
'caseManagement.featureCase.sortSuccess': '排序成功',
}; };

View File

@ -93,14 +93,14 @@
const loginConfig = useStorage('login-config', { const loginConfig = useStorage('login-config', {
rememberPassword: true, rememberPassword: true,
username: 'admin', username: '',
password: 'Calong@2015', password: '',
}); });
const userInfo = reactive({ const userInfo = reactive({
authenticate: 'LOCAL', authenticate: 'LOCAL',
username: 'admin', username: '',
password: 'Calong@2015', password: '',
}); });
const handleSubmit = async ({ const handleSubmit = async ({

View File

@ -164,6 +164,7 @@
dataIndex: 'createTime', dataIndex: 'createTime',
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
showInTable: true, showInTable: true,
width: 300, width: 300,

View File

@ -27,6 +27,7 @@
<MsIcon type="icon-icon-maybe_outlined" class="mr-[8px] cursor-pointer hover:text-[rgb(var(--primary-5))]" /> <MsIcon type="icon-icon-maybe_outlined" class="mr-[8px] cursor-pointer hover:text-[rgb(var(--primary-5))]" />
</a-tooltip> </a-tooltip>
<MsButton <MsButton
v-permission="['PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD']"
type="icon" type="icon"
status="secondary" status="secondary"
class="!rounded-[var(--border-radius-small)] !text-[var(--color-text-1)]" class="!rounded-[var(--border-radius-small)] !text-[var(--color-text-1)]"
@ -39,6 +40,7 @@
</MsButton> </MsButton>
<MsButton <MsButton
v-if="detail?.storage === 'GIT'" v-if="detail?.storage === 'GIT'"
v-permission="['PROJECT_FILE_MANAGEMENT:READ+UPDATE']"
type="icon" type="icon"
status="secondary" status="secondary"
class="!rounded-[var(--border-radius-small)] !text-[var(--color-text-1)]" class="!rounded-[var(--border-radius-small)] !text-[var(--color-text-1)]"
@ -132,7 +134,9 @@
:all-names="[]" :all-names="[]"
@update-desc-finish="detailDrawerRef?.initDetail" @update-desc-finish="detailDrawerRef?.initDetail"
> >
<MsButton class="ml-[8px]"><MsIcon type="icon-icon_edit_outlined"></MsIcon></MsButton> <MsButton v-permission="['PROJECT_FILE_MANAGEMENT:READ+UPDATE']" class="ml-[8px]"
><MsIcon type="icon-icon_edit_outlined"></MsIcon
></MsButton>
</popConfirm> </popConfirm>
</template> </template>
</div> </div>
@ -167,7 +171,12 @@
v-on="caseTableEvent" v-on="caseTableEvent"
> >
<template #action="{ record }"> <template #action="{ record }">
<MsButton type="text" class="mr-[8px]" @click="updateCase(record)"> <MsButton
v-permission="['PROJECT_FILE_MANAGEMENT:READ+UPDATE']"
type="text"
class="mr-[8px]"
@click="updateCase(record)"
>
{{ t('project.fileManagement.updateCaseFile') }} {{ t('project.fileManagement.updateCaseFile') }}
</MsButton> </MsButton>
</template> </template>

View File

@ -1,7 +1,9 @@
<template> <template>
<div class="flex h-[calc(100vh-88px)] flex-col overflow-hidden p-[24px]"> <div class="flex h-[calc(100vh-88px)] flex-col overflow-hidden p-[24px]">
<div class="header"> <div class="header">
<a-button type="primary" @click="handleAddClick">{{ t('project.fileManagement.addFile') }}</a-button> <a-button v-permission="['PROJECT_FILE_MANAGEMENT:READ+ADD']" type="primary" @click="handleAddClick">{{
t('project.fileManagement.addFile')
}}</a-button>
<div class="header-right"> <div class="header-right">
<a-select v-model="tableFileType" class="w-[240px]" :loading="fileTypeLoading" @change="searchList"> <a-select v-model="tableFileType" class="w-[240px]" :loading="fileTypeLoading" @change="searchList">
<template #prefix> <template #prefix>
@ -562,15 +564,18 @@
{ {
label: 'project.fileManagement.download', label: 'project.fileManagement.download',
eventTag: 'download', eventTag: 'download',
permission: ['PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD'],
}, },
{ {
label: 'project.fileManagement.move', label: 'project.fileManagement.move',
eventTag: 'move', eventTag: 'move',
permission: ['PROJECT_FILE_MANAGEMENT:READ+UPDATE'],
}, },
{ {
label: 'project.fileManagement.delete', label: 'project.fileManagement.delete',
eventTag: 'delete', eventTag: 'delete',
danger: true, danger: true,
permission: ['PROJECT_FILE_MANAGEMENT:READ+DELETE'],
}, },
], ],
}; };
@ -579,11 +584,13 @@
{ {
label: 'project.fileManagement.download', label: 'project.fileManagement.download',
eventTag: 'download', eventTag: 'download',
permission: ['PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD'],
}, },
{ {
label: 'project.fileManagement.delete', label: 'project.fileManagement.delete',
eventTag: 'delete', eventTag: 'delete',
danger: true, danger: true,
permission: ['PROJECT_FILE_MANAGEMENT:READ+DELETE'],
}, },
], ],
}; };

View File

@ -18,7 +18,7 @@
> >
<template #footer> <template #footer>
<div class="mb-[6px] mt-[4px] p-[3px_8px]"> <div class="mb-[6px] mt-[4px] p-[3px_8px]">
<MsButton type="text" @click="emit('createRobot')"> <MsButton v-permission="['PROJECT_MESSAGE:READ+ADD']" type="text" @click="emit('createRobot')">
<MsIcon type="icon-icon_add_outlined" class="mr-[8px] text-[rgb(var(--primary-6))]" size="14" /> <MsIcon type="icon-icon_add_outlined" class="mr-[8px] text-[rgb(var(--primary-6))]" size="14" />
{{ t('project.messageManagement.createBot') }} {{ t('project.messageManagement.createBot') }}
</MsButton> </MsButton>
@ -86,6 +86,7 @@
<div v-if="!record.children && record.projectRobotConfigMap?.[dataIndex as string]" class="flex items-center"> <div v-if="!record.children && record.projectRobotConfigMap?.[dataIndex as string]" class="flex items-center">
<a-switch <a-switch
v-model:model-value="record.projectRobotConfigMap[dataIndex as string].enable" v-model:model-value="record.projectRobotConfigMap[dataIndex as string].enable"
v-permission="['PROJECT_MESSAGE:READ+UPDATE']"
:before-change="(val) => handleChangeIntercept(!!val, record, dataIndex as string)" :before-change="(val) => handleChangeIntercept(!!val, record, dataIndex as string)"
size="small" size="small"
type="line" type="line"
@ -104,7 +105,13 @@
/> />
</template> </template>
</a-popover> </a-popover>
<MsButton type="button" @click="editRobot(record, dataIndex as string)">{{ t('common.setting') }}</MsButton> <MsButton
v-permission="['PROJECT_MESSAGE:READ+UPDATE']"
v-xpack
type="button"
@click="editRobot(record, dataIndex as string)"
>{{ t('common.setting') }}</MsButton
>
</div> </div>
<span v-else></span> <span v-else></span>
</template> </template>

View File

@ -7,7 +7,7 @@
<span class="text-[14px]">{{ t('project.messageManagement.notRemind') }}</span> <span class="text-[14px]">{{ t('project.messageManagement.notRemind') }}</span>
</template> </template>
</a-alert> </a-alert>
<a-button type="primary" class="mb-[16px]" @click="handleCreateClick"> <a-button v-permission="['PROJECT_MESSAGE:READ+ADD']" type="primary" class="mb-[16px]" @click="handleCreateClick">
{{ t('project.messageManagement.createBot') }} {{ t('project.messageManagement.createBot') }}
</a-button> </a-button>
<div <div
@ -52,6 +52,7 @@
<div class="flex items-center justify-between leading-[24px]"> <div class="flex items-center justify-between leading-[24px]">
<div v-if="!['IN_SITE', 'MAIL'].includes(robot.platform)"> <div v-if="!['IN_SITE', 'MAIL'].includes(robot.platform)">
<a-button <a-button
v-permission="['PROJECT_MESSAGE:READ+UPDATE']"
type="outline" type="outline"
size="mini" size="mini"
class="arco-btn-outline--secondary mr-[8px]" class="arco-btn-outline--secondary mr-[8px]"
@ -59,7 +60,13 @@
> >
{{ t('common.edit') }} {{ t('common.edit') }}
</a-button> </a-button>
<a-button type="outline" size="mini" class="arco-btn-outline--secondary" @click="delRobot(robot)"> <a-button
v-permission="['PROJECT_MESSAGE:READ+DELETE']"
type="outline"
size="mini"
class="arco-btn-outline--secondary"
@click="delRobot(robot)"
>
{{ t('common.delete') }} {{ t('common.delete') }}
</a-button> </a-button>
</div> </div>

View File

@ -25,16 +25,22 @@
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="font-normal">{{ t(item.title) }}</span> <span class="font-normal">{{ t(item.title) }}</span>
<span> <span>
<a-tooltip
:content="isHasSystemPermission ? '' : t('organization.service.noPermissionsTip')"
position="bottom"
>
<a-button <a-button
v-for="links of item.skipTitle" v-for="links of item.skipTitle"
:key="links.name" :key="links.name"
size="mini" size="mini"
class="ml-3 px-0 text-sm" class="ml-3 px-0 text-sm"
type="text" type="text"
:disabled="!links.disabled"
@click.stop="jumpHandler(links)" @click.stop="jumpHandler(links)"
> >
{{ t(links.name) }} {{ t(links.name) }}
</a-button> </a-button>
</a-tooltip>
</span> </span>
</div> </div>
<div class="text-xs text-[var(--color-text-4)]"> <div class="text-xs text-[var(--color-text-4)]">
@ -61,11 +67,20 @@
import ServiceList from './components/serviceList.vue'; import ServiceList from './components/serviceList.vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useUserStore from '@/store/modules/user/index';
import { openWindow } from '@/utils/index'; import { openWindow } from '@/utils/index';
import { hasAnyPermission } from '@/utils/permission';
import type { SkipTitle, StepListType } from '@/models/setting/serviceIntegration'; import type { SkipTitle, StepListType } from '@/models/setting/serviceIntegration';
import { SettingRouteEnum } from '@/enums/routeEnum'; import { SettingRouteEnum } from '@/enums/routeEnum';
const userStore = useUserStore();
const isHasSystemPermission = computed(() => {
const { systemPermissions } = userStore.currentRole;
return hasAnyPermission(systemPermissions, ['SYSTEM']) as boolean;
});
const { t } = useI18n(); const { t } = useI18n();
const router = useRouter(); const router = useRouter();
const cardContent = ref<StepListType[]>([ const cardContent = ref<StepListType[]>([
@ -78,11 +93,13 @@
name: 'organization.service.developmentDoc', name: 'organization.service.developmentDoc',
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', 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, active: false,
disabled: false,
}, },
{ {
name: 'organization.service.downPlugin', name: 'organization.service.downPlugin',
src: 'https://github.com/metersphere/metersphere-platform-plugin', src: 'https://github.com/metersphere/metersphere-platform-plugin',
active: false, active: false,
disabled: false,
}, },
], ],
step: '@/assets/images/ms_plugindownload.jpg', step: '@/assets/images/ms_plugindownload.jpg',
@ -97,6 +114,7 @@
name: 'organization.service.jumpPlugin', name: 'organization.service.jumpPlugin',
src: '', src: '',
active: true, active: true,
disabled: isHasSystemPermission.value,
}, },
], ],
step: '@/assets/images/ms_configplugin.jpg', step: '@/assets/images/ms_configplugin.jpg',

View File

@ -50,4 +50,5 @@ export default {
'organization.service.closeSuccess': 'Disable successfully', 'organization.service.closeSuccess': 'Disable successfully',
'organization.service.configSuccess': 'Configuration successfully', 'organization.service.configSuccess': 'Configuration successfully',
'organization.service.updateSuccess': 'Update successfully', 'organization.service.updateSuccess': 'Update successfully',
'organization.service.noPermissionsTip': 'You do not have operation permission, please contact the administrator',
}; };

View File

@ -45,4 +45,5 @@ export default {
'organization.service.closeSuccess': '禁用成功', 'organization.service.closeSuccess': '禁用成功',
'organization.service.configSuccess': '配置成功', 'organization.service.configSuccess': '配置成功',
'organization.service.updateSuccess': '更新成功', 'organization.service.updateSuccess': '更新成功',
'organization.service.noPermissionsTip': '您没有操作权限,请联系管理员',
}; };

View File

@ -2,7 +2,7 @@
<div> <div>
<MsCard :loading="loading" simple> <MsCard :loading="loading" simple>
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<a-button type="primary" @click="createAuth"> <a-button v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+ADD']" type="primary" @click="createAuth">
{{ t('system.config.auth.add') }} {{ t('system.config.auth.add') }}
</a-button> </a-button>
</div> </div>
@ -11,12 +11,24 @@
<a-button type="text" class="px-0" @click="openAuthDetail(record.id)">{{ record.name }}</a-button> <a-button type="text" class="px-0" @click="openAuthDetail(record.id)">{{ record.name }}</a-button>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<MsButton @click="editAuth(record)">{{ t('system.config.auth.edit') }}</MsButton> <MsButton v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+UPDATE']" @click="editAuth(record)">{{
<MsButton v-if="record.enable" @click="disabledAuth(record)"> t('system.config.auth.edit')
}}</MsButton>
<MsButton
v-if="record.enable"
v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+UPDATE']"
@click="disabledAuth(record)"
>
{{ t('system.config.auth.disable') }} {{ t('system.config.auth.disable') }}
</MsButton> </MsButton>
<MsButton v-else @click="enableAuth(record)">{{ t('system.config.auth.enable') }}</MsButton> <MsButton v-else v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+UPDATE']" @click="enableAuth(record)">{{
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction> t('system.config.auth.enable')
}}</MsButton>
<MsTableMoreAction
v-permission="['SYSTEM_PARAMETER_SETTING_AUTH:READ+DELETE']"
:list="tableActions"
@select="handleSelect($event, record)"
></MsTableMoreAction>
</template> </template>
</ms-base-table> </ms-base-table>
</MsCard> </MsCard>

View File

@ -3,7 +3,12 @@
<MsCard class="mb-[16px]" :loading="baseloading" simple auto-height> <MsCard class="mb-[16px]" :loading="baseloading" simple auto-height>
<div class="mb-[16px] flex justify-between"> <div class="mb-[16px] flex justify-between">
<div class="text-[var(--color-text-000)]">{{ t('system.config.baseInfo') }}</div> <div class="text-[var(--color-text-000)]">{{ t('system.config.baseInfo') }}</div>
<a-button type="outline" size="mini" @click="baseInfoDrawerVisible = true"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_BASE:READ+UPDATE']"
type="outline"
size="mini"
@click="baseInfoDrawerVisible = true"
>
{{ t('system.config.update') }} {{ t('system.config.update') }}
</a-button> </a-button>
</div> </div>
@ -12,7 +17,12 @@
<MsCard class="mb-[16px]" :loading="emailLoading" simple auto-height> <MsCard class="mb-[16px]" :loading="emailLoading" simple auto-height>
<div class="mb-[16px] flex justify-between"> <div class="mb-[16px] flex justify-between">
<div class="text-[var(--color-text-000)]">{{ t('system.config.emailConfig') }}</div> <div class="text-[var(--color-text-000)]">{{ t('system.config.emailConfig') }}</div>
<a-button type="outline" size="mini" @click="emailConfigDrawerVisible = true"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_BASE:READ+UPDATE']"
type="outline"
size="mini"
@click="emailConfigDrawerVisible = true"
>
{{ t('system.config.update') }} {{ t('system.config.update') }}
</a-button> </a-button>
</div> </div>

View File

@ -39,7 +39,12 @@
<div class="config-content"> <div class="config-content">
<div class="config-title !mb-[8px] flex items-center justify-between"> <div class="config-title !mb-[8px] flex items-center justify-between">
{{ t('system.config.page.pagePreview') }} {{ t('system.config.page.pagePreview') }}
<MsButton class="!leading-none" @click="resetLoginPageConfig">{{ t('system.config.page.reset') }}</MsButton> <MsButton
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
class="!leading-none"
@click="resetLoginPageConfig"
>{{ t('system.config.page.reset') }}</MsButton
>
</div> </div>
<!-- 登录页预览盒子 --> <!-- 登录页预览盒子 -->
<div :class="['config-preview', currentLocale === 'en-US' ? 'config-preview--en' : '']"> <div :class="['config-preview', currentLocale === 'en-US' ? 'config-preview--en' : '']">
@ -90,7 +95,12 @@
size-unit="KB" size-unit="KB"
:auto-upload="false" :auto-upload="false"
> >
<a-button type="outline" class="arco-btn-outline--secondary" size="mini"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
>
{{ t('system.config.page.replace') }} {{ t('system.config.page.replace') }}
</a-button> </a-button>
</MsUpload> </MsUpload>
@ -118,7 +128,12 @@
size-unit="KB" size-unit="KB"
:auto-upload="false" :auto-upload="false"
> >
<a-button type="outline" class="arco-btn-outline--secondary" size="mini"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
>
{{ t('system.config.page.replace') }} {{ t('system.config.page.replace') }}
</a-button> </a-button>
</MsUpload> </MsUpload>
@ -148,7 +163,12 @@
size-unit="MB" size-unit="MB"
:auto-upload="false" :auto-upload="false"
> >
<a-button type="outline" class="arco-btn-outline--secondary" size="mini"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
>
{{ t('system.config.page.replace') }} {{ t('system.config.page.replace') }}
</a-button> </a-button>
</MsUpload> </MsUpload>
@ -199,7 +219,12 @@
<div class="config-content border border-solid border-[var(--color-text-n8)] !bg-white"> <div class="config-content border border-solid border-[var(--color-text-n8)] !bg-white">
<div class="config-title !mb-[8px] flex items-center justify-between"> <div class="config-title !mb-[8px] flex items-center justify-between">
{{ t('system.config.page.pagePreview') }} {{ t('system.config.page.pagePreview') }}
<MsButton class="!leading-none" @click="resetPlatformConfig">{{ t('system.config.page.reset') }}</MsButton> <MsButton
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
class="!leading-none"
@click="resetPlatformConfig"
>{{ t('system.config.page.reset') }}</MsButton
>
</div> </div>
<!-- 平台主页预览盒子 --> <!-- 平台主页预览盒子 -->
<div :class="['config-preview', '!h-[290px]', currentLocale === 'en-US' ? '!h-[340px]' : '']"> <div :class="['config-preview', '!h-[290px]', currentLocale === 'en-US' ? '!h-[340px]' : '']">
@ -252,7 +277,12 @@
size-unit="MB" size-unit="MB"
:auto-upload="false" :auto-upload="false"
> >
<a-button type="outline" class="arco-btn-outline--secondary" size="mini"> <a-button
v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']"
type="outline"
class="arco-btn-outline--secondary"
size="mini"
>
{{ t('system.config.page.replace') }} {{ t('system.config.page.replace') }}
</a-button> </a-button>
</MsUpload> </MsUpload>
@ -299,8 +329,10 @@
class="fixed bottom-0 right-[16px] z-[999] flex justify-between bg-white p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]" class="fixed bottom-0 right-[16px] z-[999] flex justify-between bg-white p-[24px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
:style="{ width: `calc(100% - ${menuWidth + 16}px)` }" :style="{ width: `calc(100% - ${menuWidth + 16}px)` }"
> >
<a-button type="secondary" @click="resetAll">{{ t('system.config.page.resetAll') }}</a-button> <a-button v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']" type="secondary" @click="resetAll">{{
<a-button type="primary" @click="beforeSave"> t('system.config.page.resetAll')
}}</a-button>
<a-button v-permission="['SYSTEM_PARAMETER_SETTING_DISPLAY:READ+UPDATE']" type="primary" @click="beforeSave">
{{ t('system.config.page.save') }} {{ t('system.config.page.save') }}
</a-button> </a-button>
</div> </div>

View File

@ -31,10 +31,10 @@
const isInitMemoryCleanup = ref(activeTab.value === 'memoryCleanup'); const isInitMemoryCleanup = ref(activeTab.value === 'memoryCleanup');
const authConfigRef = ref<AuthConfigInstance | null>(); const authConfigRef = ref<AuthConfigInstance | null>();
const tabList = ref([ const tabList = ref([
{ key: 'baseConfig', title: t('system.config.baseConfig') }, { key: 'baseConfig', title: t('system.config.baseConfig'), permission: ['SYSTEM_PARAMETER_SETTING_BASE:READ'] },
{ key: 'pageConfig', title: t('system.config.pageConfig') }, { key: 'pageConfig', title: t('system.config.pageConfig'), permission: ['SYSTEM_PARAMETER_SETTING_DISPLAY:READ'] },
{ key: 'authConfig', title: t('system.config.authConfig') }, { key: 'authConfig', title: t('system.config.authConfig'), permission: ['SYSTEM_PARAMETER_SETTING_AUTH:READ'] },
{ key: 'memoryCleanup', title: t('system.config.memoryCleanup') }, { key: 'memoryCleanup', title: t('system.config.memoryCleanup'), permission: [] },
]); ]);
watch( watch(

View File

@ -1,7 +1,7 @@
<template> <template>
<MsCard :loading="loading" simple> <MsCard :loading="loading" simple>
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<a-button v-xpack type="primary" @click="addPool"> <a-button v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+ADD']" v-xpack type="primary" @click="addPool">
{{ t('system.resourcePool.createPool') }} {{ t('system.resourcePool.createPool') }}
</a-button> </a-button>
<a-input-search <a-input-search
@ -18,12 +18,25 @@
<a-button type="text" class="px-0" @click="showPoolDetail(record.id)">{{ record.name }}</a-button> <a-button type="text" class="px-0" @click="showPoolDetail(record.id)">{{ record.name }}</a-button>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton> <MsButton v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+UPDATE']" @click="editPool(record)">{{
<MsButton v-if="record.enable" v-xpack @click="disabledPool(record)"> t('system.resourcePool.editPool')
}}</MsButton>
<MsButton
v-if="record.enable"
v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+UPDATE']"
v-xpack
@click="disabledPool(record)"
>
{{ t('system.resourcePool.tableDisable') }} {{ t('system.resourcePool.tableDisable') }}
</MsButton> </MsButton>
<MsButton v-else v-xpack @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton> <MsButton v-else v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+UPDATE']" v-xpack @click="enablePool(record)">{{
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction> t('system.resourcePool.tableEnable')
}}</MsButton>
<MsTableMoreAction
v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+DELETE']"
:list="tableActions"
@select="handleSelect($event, record)"
></MsTableMoreAction>
</template> </template>
</ms-base-table> </ms-base-table>
</MsCard> </MsCard>
@ -40,7 +53,13 @@
show-description show-description
> >
<template #tbutton> <template #tbutton>
<a-button type="outline" size="mini" :disabled="drawerLoading" @click="editPool(activePool)"> <a-button
v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+UPDATE']"
type="outline"
size="mini"
:disabled="drawerLoading"
@click="editPool(activePool)"
>
{{ t('system.resourcePool.editPool') }} {{ t('system.resourcePool.editPool') }}
</a-button> </a-button>
</template> </template>

View File

@ -2,13 +2,15 @@
<MsCard simple> <MsCard simple>
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<div> <div>
<a-button class="mr-3" type="primary" @click="showUserModal('create')"> <a-button v-permission="['SYSTEM_USER:READ+ADD']" class="mr-3" type="primary" @click="showUserModal('create')">
{{ t('system.user.createUser') }} {{ t('system.user.createUser') }}
</a-button> </a-button>
<a-button class="mr-3" type="outline" @click="showEmailInviteModal"> <a-button v-permission="['SYSTEM_USER_INVITE']" class="mr-3" type="outline" @click="showEmailInviteModal">
{{ t('system.user.emailInvite') }} {{ t('system.user.emailInvite') }}
</a-button> </a-button>
<a-button class="mr-3" type="outline" @click="showImportModal">{{ t('system.user.importUser') }}</a-button> <a-button v-permission="['SYSTEM_USER:READ+IMPORT']" class="mr-3" type="outline" @click="showImportModal">{{
t('system.user.importUser')
}}</a-button>
</div> </div>
<a-input-search <a-input-search
v-model:model-value="keyword" v-model:model-value="keyword"
@ -28,10 +30,14 @@
<template #action="{ record }"> <template #action="{ record }">
<template v-if="!record.enable"> <template v-if="!record.enable">
<MsButton @click="enableUser(record)">{{ t('system.user.enable') }}</MsButton> <MsButton @click="enableUser(record)">{{ t('system.user.enable') }}</MsButton>
<MsButton @click="deleteUser(record)">{{ t('system.user.delete') }}</MsButton> <MsButton v-permission="['SYSTEM_USER:READ+DELETE']" @click="deleteUser(record)">{{
t('system.user.delete')
}}</MsButton>
</template> </template>
<template v-else> <template v-else>
<MsButton @click="showUserModal('edit', record)">{{ t('system.user.editUser') }}</MsButton> <MsButton v-permission="['SYSTEM_USER:READ+UPDATE']" @click="showUserModal('edit', record)">{{
t('system.user.editUser')
}}</MsButton>
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction> <MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
</template> </template>
</template> </template>
@ -470,28 +476,34 @@
{ {
label: 'system.user.batchActionAddProject', label: 'system.user.batchActionAddProject',
eventTag: 'batchAddProject', eventTag: 'batchAddProject',
permission: ['SYSTEM_USER:READ+ADD'],
}, },
{ {
label: 'system.user.batchActionAddUserGroup', label: 'system.user.batchActionAddUserGroup',
eventTag: 'batchAddUserGroup', eventTag: 'batchAddUserGroup',
permission: ['SYSTEM_USER:READ+ADD'],
}, },
{ {
label: 'system.user.batchActionAddOrganization', label: 'system.user.batchActionAddOrganization',
eventTag: 'batchAddOrganization', eventTag: 'batchAddOrganization',
permission: ['SYSTEM_USER:READ+ADD'],
}, },
], ],
moreAction: [ moreAction: [
{ {
label: 'system.user.resetPassword', label: 'system.user.resetPassword',
eventTag: 'resetPassword', eventTag: 'resetPassword',
permission: ['SYSTEM_USER:READ+UPDATE'],
}, },
{ {
label: 'system.user.disable', label: 'system.user.disable',
eventTag: 'disabled', eventTag: 'disabled',
permission: ['SYSTEM_USER:READ+UPDATE'],
}, },
{ {
label: 'system.user.enable', label: 'system.user.enable',
eventTag: 'enable', eventTag: 'enable',
permission: ['SYSTEM_USER:READ+UPDATE'],
}, },
{ {
isDivider: true, isDivider: true,
@ -500,6 +512,7 @@
label: 'system.user.delete', label: 'system.user.delete',
eventTag: 'delete', eventTag: 'delete',
danger: true, danger: true,
permission: ['SYSTEM_USER:READ+DELETE'],
}, },
], ],
}; };

View File

@ -121,6 +121,7 @@
editType: ColumnEditTypeEnum.INPUT, editType: ColumnEditTypeEnum.INPUT,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
ellipsis: true, ellipsis: true,
showDrag: false, showDrag: false,
@ -186,6 +187,7 @@
showInTable: true, showInTable: true,
sortable: { sortable: {
sortDirections: ['ascend', 'descend'], sortDirections: ['ascend', 'descend'],
sorter: true,
}, },
width: 200, width: 200,
showDrag: true, showDrag: true,