mirror of
https://gitee.com/fit2cloud-feizhiyun/MeterSphere.git
synced 2024-12-03 04:28:51 +08:00
fix(all): 修复bugs
This commit is contained in:
parent
e3864bf760
commit
5714a1275a
@ -2,6 +2,8 @@ import { Message, Modal } from '@arco-design/web-vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useUser from '@/hooks/useUser';
|
||||
import router from '@/router';
|
||||
import { NO_RESOURCE_ROUTE_NAME } from '@/router/constants';
|
||||
|
||||
import type { ErrorMessageMode } from '#/axios';
|
||||
|
||||
@ -22,7 +24,9 @@ export default function checkStatus(status: number, msg: string, errorMessageMod
|
||||
break;
|
||||
}
|
||||
case 403:
|
||||
errMessage = msg || t('api.errMsg403');
|
||||
if (router.currentRoute.value.name !== NO_RESOURCE_ROUTE_NAME) {
|
||||
router.push({ name: NO_RESOURCE_ROUTE_NAME });
|
||||
}
|
||||
break;
|
||||
// 404请求不存在
|
||||
case 404:
|
||||
|
@ -424,6 +424,9 @@
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.arco-icon-hover::before {
|
||||
@apply hidden;
|
||||
}
|
||||
}
|
||||
.arco-checkbox-indeterminate .arco-checkbox-icon {
|
||||
border: 1px solid rgba(var(--primary-7)) !important;
|
||||
|
@ -76,6 +76,7 @@
|
||||
:max-tag-count="1"
|
||||
:size="props.inputSize"
|
||||
readonly
|
||||
no-tooltip
|
||||
>
|
||||
<template v-if="alreadyDeleteFiles.length > 0" #prefix>
|
||||
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />
|
||||
|
@ -363,9 +363,9 @@
|
||||
import { TableColumnData, TableData } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { EQUAL, statusCodeOptions } from '@/components/pure/ms-advance-filter';
|
||||
import { statusCodeOptions } from '@/components/pure/ms-advance-filter';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import { TableOperationColumn } from '../../ms-user-group-comp/authTable.vue';
|
||||
import { TableOperationColumn } from '@/components/business/ms-user-group-comp/authTable.vue';
|
||||
import fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
|
||||
import moreSetting from '@/views/api-test/components/fastExtraction/moreSetting.vue';
|
||||
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
|
||||
|
@ -206,6 +206,7 @@ export default defineComponent(
|
||||
typeof props.optionLabelRender === 'function'
|
||||
? props.optionLabelRender(item)
|
||||
: item[props.labelKey || 'label'],
|
||||
class: 'one-line-text',
|
||||
});
|
||||
};
|
||||
|
||||
@ -276,7 +277,7 @@ export default defineComponent(
|
||||
const _slots: MsSearchSelectSlots = {
|
||||
default: () =>
|
||||
filterOptions.value.map((item) => (
|
||||
<a-tooltip content={item.tooltipContent} mouse-enter-delay={500}>
|
||||
<a-tooltip content={item.tooltipContent} mouse-enter-delay={500} position="bl">
|
||||
<a-option
|
||||
key={item[props.valueKey || 'value']}
|
||||
value={props.objectValue ? item : item[props.valueKey || 'value']}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex h-full flex-col gap-[24px] overflow-hidden">
|
||||
<div class="group-auth-table">
|
||||
<a-table
|
||||
:span-method="dataSpanMethod"
|
||||
@ -77,7 +77,6 @@
|
||||
saveOrgUSetting,
|
||||
} from '@/api/modules/setting/usergroup';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useUserStore } from '@/store';
|
||||
|
||||
import {
|
||||
type AuthScopeType,
|
||||
@ -117,12 +116,11 @@
|
||||
scroll() {
|
||||
return {
|
||||
x: '800px',
|
||||
y: 'calc(100vh - 264px)',
|
||||
y: '100%',
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
const userStore = useUserStore();
|
||||
const systemType = inject<AuthScopeEnum>('systemType');
|
||||
|
||||
const loading = ref(false);
|
||||
@ -406,8 +404,9 @@
|
||||
// 初始化数据
|
||||
const initData = async (id: string) => {
|
||||
try {
|
||||
let res: UserGroupAuthSetting[] = [];
|
||||
loading.value = true;
|
||||
tableData.value = []; // 重置数据,可以使表格滚动条重新计算
|
||||
let res: UserGroupAuthSetting[] = [];
|
||||
if (systemType === AuthScopeEnum.SYSTEM) {
|
||||
res = await getGlobalUSetting(id);
|
||||
} else if (systemType === AuthScopeEnum.ORGANIZATION) {
|
||||
@ -415,7 +414,6 @@
|
||||
} else {
|
||||
res = await getAuthByUserGroup(id);
|
||||
}
|
||||
|
||||
tableData.value = transformData(res);
|
||||
handleAllChange(true);
|
||||
} catch (error) {
|
||||
@ -488,16 +486,20 @@
|
||||
|
||||
<style scoped lang="less">
|
||||
.group-auth-table {
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
@apply flex-1 overflow-hidden;
|
||||
|
||||
padding: 0 24px;
|
||||
:deep(.arco-table-container) {
|
||||
border-top: 1px solid var(--color-text-n8) !important;
|
||||
border-right: 1px solid var(--color-text-n8) !important;
|
||||
border-left: 1px solid var(--color-text-n8) !important;
|
||||
}
|
||||
:deep(.arco-table-th-title) {
|
||||
width: 100%;
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-text-n9);
|
||||
line-height: normal;
|
||||
}
|
||||
:deep(.arco-checkbox-indeterminate) {
|
||||
.arco-checkbox-icon {
|
||||
border-color: rgb(var(--primary-5));
|
||||
@ -506,8 +508,6 @@
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
@apply absolute bottom-0 left-0 w-full;
|
||||
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 24px;
|
||||
|
@ -70,6 +70,13 @@
|
||||
>
|
||||
</a-tooltip>
|
||||
<div
|
||||
v-if="
|
||||
element.type === systemType ||
|
||||
(isSystemShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
systemMoreAction.length > 0)
|
||||
"
|
||||
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
|
||||
:class="{ '!opacity-100': element.id === currentId }"
|
||||
>
|
||||
@ -83,7 +90,12 @@
|
||||
/>
|
||||
</div>
|
||||
<MsMoreAction
|
||||
v-if="isSystemShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)"
|
||||
v-if="
|
||||
isSystemShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
systemMoreAction.length > 0
|
||||
"
|
||||
:list="systemMoreAction"
|
||||
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.SYSTEM)"
|
||||
>
|
||||
@ -160,6 +172,13 @@
|
||||
>
|
||||
</a-tooltip>
|
||||
<div
|
||||
v-if="
|
||||
element.type === systemType ||
|
||||
(isOrdShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
orgMoreAction.length > 0)
|
||||
"
|
||||
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
|
||||
:class="{ '!opacity-100': element.id === currentId }"
|
||||
>
|
||||
@ -173,7 +192,12 @@
|
||||
/>
|
||||
</div>
|
||||
<MsMoreAction
|
||||
v-if="isOrdShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)"
|
||||
v-if="
|
||||
isOrdShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
orgMoreAction.length > 0
|
||||
"
|
||||
:list="orgMoreAction"
|
||||
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.ORGANIZATION)"
|
||||
>
|
||||
@ -250,6 +274,13 @@
|
||||
>
|
||||
</a-tooltip>
|
||||
<div
|
||||
v-if="
|
||||
element.type === systemType ||
|
||||
(isProjectShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
projectMoreAction.length > 0)
|
||||
"
|
||||
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
|
||||
:class="{ '!opacity-100': element.id === currentId }"
|
||||
>
|
||||
@ -263,7 +294,12 @@
|
||||
/>
|
||||
</div>
|
||||
<MsMoreAction
|
||||
v-if="isProjectShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)"
|
||||
v-if="
|
||||
isProjectShowAll &&
|
||||
!element.internal &&
|
||||
(element.scopeId !== 'global' || !isGlobalDisable) &&
|
||||
projectMoreAction.length > 0
|
||||
"
|
||||
:list="projectMoreAction"
|
||||
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.PROJECT)"
|
||||
>
|
||||
@ -335,7 +371,6 @@
|
||||
|
||||
const currentItem = ref<CurrentUserGroupItem>({ id: '', name: '', internal: false, type: AuthScopeEnum.SYSTEM });
|
||||
const currentId = ref('');
|
||||
const currentName = computed(() => currentItem.value.name);
|
||||
|
||||
const userModalVisible = ref(false);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="px-[24px]">
|
||||
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
|
||||
<MsBaseTable v-bind="propsRes" v-on="propsEvent">
|
||||
<template v-if="hasAnyPermission(props.updatePermission || [])" #quickCreate>
|
||||
<MsConfirmUserSelector :ok-loading="okLoading" v-bind="userSelectorProps" @confirm="handleAddMember" />
|
||||
</template>
|
||||
|
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<a-tooltip :content="allTagText" :disabled="(innerModelValue || []).length === 0" :mouse-enter-delay="300">
|
||||
<a-tooltip
|
||||
:content="allTagText"
|
||||
:disabled="props.noTooltip || (innerModelValue || []).length === 0"
|
||||
:mouse-enter-delay="300"
|
||||
>
|
||||
<div :class="`flex w-full items-center ${props.class}`">
|
||||
<a-input-tag
|
||||
v-model:model-value="innerModelValue"
|
||||
@ -59,6 +63,7 @@
|
||||
inputClass?: string;
|
||||
size?: 'small' | 'large' | 'medium' | 'mini';
|
||||
disabled?: boolean;
|
||||
noTooltip?: boolean;
|
||||
}>(),
|
||||
{
|
||||
retainInputValue: true,
|
||||
@ -68,6 +73,7 @@
|
||||
class: '',
|
||||
inputClass: '',
|
||||
size: 'medium',
|
||||
noTooltip: false,
|
||||
}
|
||||
);
|
||||
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear', 'blur', 'click']);
|
||||
|
@ -88,7 +88,7 @@ export default function useSelect(config: UseSelectOption) {
|
||||
|
||||
onMounted(() => {
|
||||
if (config.selectRef.value) {
|
||||
selectWidth.value = config.selectRef.value.$el.nextElementSibling.getBoundingClientRect().width;
|
||||
selectWidth.value = config.selectRef.value.$el.nextElementSibling.clientWidth;
|
||||
selectViewInner.value = config.selectRef.value.$el.nextElementSibling.querySelector('.arco-select-view-inner');
|
||||
}
|
||||
});
|
||||
|
@ -728,6 +728,7 @@ interface ParsedCurlOptions {
|
||||
url?: string;
|
||||
queryParameters?: { key: string; value: string }[];
|
||||
headers?: { key: string; value: string }[];
|
||||
method?: string;
|
||||
}
|
||||
/**
|
||||
* 解析 curl 脚本
|
||||
@ -740,16 +741,21 @@ export function parseCurlScript(curlScript: string): ParsedCurlOptions {
|
||||
const [_, url] = curlScript.match(/curl\s+'([^']+)'/) || [];
|
||||
if (url) {
|
||||
options.url = url;
|
||||
// 提取 query 参数
|
||||
const queryParams = url
|
||||
.split('?')[1]
|
||||
?.split('&')
|
||||
.map((param) => {
|
||||
const [key, value] = param.split('=');
|
||||
return { key, value };
|
||||
});
|
||||
options.queryParameters = queryParams || [];
|
||||
}
|
||||
|
||||
// 提取 query 参数
|
||||
const queryMatch = curlScript.match(/\?(.*?)'/);
|
||||
if (queryMatch) {
|
||||
const queryParams = queryMatch[1].split('&').map((param) => {
|
||||
const [key, value] = param.split('=');
|
||||
return { key, value };
|
||||
});
|
||||
options.queryParameters = queryParams;
|
||||
// 提取请求方式
|
||||
const [, method] = curlScript.match(/-X\s+'([^']+)'/) || [];
|
||||
if (method) {
|
||||
options.method = method;
|
||||
}
|
||||
|
||||
// 提取 header
|
||||
|
@ -821,9 +821,11 @@
|
||||
() => props.params,
|
||||
(arr) => {
|
||||
if (arr.length > 0) {
|
||||
let hasNoIdItem = false;
|
||||
paramsData.value = arr.map((item, i) => {
|
||||
if (!item) {
|
||||
// 批量添加过来的数据最后一行会是 undefined
|
||||
hasNoIdItem = true;
|
||||
return {
|
||||
...cloneDeep(props.defaultParamItem),
|
||||
id: new Date().getTime() + i,
|
||||
@ -831,6 +833,7 @@
|
||||
}
|
||||
if (!item.id) {
|
||||
// 后台存储无id,渲染时需要手动添加一次
|
||||
hasNoIdItem = true;
|
||||
return {
|
||||
...item,
|
||||
id: new Date().getTime() + i,
|
||||
@ -839,8 +842,10 @@
|
||||
return item;
|
||||
});
|
||||
const lastTwoIsSame =
|
||||
arr.length >= 2 && filterKeyValParams([arr[arr.length - 2]], arr[arr.length - 1]).lastDataIsDefault;
|
||||
arr.length === 1 ||
|
||||
(arr.length >= 2 && filterKeyValParams([arr[arr.length - 2]], arr[arr.length - 1]).lastDataIsDefault);
|
||||
if (
|
||||
hasNoIdItem &&
|
||||
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
|
||||
!props.isTreeTable &&
|
||||
!lastTwoIsSame // 为了判断最后俩行是否一致(因为下拉框切换会新增一行一样的数据,此时最后一条数据与默认数据是不一样的)
|
||||
|
@ -274,9 +274,10 @@
|
||||
}
|
||||
|
||||
function handleCurlImportConfirm() {
|
||||
const { url, headers, queryParameters } = parseCurlScript(curlCode.value);
|
||||
const { url, headers, queryParameters, method } = parseCurlScript(curlCode.value);
|
||||
addDebugTab({
|
||||
url,
|
||||
method: method?.toUpperCase() || RequestMethods.GET,
|
||||
headers:
|
||||
headers?.map((e) => ({
|
||||
contentType: RequestContentTypeEnum.TEXT,
|
||||
@ -348,6 +349,9 @@
|
||||
[activeDebug.value] = debugTabs.value;
|
||||
}
|
||||
}
|
||||
if (debugTabs.value.length === 0) {
|
||||
addDebugTab();
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -116,7 +116,7 @@
|
||||
|
||||
import type { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
|
||||
import type { ApiScenarioTableItem } from '@/models/apiTest/scenario';
|
||||
import { ScenarioStepRefType } from '@/enums/apiEnum';
|
||||
import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
|
||||
|
||||
export interface ImportData {
|
||||
api: MsTableDataItem<ApiDefinitionDetail>[];
|
||||
@ -261,11 +261,25 @@
|
||||
// 复制需要递归给每个节点生成新的uniqueId,并记录copyFromStepId
|
||||
fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => {
|
||||
const id = getGenerateId();
|
||||
if (
|
||||
node.parent &&
|
||||
node.parent.stepType === ScenarioStepType.API_SCENARIO &&
|
||||
[ScenarioStepRefType.REF, ScenarioStepRefType.PARTIAL_REF].includes(node.parent.refType)
|
||||
) {
|
||||
// 如果根节点是引用场景
|
||||
node.isQuoteScenarioStep = true; // 标记为引用场景下的子步骤
|
||||
node.isRefScenarioStep = node.parent.refType === ScenarioStepRefType.REF; // 标记为完全引用场景
|
||||
node.draggable = false; // 引用场景下的任何步骤不可拖拽
|
||||
} else if (node.parent) {
|
||||
// 如果有父节点
|
||||
node.isQuoteScenarioStep = node.parent.isQuoteScenarioStep; // 复用父节点的引用场景标记
|
||||
node.isRefScenarioStep = node.parent.isRefScenarioStep; // 复用父节点的是否完全引用场景标记
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
copyFromStepId: node.id,
|
||||
originProjectId: node.projectId,
|
||||
id,
|
||||
id: node.stepType === ScenarioStepType.API_SCENARIO ? id : node.id, // 引用场景节点生成新的步骤 id
|
||||
uniqueId: id,
|
||||
};
|
||||
});
|
||||
|
@ -407,7 +407,13 @@
|
||||
<a-button type="secondary" :disabled="scheduleModalLoading" @click="cancelScheduleModal">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button class="ml-3" type="primary" :loading="scheduleModalLoading" @click="saveScheduleModal">
|
||||
<a-button
|
||||
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||
class="ml-3"
|
||||
type="primary"
|
||||
:loading="scheduleModalLoading"
|
||||
@click="saveScheduleModal"
|
||||
>
|
||||
{{ t('common.save') }}
|
||||
</a-button>
|
||||
</div>
|
||||
|
@ -711,6 +711,9 @@
|
||||
|
||||
function setStepMoreAction(items: ActionsItem[], node: MsTreeNodeData) {
|
||||
const _stepType = getStepType(node as ScenarioStepItem);
|
||||
if ((node as ScenarioStepItem).isQuoteScenarioStep) {
|
||||
return [];
|
||||
}
|
||||
if ((node as ScenarioStepItem).stepType === ScenarioStepType.CUSTOM_REQUEST) {
|
||||
// 自定义请求
|
||||
return [
|
||||
@ -746,9 +749,6 @@
|
||||
},
|
||||
];
|
||||
}
|
||||
if ((node as ScenarioStepItem).isQuoteScenarioStep) {
|
||||
return [];
|
||||
}
|
||||
if (_stepType.isQuoteApi || _stepType.isCopyApi) {
|
||||
return [
|
||||
{
|
||||
@ -2096,6 +2096,8 @@
|
||||
}
|
||||
.ms-tree-node-extra {
|
||||
@apply !visible !w-auto;
|
||||
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
.ms-form {
|
||||
|
@ -105,10 +105,11 @@
|
||||
:field-config="{
|
||||
field: detail.name,
|
||||
placeholder: t('project.fileManagement.fileNamePlaceholder'),
|
||||
NotNullTip: t('project.fileManagement.fileNameNotNull'),
|
||||
}"
|
||||
:node-id="detail.id"
|
||||
:all-names="[]"
|
||||
@rename-finish="detailDrawerRef?.initDetail"
|
||||
@rename-finish="() => detailDrawerRef?.initDetail()"
|
||||
>
|
||||
<MsButton class="!mr-0 ml-[8px]">
|
||||
{{ t('common.rename') }}
|
||||
@ -132,12 +133,13 @@
|
||||
:field-config="{
|
||||
field: detail.desc,
|
||||
placeholder: t('project.fileManagement.descPlaceholder'),
|
||||
maxLength: 250,
|
||||
maxLength: 1000,
|
||||
isTextArea: true,
|
||||
}"
|
||||
:node-id="detail.id"
|
||||
:all-names="[]"
|
||||
@update-desc-finish="detailDrawerRef?.initDetail"
|
||||
no-rule
|
||||
@update-desc-finish="() => detailDrawerRef?.initDetail()"
|
||||
>
|
||||
<MsButton class="ml-[8px]"> <MsIcon type="icon-icon_edit_outlined" /></MsButton>
|
||||
</popConfirm>
|
||||
@ -170,7 +172,6 @@
|
||||
v-bind="caseTableProps"
|
||||
:data="caseList"
|
||||
no-disable
|
||||
:action-config="caseBatchActions"
|
||||
v-on="caseTableEvent"
|
||||
>
|
||||
<template #id="{ record }">
|
||||
@ -403,8 +404,7 @@
|
||||
title: 'project.fileManagement.caseType',
|
||||
dataIndex: 'sourceType',
|
||||
slotName: 'sourceType',
|
||||
width: 100,
|
||||
showTooltip: true,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.caseFileVersion',
|
||||
@ -428,20 +428,20 @@
|
||||
scroll: { x: 800 },
|
||||
columns: caseColumns,
|
||||
heightUsed: 200,
|
||||
selectable: true,
|
||||
showSelectAll: true,
|
||||
selectable: false,
|
||||
showSelectAll: false,
|
||||
showPagination: false,
|
||||
});
|
||||
|
||||
const caseBatchActions = {
|
||||
baseAction: [
|
||||
{
|
||||
label: 'project.fileManagement.updateCaseFile',
|
||||
eventTag: 'updateCaseFile',
|
||||
permission: ['PROJECT_FILE_MANAGEMENT:READ+UPDATE'],
|
||||
},
|
||||
],
|
||||
};
|
||||
// const caseBatchActions = {
|
||||
// baseAction: [
|
||||
// {
|
||||
// label: 'project.fileManagement.updateCaseFile',
|
||||
// eventTag: 'updateCaseFile',
|
||||
// permission: ['PROJECT_FILE_MANAGEMENT:READ+UPDATE'],
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
const keyword = ref('');
|
||||
const caseList = computed(() => caseTableProps.value.data.filter((item) => item.sourceName.includes(keyword.value)));
|
||||
|
@ -20,7 +20,14 @@
|
||||
<a-form-item
|
||||
class="hidden-item"
|
||||
field="field"
|
||||
:rules="[{ required: true, message: t('project.fileManagement.nameNotNull') }, { validator: validateName }]"
|
||||
:rules="
|
||||
props.noRule
|
||||
? []
|
||||
: [
|
||||
{ required: true, message: props.fieldConfig?.NotNullTip || t('project.fileManagement.nameNotNull') },
|
||||
{ validator: validateName },
|
||||
]
|
||||
"
|
||||
>
|
||||
<a-textarea
|
||||
v-if="props.fieldConfig?.isTextArea"
|
||||
@ -68,6 +75,7 @@
|
||||
placeholder?: string;
|
||||
maxLength?: number;
|
||||
isTextArea?: boolean;
|
||||
NotNullTip?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
@ -79,6 +87,7 @@
|
||||
fieldConfig?: FieldConfig;
|
||||
parentId?: string; // 父节点 id
|
||||
nodeId?: string; // 节点 id
|
||||
noRule?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'close', 'addFinish', 'renameFinish', 'updateDescFinish']);
|
||||
|
@ -227,7 +227,13 @@
|
||||
},
|
||||
});
|
||||
} else {
|
||||
initModulesCount(tableFilterParams.value);
|
||||
initModulesCount({
|
||||
...tableFilterParams.value,
|
||||
combine: {
|
||||
...tableFilterParams.value.combine,
|
||||
storage: 'minio',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ export default {
|
||||
'project.fileManagement.addStorage': 'Add repository',
|
||||
'project.fileManagement.rename': 'Rename',
|
||||
'project.fileManagement.nameNotNull': 'Module name cannot be empty',
|
||||
'project.fileManagement.fileNameNotNull': 'File name cannot be empty',
|
||||
'project.fileManagement.namePlaceholder': 'Please enter the module name and press Enter to save',
|
||||
'project.fileManagement.renameSuccess': 'Rename successful',
|
||||
'project.fileManagement.updateDescSuccess': 'File description updated successfully',
|
||||
|
@ -8,6 +8,7 @@ export default {
|
||||
'project.fileManagement.addStorage': '添加存储库',
|
||||
'project.fileManagement.rename': '重命名',
|
||||
'project.fileManagement.nameNotNull': '模块名称不能为空',
|
||||
'project.fileManagement.fileNameNotNull': '文件名称不能为空',
|
||||
'project.fileManagement.namePlaceholder': '请输入模块名称,按回车键保存',
|
||||
'project.fileManagement.renameSuccess': '重命名成功',
|
||||
'project.fileManagement.updateDescSuccess': '文件描述更新成功',
|
||||
|
@ -1,52 +1,81 @@
|
||||
<template>
|
||||
<MsBaseTable
|
||||
v-bind="propsRes"
|
||||
:action-config="tableBatchActions"
|
||||
@selected-change="handleTableSelect"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #userRole="{ record }">
|
||||
<MsTagGroup
|
||||
v-if="!record.showUserSelect"
|
||||
:tag-list="record.userRoles || []"
|
||||
type="primary"
|
||||
theme="outline"
|
||||
@click="changeUser(record)"
|
||||
/>
|
||||
<a-select
|
||||
v-else
|
||||
v-model="record.selectUserList"
|
||||
:popup-visible="record.showUserSelect"
|
||||
multiple
|
||||
class="w-[260px]"
|
||||
:max-tag-count="2"
|
||||
@popup-visible-change="(value) => userGroupChange(value, record)"
|
||||
>
|
||||
<a-option v-for="item of userGroupOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template #enable="{ record }">
|
||||
<div v-if="record.enable" class="flex items-center">
|
||||
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('organization.member.statusEnable') }}
|
||||
<div>
|
||||
<div class="mb-4 grid grid-cols-4 gap-2">
|
||||
<div class="col-span-2">
|
||||
<a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
|
||||
{{ t('project.member.addMember') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div v-else class="flex items-center text-[var(--color-text-4)]">
|
||||
<MsIcon type="icon-icon_disable" class="mr-[2px]" />
|
||||
{{ t('organization.member.statusDisable') }}
|
||||
<div>
|
||||
<a-select v-model="roleIds" @change="changeSelect">
|
||||
<a-option v-for="item of userGroupAll" :key="item.id" :value="item.id">{{ t(item.name) }}</a-option>
|
||||
<template #prefix>
|
||||
<span>{{ t('project.member.tableColumnUserGroup') }}</span>
|
||||
</template>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsRemoveButton
|
||||
v-permission="['PROJECT_USER:READ+DELETE']"
|
||||
position="br"
|
||||
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
||||
:sub-title-tip="t('project.member.subTitle')"
|
||||
:loading="deleteLoading"
|
||||
@ok="removeMember(record)"
|
||||
/>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<div>
|
||||
<a-input-search
|
||||
v-model="keyword"
|
||||
:max-length="255"
|
||||
:placeholder="t('project.member.searchMember')"
|
||||
allow-clear
|
||||
@search="searchHandler"
|
||||
@press-enter="searchHandler"
|
||||
@clear="searchHandler"
|
||||
>
|
||||
</a-input-search>
|
||||
</div>
|
||||
</div>
|
||||
<MsBaseTable
|
||||
v-bind="propsRes"
|
||||
:action-config="tableBatchActions"
|
||||
@selected-change="handleTableSelect"
|
||||
v-on="propsEvent"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #userRole="{ record }">
|
||||
<MsTagGroup
|
||||
v-if="!record.showUserSelect"
|
||||
:tag-list="record.userRoles || []"
|
||||
type="primary"
|
||||
theme="outline"
|
||||
@click="changeUser(record)"
|
||||
/>
|
||||
<a-select
|
||||
v-else
|
||||
v-model="record.selectUserList"
|
||||
:popup-visible="record.showUserSelect"
|
||||
multiple
|
||||
class="w-[260px]"
|
||||
:max-tag-count="2"
|
||||
@popup-visible-change="(value) => userGroupChange(value, record)"
|
||||
>
|
||||
<a-option v-for="item of userGroupOptions" :key="item.id" :value="item.id">{{ item.name }}</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template #enable="{ record }">
|
||||
<div v-if="record.enable" class="flex items-center">
|
||||
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('organization.member.statusEnable') }}
|
||||
</div>
|
||||
<div v-else class="flex items-center text-[var(--color-text-4)]">
|
||||
<MsIcon type="icon-icon_disable" class="mr-[2px]" />
|
||||
{{ t('organization.member.statusDisable') }}
|
||||
</div>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<MsRemoveButton
|
||||
v-permission="['PROJECT_USER:READ+DELETE']"
|
||||
position="br"
|
||||
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })"
|
||||
:sub-title-tip="t('project.member.subTitle')"
|
||||
:loading="deleteLoading"
|
||||
@ok="removeMember(record)"
|
||||
/>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
</div>
|
||||
<AddMemberModal
|
||||
ref="projectMemberRef"
|
||||
v-model:visible="addMemberVisible"
|
||||
@ -95,12 +124,6 @@
|
||||
} from '@/models/projectManagement/projectAndPermission';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
roleIds: string;
|
||||
userGroupOptions: ProjectUserOption[];
|
||||
keyword: string;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const { openModal } = useModal();
|
||||
@ -193,18 +216,43 @@
|
||||
}
|
||||
);
|
||||
|
||||
const userGroupAll = ref<ProjectUserOption[]>([]);
|
||||
const userGroupOptions = ref<ProjectUserOption[]>([]);
|
||||
|
||||
const initOptions = async () => {
|
||||
userGroupOptions.value = await getProjectUserGroup(appStore.currentProjectId);
|
||||
userGroupAll.value = [
|
||||
{
|
||||
id: '',
|
||||
name: '全部',
|
||||
},
|
||||
...userGroupOptions.value,
|
||||
];
|
||||
};
|
||||
|
||||
const roleIds = ref<string>('');
|
||||
const keyword = ref<string>('');
|
||||
|
||||
const initData = async () => {
|
||||
await nextTick();
|
||||
setLoadListParams({
|
||||
filter: {
|
||||
roleIds: props.roleIds ? [props.roleIds] : [],
|
||||
roleIds: roleIds.value ? [roleIds.value] : [],
|
||||
},
|
||||
projectId: lastProjectId.value,
|
||||
keyword: props.keyword,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
await loadList();
|
||||
};
|
||||
|
||||
const searchHandler = () => {
|
||||
initData();
|
||||
};
|
||||
|
||||
const changeSelect = () => {
|
||||
initData();
|
||||
};
|
||||
|
||||
// 跨页选择项
|
||||
const selectData = ref<string[] | undefined>([]);
|
||||
|
||||
@ -250,6 +298,7 @@
|
||||
resetSelector();
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
deleteLoading.value = false;
|
||||
@ -276,9 +325,9 @@
|
||||
selectAll: !!selectAll,
|
||||
excludeIds: excludeIds || [],
|
||||
selectIds: selectedIds || [],
|
||||
keyword: props.keyword,
|
||||
keyword: keyword.value,
|
||||
condition: {
|
||||
keyword: props.keyword,
|
||||
keyword: keyword.value,
|
||||
filter: propsRes.value.filter,
|
||||
combine: batchParams.value.condition,
|
||||
},
|
||||
@ -288,6 +337,7 @@
|
||||
initData();
|
||||
resetSelector();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
@ -328,6 +378,7 @@
|
||||
record.showUserSelect = false;
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
@ -360,15 +411,11 @@
|
||||
editProjectMember(record);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
onBeforeMount(async () => {
|
||||
await initOptions();
|
||||
initData();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
initData,
|
||||
addMember,
|
||||
});
|
||||
|
||||
await tableStore.initColumn(TableKeyEnum.PROJECT_MEMBER, columns, 'drawer');
|
||||
</script>
|
||||
|
||||
|
@ -1,87 +1,12 @@
|
||||
<template>
|
||||
<div class="mb-4 grid grid-cols-4 gap-2">
|
||||
<div class="col-span-2">
|
||||
<a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
|
||||
{{ t('project.member.addMember') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<div>
|
||||
<a-select v-model="roleIds" @change="changeSelect">
|
||||
<a-option v-for="item of userGroupAll" :key="item.id" :value="item.id">{{ t(item.name) }}</a-option>
|
||||
<template #prefix
|
||||
><span>{{ t('project.member.tableColumnUserGroup') }}</span></template
|
||||
>
|
||||
</a-select></div
|
||||
>
|
||||
<div>
|
||||
<a-input-search
|
||||
v-model="keyword"
|
||||
:max-length="255"
|
||||
:placeholder="t('project.member.searchMember')"
|
||||
allow-clear
|
||||
@search="searchHandler"
|
||||
@press-enter="searchHandler"
|
||||
@clear="searchHandler"
|
||||
></a-input-search
|
||||
></div>
|
||||
</div>
|
||||
<memberTable
|
||||
v-if="isMounted"
|
||||
ref="memberTableRef"
|
||||
:keyword="keyword"
|
||||
:role-ids="roleIds"
|
||||
:user-group-options="userGroupOptions"
|
||||
/>
|
||||
<memberTable v-if="isMounted" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getProjectUserGroup } from '@/api/modules/project-management/projectMember';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import { ProjectUserOption } from '@/models/projectManagement/projectAndPermission';
|
||||
|
||||
const memberTable = defineAsyncComponent(() => import('./components/memberTable.vue'));
|
||||
|
||||
/**
|
||||
* @description 项目管理-项目与权限-成员
|
||||
*/
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const userGroupAll = ref<ProjectUserOption[]>([]);
|
||||
const userGroupOptions = ref<ProjectUserOption[]>([]);
|
||||
|
||||
const initOptions = async () => {
|
||||
userGroupOptions.value = await getProjectUserGroup(appStore.currentProjectId);
|
||||
userGroupAll.value = [
|
||||
{
|
||||
id: '',
|
||||
name: '全部',
|
||||
},
|
||||
...userGroupOptions.value,
|
||||
];
|
||||
};
|
||||
initOptions();
|
||||
const memberTableRef = ref<InstanceType<typeof memberTable> | null>(null);
|
||||
|
||||
const roleIds = ref<string>('');
|
||||
const keyword = ref<string>('');
|
||||
const initData = async () => {
|
||||
memberTableRef.value?.initData();
|
||||
};
|
||||
|
||||
const searchHandler = () => {
|
||||
initData();
|
||||
};
|
||||
|
||||
const changeSelect = () => {
|
||||
initData();
|
||||
};
|
||||
|
||||
function addMember() {
|
||||
memberTableRef.value?.addMember();
|
||||
}
|
||||
const memberTable = defineAsyncComponent(() => import('./components/memberTable.vue'));
|
||||
|
||||
const isMounted = ref(false);
|
||||
|
||||
|
@ -34,6 +34,14 @@
|
||||
></a-textarea>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.resourcePool.serverUrl')" field="serverUrl" class="form-item">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
{{ t('system.resourcePool.serverUrl') }}
|
||||
<a-tooltip :content="t('system.resourcePool.serverUrlTip')" position="tl" mini>
|
||||
<icon-question-circle class="ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-6))]" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:model-value="form.serverUrl"
|
||||
:placeholder="t('system.resourcePool.rootUrlPlaceholder')"
|
||||
|
@ -26,7 +26,7 @@ export default {
|
||||
'system.resourcePool.deletePoolCancel': 'Cancel',
|
||||
'system.resourcePool.deletePoolSuccess': 'Deleted successfully',
|
||||
'system.resourcePool.detailDesc': 'Description',
|
||||
'system.resourcePool.detailUrl': 'Current site URL',
|
||||
'system.resourcePool.detailUrl': 'Intranet URL',
|
||||
'system.resourcePool.detailRange': 'Applied organization',
|
||||
'system.resourcePool.detailUse': 'Use',
|
||||
'system.resourcePool.detailMirror': 'Mirror',
|
||||
@ -41,7 +41,9 @@ export default {
|
||||
'system.resourcePool.namePlaceholder': 'Please enter a resource pool name',
|
||||
'system.resourcePool.desc': 'Description',
|
||||
'system.resourcePool.descPlaceholder': 'Please describe the resource pool',
|
||||
'system.resourcePool.serverUrl': 'Current site URL',
|
||||
'system.resourcePool.serverUrl': 'Intranet URL',
|
||||
'system.resourcePool.serverUrlTip':
|
||||
'When the resource pool is deployed on the intranet, the intranet address can be used',
|
||||
'system.resourcePool.rootUrlPlaceholder': 'MS deployment address',
|
||||
'system.resourcePool.orgRange': 'Applied organization',
|
||||
'system.resourcePool.orgAll': 'All organization',
|
||||
|
@ -25,7 +25,7 @@ export default {
|
||||
'system.resourcePool.deletePoolCancel': '取消',
|
||||
'system.resourcePool.deletePoolSuccess': '删除成功',
|
||||
'system.resourcePool.detailDesc': '描述',
|
||||
'system.resourcePool.detailUrl': '当前站点 URL',
|
||||
'system.resourcePool.detailUrl': '内网 URL',
|
||||
'system.resourcePool.detailRange': '应用组织',
|
||||
'system.resourcePool.detailUse': '用途',
|
||||
'system.resourcePool.detailMirror': '镜像',
|
||||
@ -40,7 +40,8 @@ export default {
|
||||
'system.resourcePool.namePlaceholder': '请输入资源池名称',
|
||||
'system.resourcePool.desc': '描述',
|
||||
'system.resourcePool.descPlaceholder': '请对该资源池进行描述',
|
||||
'system.resourcePool.serverUrl': '当前站点 URL',
|
||||
'system.resourcePool.serverUrl': '内网 URL',
|
||||
'system.resourcePool.serverUrlTip': '资源池部署在内网时,可走内网地址',
|
||||
'system.resourcePool.rootUrlPlaceholder': 'MS的部署地址',
|
||||
'system.resourcePool.orgRange': '应用组织',
|
||||
'system.resourcePool.orgAll': '全部组织',
|
||||
|
@ -12,7 +12,7 @@
|
||||
/>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="h-full pt-[24px]">
|
||||
<div class="flex h-full flex-col gap-[16px] overflow-hidden pt-[24px]">
|
||||
<div class="flex flex-row items-center justify-between px-[24px]">
|
||||
<a-tooltip :content="currentUserGroupItem.name">
|
||||
<div class="one-line-text max-w-[300px] font-medium">{{ currentUserGroupItem.name }}</div>
|
||||
@ -38,7 +38,7 @@
|
||||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[16px]">
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<UserTable
|
||||
v-if="currentTable === 'user' && couldShowUser"
|
||||
ref="userRef"
|
||||
|
Loading…
Reference in New Issue
Block a user