feat: 批量操作的权限&缺陷管理权限&用户组权限自动勾选查询&解决控制台vite报错
This commit is contained in:
parent
99d294694c
commit
57f3493219
|
@ -76,6 +76,11 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
'process.env': {},
|
'process.env': {},
|
||||||
|
// 定义特性标志
|
||||||
|
'__VUE_OPTIONS_API__': true,
|
||||||
|
'__VUE_PROD_DEVTOOLS__': false,
|
||||||
|
// 设置hydration不匹配详细信息的标志
|
||||||
|
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': true,
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
|
|
|
@ -12,8 +12,13 @@ import type {
|
||||||
} from '@/models/projectManagement/environmental';
|
} from '@/models/projectManagement/environmental';
|
||||||
import { OptionsItem } from '@/models/setting/log';
|
import { OptionsItem } from '@/models/setting/log';
|
||||||
|
|
||||||
export function updateOrAddEnv(data: EnvDetailItem) {
|
export function updateOrAddEnv(data: { request: EnvDetailItem; fileList: FileItem[] }) {
|
||||||
return MSR.post<EnvDetailItem>({ url: data.id ? envURL.updateEnvUrl : envURL.addEnvUrl, data });
|
return MSR.uploadFile<EnvDetailItem>(
|
||||||
|
{ url: data.request.id ? envURL.updateEnvUrl : envURL.addEnvUrl },
|
||||||
|
data,
|
||||||
|
'',
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
export function listEnv(data: { projectId: string; keyword: string }) {
|
export function listEnv(data: { projectId: string; keyword: string }) {
|
||||||
return MSR.post<EnvListItem[]>({ url: envURL.listEnvUrl, data });
|
return MSR.post<EnvListItem[]>({ url: envURL.listEnvUrl, data });
|
||||||
|
|
|
@ -28,7 +28,10 @@
|
||||||
</template>
|
</template>
|
||||||
<template #cell="{ record, rowIndex }">
|
<template #cell="{ record, rowIndex }">
|
||||||
<div class="flex flex-row items-center justify-between">
|
<div class="flex flex-row items-center justify-between">
|
||||||
<a-checkbox-group v-model="record.perChecked" @change="(v) => handleCellAuthChange(v, rowIndex)">
|
<a-checkbox-group
|
||||||
|
:model-value="record.perChecked"
|
||||||
|
@change="(v, e) => handleCellAuthChange(v, rowIndex, record, e)"
|
||||||
|
>
|
||||||
<a-checkbox
|
<a-checkbox
|
||||||
v-for="item in record.permissions"
|
v-for="item in record.permissions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
@ -339,9 +342,32 @@
|
||||||
handleAllChange();
|
handleAllChange();
|
||||||
if (!canSave.value) canSave.value = true;
|
if (!canSave.value) canSave.value = true;
|
||||||
};
|
};
|
||||||
|
// 当选中某个权限值时判断当前选中的列中有没有read权限
|
||||||
|
const setAutoRead = (record: TableData, currentValue: string) => {
|
||||||
|
if (!record.perChecked.includes(currentValue)) {
|
||||||
|
// 如果当前没有选中则执行自动添加查询权限逻辑
|
||||||
|
// 添加权限值
|
||||||
|
record.perChecked.push(currentValue);
|
||||||
|
const preStr = currentValue.split(':')[0];
|
||||||
|
const postStr = currentValue.split(':')[1];
|
||||||
|
const existRead = record.perChecked.some((item: string) => item.split(':')[1] === 'READ');
|
||||||
|
if (!existRead && postStr !== 'READ') {
|
||||||
|
record.perChecked.push(`${preStr}:READ`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 删除权限值
|
||||||
|
record.perChecked.splice(record.perChecked.indexOf(currentValue), 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 表格第三列的复选框change事件
|
// 表格第三列的复选框change事件
|
||||||
const handleCellAuthChange = (values: (string | number | boolean)[], rowIndex: number) => {
|
const handleCellAuthChange = (
|
||||||
|
values: (string | number | boolean)[],
|
||||||
|
rowIndex: number,
|
||||||
|
record: TableData,
|
||||||
|
e: Event
|
||||||
|
) => {
|
||||||
|
setAutoRead(record, (e.target as HTMLInputElement).value);
|
||||||
if (!tableData.value) return;
|
if (!tableData.value) return;
|
||||||
const tmpArr = tableData.value;
|
const tmpArr = tableData.value;
|
||||||
const length = tmpArr[rowIndex].permissions?.length || 0;
|
const length = tmpArr[rowIndex].permissions?.length || 0;
|
||||||
|
|
|
@ -1,26 +1,28 @@
|
||||||
<template>
|
<template>
|
||||||
<a-dropdown :trigger="props.trigger || 'hover'" @select="selectHandler" @popup-visible-change="visibleChange">
|
<span>
|
||||||
<slot>
|
<a-dropdown :trigger="props.trigger || 'hover'" @select="selectHandler" @popup-visible-change="visibleChange">
|
||||||
<MsButton type="text" size="mini" class="more-icon">
|
<slot>
|
||||||
<MsIcon type="icon-icon_more_outlined" size="16" class="text-[var(--color-text-4)]" />
|
<MsButton type="text" size="mini" class="more-icon">
|
||||||
</MsButton>
|
<MsIcon type="icon-icon_more_outlined" size="16" class="text-[var(--color-text-4)]" />
|
||||||
</slot>
|
</MsButton>
|
||||||
<template #content>
|
</slot>
|
||||||
<template v-for="item of props.list">
|
<template #content>
|
||||||
<a-divider v-if="item.isDivider" :key="`${item.label}-divider`" margin="4px" />
|
<template v-for="item of props.list">
|
||||||
<a-doption
|
<a-divider v-if="item.isDivider" :key="`${item.label}-divider`" margin="4px" />
|
||||||
v-else
|
<a-doption
|
||||||
:key="item.label"
|
v-else
|
||||||
:class="item.danger ? 'error-6' : ''"
|
:key="item.label"
|
||||||
:disabled="item.disabled"
|
:class="item.danger ? 'error-6' : ''"
|
||||||
:value="item.eventTag"
|
:disabled="item.disabled"
|
||||||
>
|
:value="item.eventTag"
|
||||||
<MsIcon v-if="item.icon" :type="item.icon" />
|
>
|
||||||
{{ t(item.label || '') }}
|
<MsIcon v-if="item.icon" :type="item.icon" />
|
||||||
</a-doption>
|
{{ t(item.label || '') }}
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</a-dropdown>
|
||||||
</a-dropdown>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<template v-for="(element, idx) in baseAction" :key="element.label">
|
<template v-for="(element, idx) in baseAction" :key="element.label">
|
||||||
<a-divider v-if="element.isDivider" class="divider mx-0 my-[6px]" />
|
<a-divider v-if="element.isDivider" class="divider mx-0 my-[6px]" />
|
||||||
<a-button
|
<a-button
|
||||||
v-if="!element.isDivider && !element.children"
|
v-if="!element.isDivider && !element.children && hasAnyPermission(element.permission as string[])"
|
||||||
class="ml-[12px]"
|
class="ml-[12px]"
|
||||||
:class="{
|
:class="{
|
||||||
'arco-btn-outline--danger': element.danger,
|
'arco-btn-outline--danger': element.danger,
|
||||||
|
@ -15,7 +15,11 @@
|
||||||
>{{ t(element.label as string) }}</a-button
|
>{{ t(element.label as string) }}</a-button
|
||||||
>
|
>
|
||||||
<!-- baseAction多菜单选择 -->
|
<!-- baseAction多菜单选择 -->
|
||||||
<a-dropdown v-if="!element.isDivider && element.children" position="tr" @select="handleSelect">
|
<a-dropdown
|
||||||
|
v-if="!element.isDivider && element.children && hasAnyPermission(element.permission as string[])"
|
||||||
|
position="tr"
|
||||||
|
@select="handleSelect"
|
||||||
|
>
|
||||||
<a-button
|
<a-button
|
||||||
class="ml-[12px]"
|
class="ml-[12px]"
|
||||||
:class="{
|
:class="{
|
||||||
|
@ -43,7 +47,11 @@
|
||||||
<template #content>
|
<template #content>
|
||||||
<template v-for="element in moreAction" :key="element.label">
|
<template v-for="element in moreAction" :key="element.label">
|
||||||
<a-divider v-if="element.isDivider" margin="4px" />
|
<a-divider v-if="element.isDivider" margin="4px" />
|
||||||
<a-doption v-else :value="element" :class="{ delete: element.danger }">
|
<a-doption
|
||||||
|
v-else-if="hasAnyPermission(element.permission as string[])"
|
||||||
|
:value="element"
|
||||||
|
:class="{ delete: element.danger }"
|
||||||
|
>
|
||||||
{{ t(element.label as string) }}
|
{{ t(element.label as string) }}
|
||||||
</a-doption>
|
</a-doption>
|
||||||
</template>
|
</template>
|
||||||
|
@ -59,6 +67,7 @@
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { getNodeWidth } from '@/utils/dom';
|
import { getNodeWidth } from '@/utils/dom';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { BatchActionConfig, BatchActionParams } from './type';
|
import { BatchActionConfig, BatchActionParams } from './type';
|
||||||
import ResizeObserver from 'resize-observer-polyfill';
|
import ResizeObserver from 'resize-observer-polyfill';
|
||||||
|
|
|
@ -127,6 +127,7 @@ export interface BatchActionParams {
|
||||||
isDivider?: boolean;
|
isDivider?: boolean;
|
||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
children?: BatchActionParams[];
|
children?: BatchActionParams[];
|
||||||
|
permission?: string[];
|
||||||
}
|
}
|
||||||
export interface BatchActionConfig {
|
export interface BatchActionConfig {
|
||||||
baseAction: BatchActionParams[];
|
baseAction: BatchActionParams[];
|
||||||
|
|
|
@ -22,7 +22,7 @@ const BugManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/bug-management/index.vue'),
|
component: () => import('@/views/bug-management/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'bugManagement.index',
|
locale: 'bugManagement.index',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_BUG:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -33,7 +33,7 @@ const BugManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/bug-management/edit.vue'),
|
component: () => import('@/views/bug-management/edit.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'bugManagement.editBug',
|
locale: 'bugManagement.editBug',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_BUG:READ+ADD', 'PROJECT_BUG:READ+UPDATE'],
|
||||||
breadcrumbs: [
|
breadcrumbs: [
|
||||||
{
|
{
|
||||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||||
|
@ -54,7 +54,7 @@ const BugManagement: AppRouteRecordRaw = {
|
||||||
component: () => import('@/views/bug-management/recycle.vue'),
|
component: () => import('@/views/bug-management/recycle.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
locale: 'bugManagement.recycle',
|
locale: 'bugManagement.recycle',
|
||||||
roles: ['*'],
|
roles: ['PROJECT_BUG:READ'],
|
||||||
isTopMenu: true,
|
isTopMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,6 +22,9 @@ export function hasPermission(permission: string, typeList: string[]) {
|
||||||
|
|
||||||
// 判断是否有权限
|
// 判断是否有权限
|
||||||
export function hasAnyPermission(permissions: string[], typeList = ['PROJECT', 'ORGANIZATION', 'SYSTEM']) {
|
export function hasAnyPermission(permissions: string[], typeList = ['PROJECT', 'ORGANIZATION', 'SYSTEM']) {
|
||||||
|
if (!permissions || permissions.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return permissions.some((permission) => hasPermission(permission, typeList));
|
return permissions.some((permission) => hasPermission(permission, typeList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const back = () => {
|
const back = () => {
|
||||||
// warning: Go to the node that has the permission
|
// warning: Go to the node that has the permission
|
||||||
router.push({ name: 'workbench' });
|
router.push({ name: 'workstation' });
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -8,8 +8,14 @@
|
||||||
>
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<div class="flex gap-[12px]">
|
<div class="flex gap-[12px]">
|
||||||
<a-button type="primary" @click="handleCreate">{{ t('bugManagement.createBug') }} </a-button>
|
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="primary" @click="handleCreate"
|
||||||
<a-button :disabled="syncBugLoading" type="outline" @click="handleSync"
|
>{{ t('bugManagement.createBug') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-permission="['PROJECT_BUG:READ+IMPORT']"
|
||||||
|
:disabled="syncBugLoading"
|
||||||
|
type="outline"
|
||||||
|
@click="handleSync"
|
||||||
>{{ t('bugManagement.syncBug') }}
|
>{{ t('bugManagement.syncBug') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,11 +46,20 @@
|
||||||
</template>
|
</template>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<div class="flex flex-row flex-nowrap">
|
<div class="flex flex-row flex-nowrap">
|
||||||
<MsButton class="!mr-0" @click="handleCopy(record)">{{ t('common.copy') }}</MsButton>
|
<span v-permission="['PROJECT_BUG:READ+ADD']" class="flex flex-row">
|
||||||
<a-divider direction="vertical" />
|
<MsButton class="!mr-0" @click="handleCopy(record)">{{ t('common.copy') }}</MsButton>
|
||||||
<MsButton class="!mr-0" @click="handleEdit(record)">{{ t('common.edit') }}</MsButton>
|
<a-divider direction="vertical" />
|
||||||
<a-divider direction="vertical" />
|
</span>
|
||||||
<MsTableMoreAction :list="moreActionList" trigger="click" @select="handleMoreActionSelect($event, record)" />
|
<span v-permission="['PROJECT_BUG:READ+UPDATE']" class="flex flex-row">
|
||||||
|
<MsButton class="!mr-0" @click="handleEdit(record)">{{ t('common.edit') }}</MsButton>
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
</span>
|
||||||
|
<MsTableMoreAction
|
||||||
|
v-permission="['PROJECT_BUG:READ+DELETE']"
|
||||||
|
:list="moreActionList"
|
||||||
|
trigger="click"
|
||||||
|
@select="handleMoreActionSelect($event, record)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #empty> </template>
|
<template #empty> </template>
|
||||||
|
@ -364,15 +379,18 @@
|
||||||
{
|
{
|
||||||
label: 'common.export',
|
label: 'common.export',
|
||||||
eventTag: 'export',
|
eventTag: 'export',
|
||||||
|
permission: ['PROJECT_BUG:READ+EXPORT'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'common.edit',
|
label: 'common.edit',
|
||||||
eventTag: 'edit',
|
eventTag: 'edit',
|
||||||
|
permission: ['PROJECT_BUG:READ+UPDATE'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'common.delete',
|
label: 'common.delete',
|
||||||
eventTag: 'delete',
|
eventTag: 'delete',
|
||||||
danger: true,
|
danger: true,
|
||||||
|
permission: ['PROJECT_BUG:READ+DELETE'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
store.currentEnvDetailInfo.mock = true;
|
store.currentEnvDetailInfo.mock = true;
|
||||||
const res = await updateOrAddEnv(store.currentEnvDetailInfo);
|
const res = await updateOrAddEnv({ fileList: [], request: store.currentEnvDetailInfo });
|
||||||
store.currentEnvDetailInfo = res;
|
store.currentEnvDetailInfo = res;
|
||||||
Message.success(t('common.saveSuccess'));
|
Message.success(t('common.saveSuccess'));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
Loading…
Reference in New Issue