feat(项目管理): 项目管理-菜单管理首页静态页面

This commit is contained in:
RubyLiu 2023-09-27 18:52:34 +08:00 committed by fit2-zhao
parent 58966bda2f
commit 05c863f62e
17 changed files with 304 additions and 52 deletions

View File

@ -0,0 +1,60 @@
import MSR from '@/api/http/index';
import * as Url from '@/api/requrls/project-management/menuManagement';
import type { MenuTableListItem, MenuTableListParams } from '@/models/projectManagement/menuManagement';
import { MenuEnum } from '@/enums/commonEnum';
import { TableQueryParams, CommonList } from '@/models/common';
const tableList: MenuTableListItem[] = [
'workstation',
'loadTest',
'testPlan',
'bugManagement',
'caseManagement',
'apiTest',
'uiTest',
].map((item, index) => {
return { module: item, moduleEnable: index % 2 === 0 };
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function postTabletList(data: TableQueryParams) {
// return MSR.post<CommonList<MenuTableListItem>>({ url: Url.getMenuListUrl, data });
const result: CommonList<MenuTableListItem> = {
list: tableList,
total: tableList.length,
pageSize: 10,
current: 1,
};
return Promise.resolve(result);
}
export function postUpdateMenu(data: MenuTableListParams) {
let suffix = '';
switch (data.type) {
case MenuEnum.workstation:
suffix = 'workstation';
break;
case MenuEnum.testPlan:
suffix = 'test-plan';
break;
case MenuEnum.bugManagement:
suffix = 'issue';
break;
case MenuEnum.caseManagement:
suffix = 'case';
break;
case MenuEnum.apiTest:
suffix = 'api';
break;
case MenuEnum.uiTest:
suffix = 'uiTest';
break;
default:
suffix = 'performance-test';
break;
}
return MSR.post<string>({ url: `${Url.updateConfigByMenuTypeUrl}${suffix}`, data });
}
export default {};

View File

@ -0,0 +1,4 @@
export const getConfigByMenuTypeUrl = '/project/application/';
export const updateConfigByMenuTypeUrl = '/project/application/update/';
// 获取表格最外层的数据
export const getMenuListUrl = '/project/application/module-setting/';

View File

@ -143,6 +143,10 @@
</div>
</slot>
</template>
<template #expand-icon="{ expanded }">
<icon-down-circle v-if="expanded" />
<icon-right-circle v-else />
</template>
</a-table>
<div
class="mt-[16px] flex h-[32px] w-[100%] min-w-[952px] flex-row flex-nowrap items-center justify-end px-0"

View File

@ -47,6 +47,7 @@ export interface MsTableProps<T> {
rowKey: string; // 表格行的key rowId
// 表格列 - 详见 TableColumn https://arco.design/web-vue/components/table-column;
columns: MsTableColumnData[];
showPagination?: boolean; // 是否显示分页
size?: 'mini' | 'small' | 'default' | 'large'; // 表格尺寸
scroll?: {
x?: number | string;

View File

@ -8,6 +8,7 @@ import type { TableData } from '@arco-design/web-vue';
import type { TableQueryParams, CommonList } from '@/models/common';
import { SelectAllEnum } from '@/enums/tableEnum';
import type { MsTableProps, MsTableDataItem, MsTableColumn, MsTableErrorStatus, SetPaginationPrams } from './type';
import { set } from 'nprogress';
export interface Pagination {
current: number;
@ -151,52 +152,82 @@ export default function useTableProps<T>(
// 加载分页列表数据
const loadList = async () => {
const { current, pageSize } = propsRes.value.msPagination as Pagination;
const { rowKey, selectorStatus, excludeKeys } = propsRes.value;
setLoading(true);
try {
const data = await loadListFunc({
current,
pageSize,
sort: sortItem.value,
filter: filterItem.value,
keyword: keyword.value,
...loadListParams.value,
});
const tmpArr = data.list;
(propsRes.value.data as MsTableDataItem<T>[]) = tmpArr.map((item) => {
if (item.updateTime) {
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
}
if (item.createTime) {
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
}
if (dataTransform) {
item = dataTransform(item);
}
if (selectorStatus === SelectAllEnum.ALL) {
if (!excludeKeys.has(item[rowKey])) {
setTableSelected(item[rowKey]);
if (propsRes.value.showPagination) {
const { current, pageSize } = propsRes.value.msPagination as Pagination;
const { rowKey, selectorStatus, excludeKeys } = propsRes.value;
try {
setLoading(true);
const data = await loadListFunc({
current,
pageSize,
sort: sortItem.value,
filter: filterItem.value,
keyword: keyword.value,
...loadListParams.value,
});
const tmpArr = data.list;
(propsRes.value.data as MsTableDataItem<T>[]) = tmpArr.map((item) => {
if (item.updateTime) {
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
}
if (item.createTime) {
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
}
if (dataTransform) {
item = dataTransform(item);
}
if (selectorStatus === SelectAllEnum.ALL) {
if (!excludeKeys.has(item[rowKey])) {
setTableSelected(item[rowKey]);
}
}
return item;
});
if (data.total === 0) {
setTableErrorStatus('empty');
} else {
setTableErrorStatus(false);
}
setPagination({ current: data.current, total: data.total });
return data;
} catch (err) {
setTableErrorStatus('error');
} finally {
setLoading(false);
// debug 模式下打印属性
if (propsRes.value.debug && import.meta.env.DEV) {
// eslint-disable-next-line no-console
// console.log('Table propsRes: ', propsRes.value);
}
return item;
});
if (data.total === 0) {
setTableErrorStatus('empty');
} else {
setTableErrorStatus(false);
}
setPagination({ current: data.current, total: data.total });
return data;
} catch (err) {
setTableErrorStatus('error');
} finally {
setLoading(false);
// debug 模式下打印属性
if (propsRes.value.debug && import.meta.env.DEV) {
// eslint-disable-next-line no-console
// console.log('Table propsRes: ', propsRes.value);
} else {
// 没分页的情况下直接调用loadListFunc
try {
setLoading(true);
const data = await loadListFunc({ keyword: keyword.value, ...loadListParams.value });
if (data.total === 0) {
setTableErrorStatus('empty');
return;
}
setTableErrorStatus(false);
const tmpArr = data.list;
(propsRes.value.data as MsTableDataItem<T>[]) = tmpArr.map((item) => {
if (item.updateTime) {
item.updateTime = dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss');
}
if (item.createTime) {
item.createTime = dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss');
}
if (dataTransform) {
item = dataTransform(item);
}
return item;
});
return data;
} catch (err) {
setTableErrorStatus('error');
} finally {
setLoading(false);
}
}
};

View File

@ -9,4 +9,14 @@ export enum AuthScopeEnum {
PROJECT = 'PROJECT',
}
export enum MenuEnum {
workstation = 'workstation',
loadTest = 'loadTest',
testPlan = 'testPlan',
bugManagement = 'bugManagement',
caseManagement = 'caseManagement',
apiTest = 'apiTest',
uiTest = 'uiTest',
}
export default {};

View File

@ -9,9 +9,9 @@ export default interface CommonResponse<T> {
// 表格查询
export interface TableQueryParams {
// 当前页
current: number;
current?: number;
// 每页条数
pageSize: number;
pageSize?: number;
// 排序仅针对单个字段
sort?: object;
// 排序仅针对单个字段

View File

@ -0,0 +1,13 @@
import { MenuEnum } from '@/enums/commonEnum';
export interface MenuTableListItem {
module: string;
moduleEnable: boolean;
moduleDesc?: string;
}
export interface MenuTableListParams {
projectId: string;
type: MenuEnum;
typeValue: boolean;
}

View File

@ -1,9 +1,110 @@
<template>
<div> 菜单管理 waiting for development</div>
<div class="flex flex-row items-center">
<div class="text-[var(--color-text-1)]"> {{ t('project.menu.management') }}</div>
<a-tooltip :content="t('project.menu.manageTip')">
<MsIcon class="ml-[4px] text-[rgb(var(--primary-5))]" type="icon-icon-maybe_outlined" />
</a-tooltip>
</div>
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
<template #module="{ record }">
<MsIcon :type="getMenuIcon(record.module)" />
<span class="ml-[4px]">{{ record.module }}</span>
</template>
<template #moduleEnable="{ record }">
<a-switch v-model="record.moduleEnable" @change="handleMenuStatusChange(record)" />
</template>
</MsBaseTable>
</template>
<script setup lang="ts">
import { ref } from 'vue';
/**
* @description 项目管理-项目与权限-菜单管理
*/
import { onMounted, computed } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import useTable from '@/components/pure/ms-table/useTable';
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import { postTabletList, postUpdateMenu } from '@/api/modules/project-management/menuManagement';
import { useAppStore } from '@/store';
import { MenuTableListItem } from '@/models/projectManagement/menuManagement';
import { MenuEnum } from '@/enums/commonEnum';
import { Message } from '@arco-design/web-vue';
const appStore = useAppStore();
const currentProjectId = computed(() => appStore.currentProjectId);
const { t } = useI18n();
const columns: MsTableColumn = [
{
title: 'project.menu.name',
dataIndex: 'module',
slotName: 'module',
},
{
title: 'project.menu.description',
slotName: 'description',
dataIndex: 'description',
showDrag: true,
},
{
title: 'common.operation',
slotName: 'moduleEnable',
dataIndex: 'moduleEnable',
fixed: 'right',
width: 150,
},
];
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(postTabletList, {
showPagination: false,
columns,
selectable: false,
scroll: { x: '100%' },
noDisable: true,
});
const getMenuIcon = (type: MenuEnum) => {
switch (type) {
case MenuEnum.workstation:
return 'icon-icon_pc_filled';
case MenuEnum.testPlan:
return 'icon-icon_test_plan_filled';
case MenuEnum.bugManagement:
return 'icon-icon_defect';
case MenuEnum.caseManagement:
return 'icon_functional_testing';
case MenuEnum.apiTest:
return 'icon-icon_api-test-filled';
case MenuEnum.uiTest:
return 'icon-icon_ui-test-filled';
default:
return 'icon_performance-test-filled';
}
};
const handleMenuStatusChange = async (record: MenuTableListItem) => {
try {
await postUpdateMenu({
projectId: currentProjectId.value,
type: record.module as MenuEnum,
typeValue: record.moduleEnable,
});
Message.success(t('common.operationSuccess'));
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
};
const fetchData = async () => {
await loadList();
};
onMounted(() => {
setLoadListParams({ projectId: currentProjectId.value });
fetchData();
});
</script>
<style scoped></style>

View File

@ -0,0 +1,3 @@
export default {
'project.menu.management': 'Add Member',
};

View File

@ -0,0 +1,7 @@
export default {
'project.menu.management': '菜单管理',
'project.menu.manageTip':
'可根据使用场景配置各功能开关关闭后,将隐藏功能入口,成员无法访问该功能和数据;已产生的数据不够此规则影响;再次开启时,即恢复至关闭前状态',
'project.menu.name': '菜单名称',
'project.menu.description': '描述',
};

View File

@ -93,6 +93,9 @@
</template>
<script setup lang="ts">
/**
* @description 项目管理-项目与权限-用户组
*/
import { ref, onMounted, computed, provide } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';

View File

@ -56,6 +56,9 @@
</template>
<script lang="ts" setup>
/**
* @description 系统设置-组织-项目
*/
import { useI18n } from '@/hooks/useI18n';
import useTable from '@/components/pure/ms-table/useTable';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';

View File

@ -7,12 +7,12 @@
</div>
</Transition>
<Transition>
<div class="usergroup-collapse" :style="leftCollapse ? 'left: 300px' : 'left: 0'" @click="handleCollapse">
<div class="usergroup-collapse" :style="{ left: leftCollapse ? '300px' : '0' }" @click="handleCollapse">
<icon-double-left v-if="leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
<icon-double-right v-else class="text-[12px] text-[var(--color-text-brand)]" />
</div>
</Transition>
<div class="w-[100%] p-[24px]">
<div class="p-[24px]" :style="{ width: leftCollapse ? 'calc(100% - 300px)' : '100%' }">
<div class="flex flex-row items-center justify-between">
<a-tooltip :content="currentUserGroupItem.name">
<div class="one-line-text max-w-[300px]">{{ currentUserGroupItem.name }}</div>
@ -57,6 +57,9 @@
</template>
<script lang="ts" setup>
/**
* @description 系统设置-组织-用户组
*/
import { ref, computed, watchEffect, nextTick, provide, onMounted } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import UserGroupLeft from '@/components/business/ms-user-group-comp/msUserGroupLeft.vue';
@ -171,6 +174,7 @@
overflow-x: hidden;
overflow-y: auto;
padding-right: 6px;
width: 300px;
min-width: 300px;
height: 100%;
border-right: 1px solid var(--color-border-1);

View File

@ -34,6 +34,9 @@
</template>
<script lang="ts" setup>
/**
* @description 系统设置-系统-组织与项目
*/
import { ref, watch, nextTick, onBeforeMount } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import MsCard from '@/components/pure/ms-card/index.vue';

View File

@ -267,7 +267,7 @@
slotName: 'action',
dataIndex: 'operation',
fixed: 'right',
width: 110,
width: 100,
},
];
const tableStore = useTableStore();
@ -280,6 +280,7 @@
size: 'default',
selectable: true,
pageSimple: true,
showSetting: true,
},
(record) => ({
...record,

View File

@ -7,12 +7,12 @@
</div>
</Transition>
<Transition>
<div class="usergroup-collapse" :style="leftCollapse ? 'left: 300px' : 'left: 0'" @click="handleCollapse">
<div class="usergroup-collapse" :style="{ left: leftCollapse ? '300px' : '0' }" @click="handleCollapse">
<icon-double-left v-if="leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
<icon-double-right v-if="!leftCollapse" class="text-[12px] text-[var(--color-text-brand)]" />
</div>
</Transition>
<div class="w-[100%] p-[24px]">
<div class="p-[24px]" :style="{ width: leftCollapse ? 'calc(100% - 300px)' : '100%' }">
<div class="flex flex-row items-center justify-between">
<a-tooltip :content="currentUserGroupItem.name">
<div class="one-line-text max-w-[300px]">{{ currentUserGroupItem.name }}</div>
@ -57,6 +57,9 @@
</template>
<script lang="ts" setup>
/**
* @description 系统设置-系统-用户组
*/
import { ref, computed, watchEffect, nextTick, provide, onMounted } from 'vue';
import { useI18n } from '@/hooks/useI18n';
import UserGroupLeft from '@/components/business/ms-user-group-comp/msUserGroupLeft.vue';
@ -171,6 +174,7 @@
overflow-x: hidden;
overflow-y: auto;
padding-right: 6px;
width: 300px;
min-width: 300px;
height: 100%;
border-right: 1px solid var(--color-border-1);