fix(all): 修复bugs

This commit is contained in:
baiqi 2024-04-17 15:33:04 +08:00 committed by 刘瑞斌
parent e3864bf760
commit 5714a1275a
27 changed files with 289 additions and 202 deletions

View File

@ -2,6 +2,8 @@ import { Message, Modal } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useUser from '@/hooks/useUser'; import useUser from '@/hooks/useUser';
import router from '@/router';
import { NO_RESOURCE_ROUTE_NAME } from '@/router/constants';
import type { ErrorMessageMode } from '#/axios'; import type { ErrorMessageMode } from '#/axios';
@ -22,7 +24,9 @@ export default function checkStatus(status: number, msg: string, errorMessageMod
break; break;
} }
case 403: case 403:
errMessage = msg || t('api.errMsg403'); if (router.currentRoute.value.name !== NO_RESOURCE_ROUTE_NAME) {
router.push({ name: NO_RESOURCE_ROUTE_NAME });
}
break; break;
// 404请求不存在 // 404请求不存在
case 404: case 404:

View File

@ -424,6 +424,9 @@
@apply hidden; @apply hidden;
} }
} }
.arco-icon-hover::before {
@apply hidden;
}
} }
.arco-checkbox-indeterminate .arco-checkbox-icon { .arco-checkbox-indeterminate .arco-checkbox-icon {
border: 1px solid rgba(var(--primary-7)) !important; border: 1px solid rgba(var(--primary-7)) !important;

View File

@ -76,6 +76,7 @@
:max-tag-count="1" :max-tag-count="1"
:size="props.inputSize" :size="props.inputSize"
readonly readonly
no-tooltip
> >
<template v-if="alreadyDeleteFiles.length > 0" #prefix> <template v-if="alreadyDeleteFiles.length > 0" #prefix>
<icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" /> <icon-exclamation-circle-fill class="!text-[rgb(var(--warning-6))]" :size="18" />

View File

@ -363,9 +363,9 @@
import { TableColumnData, TableData } from '@arco-design/web-vue'; import { TableColumnData, TableData } from '@arco-design/web-vue';
import { cloneDeep } from 'lodash-es'; 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 { 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 fastExtraction from '@/views/api-test/components/fastExtraction/index.vue';
import moreSetting from '@/views/api-test/components/fastExtraction/moreSetting.vue'; import moreSetting from '@/views/api-test/components/fastExtraction/moreSetting.vue';
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue'; import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';

View File

@ -206,6 +206,7 @@ export default defineComponent(
typeof props.optionLabelRender === 'function' typeof props.optionLabelRender === 'function'
? props.optionLabelRender(item) ? props.optionLabelRender(item)
: item[props.labelKey || 'label'], : item[props.labelKey || 'label'],
class: 'one-line-text',
}); });
}; };
@ -276,7 +277,7 @@ export default defineComponent(
const _slots: MsSearchSelectSlots = { const _slots: MsSearchSelectSlots = {
default: () => default: () =>
filterOptions.value.map((item) => ( 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 <a-option
key={item[props.valueKey || 'value']} key={item[props.valueKey || 'value']}
value={props.objectValue ? item : item[props.valueKey || 'value']} value={props.objectValue ? item : item[props.valueKey || 'value']}

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="flex h-full flex-col gap-[24px] overflow-hidden">
<div class="group-auth-table"> <div class="group-auth-table">
<a-table <a-table
:span-method="dataSpanMethod" :span-method="dataSpanMethod"
@ -77,7 +77,6 @@
saveOrgUSetting, saveOrgUSetting,
} from '@/api/modules/setting/usergroup'; } from '@/api/modules/setting/usergroup';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import { useUserStore } from '@/store';
import { import {
type AuthScopeType, type AuthScopeType,
@ -117,12 +116,11 @@
scroll() { scroll() {
return { return {
x: '800px', x: '800px',
y: 'calc(100vh - 264px)', y: '100%',
}; };
}, },
} }
); );
const userStore = useUserStore();
const systemType = inject<AuthScopeEnum>('systemType'); const systemType = inject<AuthScopeEnum>('systemType');
const loading = ref(false); const loading = ref(false);
@ -406,8 +404,9 @@
// //
const initData = async (id: string) => { const initData = async (id: string) => {
try { try {
let res: UserGroupAuthSetting[] = [];
loading.value = true; loading.value = true;
tableData.value = []; // 使
let res: UserGroupAuthSetting[] = [];
if (systemType === AuthScopeEnum.SYSTEM) { if (systemType === AuthScopeEnum.SYSTEM) {
res = await getGlobalUSetting(id); res = await getGlobalUSetting(id);
} else if (systemType === AuthScopeEnum.ORGANIZATION) { } else if (systemType === AuthScopeEnum.ORGANIZATION) {
@ -415,7 +414,6 @@
} else { } else {
res = await getAuthByUserGroup(id); res = await getAuthByUserGroup(id);
} }
tableData.value = transformData(res); tableData.value = transformData(res);
handleAllChange(true); handleAllChange(true);
} catch (error) { } catch (error) {
@ -488,16 +486,20 @@
<style scoped lang="less"> <style scoped lang="less">
.group-auth-table { .group-auth-table {
position: relative; @apply flex-1 overflow-hidden;
padding: 24px;
padding: 0 24px;
:deep(.arco-table-container) { :deep(.arco-table-container) {
border-top: 1px solid var(--color-text-n8) !important; 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; border-left: 1px solid var(--color-text-n8) !important;
} }
:deep(.arco-table-th-title) { :deep(.arco-table-th-title) {
width: 100%; width: 100%;
} }
:deep(.arco-table-th) {
background-color: var(--color-text-n9);
line-height: normal;
}
:deep(.arco-checkbox-indeterminate) { :deep(.arco-checkbox-indeterminate) {
.arco-checkbox-icon { .arco-checkbox-icon {
border-color: rgb(var(--primary-5)); border-color: rgb(var(--primary-5));
@ -506,8 +508,6 @@
} }
} }
.footer { .footer {
@apply absolute bottom-0 left-0 w-full;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
padding: 24px; padding: 24px;

View File

@ -70,6 +70,13 @@
> >
</a-tooltip> </a-tooltip>
<div <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="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }" :class="{ '!opacity-100': element.id === currentId }"
> >
@ -83,7 +90,12 @@
/> />
</div> </div>
<MsMoreAction <MsMoreAction
v-if="isSystemShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)" v-if="
isSystemShowAll &&
!element.internal &&
(element.scopeId !== 'global' || !isGlobalDisable) &&
systemMoreAction.length > 0
"
:list="systemMoreAction" :list="systemMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.SYSTEM)" @select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.SYSTEM)"
> >
@ -160,6 +172,13 @@
> >
</a-tooltip> </a-tooltip>
<div <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="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }" :class="{ '!opacity-100': element.id === currentId }"
> >
@ -173,7 +192,12 @@
/> />
</div> </div>
<MsMoreAction <MsMoreAction
v-if="isOrdShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)" v-if="
isOrdShowAll &&
!element.internal &&
(element.scopeId !== 'global' || !isGlobalDisable) &&
orgMoreAction.length > 0
"
:list="orgMoreAction" :list="orgMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.ORGANIZATION)" @select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.ORGANIZATION)"
> >
@ -250,6 +274,13 @@
> >
</a-tooltip> </a-tooltip>
<div <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="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }" :class="{ '!opacity-100': element.id === currentId }"
> >
@ -263,7 +294,12 @@
/> />
</div> </div>
<MsMoreAction <MsMoreAction
v-if="isProjectShowAll && !element.internal && (element.scopeId !== 'global' || !isGlobalDisable)" v-if="
isProjectShowAll &&
!element.internal &&
(element.scopeId !== 'global' || !isGlobalDisable) &&
projectMoreAction.length > 0
"
:list="projectMoreAction" :list="projectMoreAction"
@select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.PROJECT)" @select="(value) => handleMoreAction(value, element.id, AuthScopeEnum.PROJECT)"
> >
@ -335,7 +371,6 @@
const currentItem = ref<CurrentUserGroupItem>({ id: '', name: '', internal: false, type: AuthScopeEnum.SYSTEM }); const currentItem = ref<CurrentUserGroupItem>({ id: '', name: '', internal: false, type: AuthScopeEnum.SYSTEM });
const currentId = ref(''); const currentId = ref('');
const currentName = computed(() => currentItem.value.name);
const userModalVisible = ref(false); const userModalVisible = ref(false);

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="px-[24px]"> <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> <template v-if="hasAnyPermission(props.updatePermission || [])" #quickCreate>
<MsConfirmUserSelector :ok-loading="okLoading" v-bind="userSelectorProps" @confirm="handleAddMember" /> <MsConfirmUserSelector :ok-loading="okLoading" v-bind="userSelectorProps" @confirm="handleAddMember" />
</template> </template>

View File

@ -1,5 +1,9 @@
<template> <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}`"> <div :class="`flex w-full items-center ${props.class}`">
<a-input-tag <a-input-tag
v-model:model-value="innerModelValue" v-model:model-value="innerModelValue"
@ -59,6 +63,7 @@
inputClass?: string; inputClass?: string;
size?: 'small' | 'large' | 'medium' | 'mini'; size?: 'small' | 'large' | 'medium' | 'mini';
disabled?: boolean; disabled?: boolean;
noTooltip?: boolean;
}>(), }>(),
{ {
retainInputValue: true, retainInputValue: true,
@ -68,6 +73,7 @@
class: '', class: '',
inputClass: '', inputClass: '',
size: 'medium', size: 'medium',
noTooltip: false,
} }
); );
const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear', 'blur', 'click']); const emit = defineEmits(['update:modelValue', 'update:inputValue', 'change', 'clear', 'blur', 'click']);

View File

@ -88,7 +88,7 @@ export default function useSelect(config: UseSelectOption) {
onMounted(() => { onMounted(() => {
if (config.selectRef.value) { 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'); selectViewInner.value = config.selectRef.value.$el.nextElementSibling.querySelector('.arco-select-view-inner');
} }
}); });

View File

@ -728,6 +728,7 @@ interface ParsedCurlOptions {
url?: string; url?: string;
queryParameters?: { key: string; value: string }[]; queryParameters?: { key: string; value: string }[];
headers?: { key: string; value: string }[]; headers?: { key: string; value: string }[];
method?: string;
} }
/** /**
* curl * curl
@ -740,16 +741,21 @@ export function parseCurlScript(curlScript: string): ParsedCurlOptions {
const [_, url] = curlScript.match(/curl\s+'([^']+)'/) || []; const [_, url] = curlScript.match(/curl\s+'([^']+)'/) || [];
if (url) { if (url) {
options.url = 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(/\?(.*?)'/); const [, method] = curlScript.match(/-X\s+'([^']+)'/) || [];
if (queryMatch) { if (method) {
const queryParams = queryMatch[1].split('&').map((param) => { options.method = method;
const [key, value] = param.split('=');
return { key, value };
});
options.queryParameters = queryParams;
} }
// 提取 header // 提取 header

View File

@ -821,9 +821,11 @@
() => props.params, () => props.params,
(arr) => { (arr) => {
if (arr.length > 0) { if (arr.length > 0) {
let hasNoIdItem = false;
paramsData.value = arr.map((item, i) => { paramsData.value = arr.map((item, i) => {
if (!item) { if (!item) {
// undefined // undefined
hasNoIdItem = true;
return { return {
...cloneDeep(props.defaultParamItem), ...cloneDeep(props.defaultParamItem),
id: new Date().getTime() + i, id: new Date().getTime() + i,
@ -831,6 +833,7 @@
} }
if (!item.id) { if (!item.id) {
// id // id
hasNoIdItem = true;
return { return {
...item, ...item,
id: new Date().getTime() + i, id: new Date().getTime() + i,
@ -839,8 +842,10 @@
return item; return item;
}); });
const lastTwoIsSame = 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 ( if (
hasNoIdItem &&
!filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault && !filterKeyValParams(arr, props.defaultParamItem).lastDataIsDefault &&
!props.isTreeTable && !props.isTreeTable &&
!lastTwoIsSame // !lastTwoIsSame //

View File

@ -274,9 +274,10 @@
} }
function handleCurlImportConfirm() { function handleCurlImportConfirm() {
const { url, headers, queryParameters } = parseCurlScript(curlCode.value); const { url, headers, queryParameters, method } = parseCurlScript(curlCode.value);
addDebugTab({ addDebugTab({
url, url,
method: method?.toUpperCase() || RequestMethods.GET,
headers: headers:
headers?.map((e) => ({ headers?.map((e) => ({
contentType: RequestContentTypeEnum.TEXT, contentType: RequestContentTypeEnum.TEXT,
@ -348,6 +349,9 @@
[activeDebug.value] = debugTabs.value; [activeDebug.value] = debugTabs.value;
} }
} }
if (debugTabs.value.length === 0) {
addDebugTab();
}
} }
onMounted(() => { onMounted(() => {

View File

@ -116,7 +116,7 @@
import type { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management'; import type { ApiCaseDetail, ApiDefinitionDetail } from '@/models/apiTest/management';
import type { ApiScenarioTableItem } from '@/models/apiTest/scenario'; import type { ApiScenarioTableItem } from '@/models/apiTest/scenario';
import { ScenarioStepRefType } from '@/enums/apiEnum'; import { ScenarioStepRefType, ScenarioStepType } from '@/enums/apiEnum';
export interface ImportData { export interface ImportData {
api: MsTableDataItem<ApiDefinitionDetail>[]; api: MsTableDataItem<ApiDefinitionDetail>[];
@ -261,11 +261,25 @@
// uniqueIdcopyFromStepId // uniqueIdcopyFromStepId
fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => { fullScenarioArr = mapTree<MsTableDataItem<ApiScenarioTableItem>>(fullScenarioArr, (node) => {
const id = getGenerateId(); 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 { return {
...node, ...node,
copyFromStepId: node.id, copyFromStepId: node.id,
originProjectId: node.projectId, originProjectId: node.projectId,
id, id: node.stepType === ScenarioStepType.API_SCENARIO ? id : node.id, // id
uniqueId: id, uniqueId: id,
}; };
}); });

View File

@ -407,7 +407,13 @@
<a-button type="secondary" :disabled="scheduleModalLoading" @click="cancelScheduleModal"> <a-button type="secondary" :disabled="scheduleModalLoading" @click="cancelScheduleModal">
{{ t('common.cancel') }} {{ t('common.cancel') }}
</a-button> </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') }} {{ t('common.save') }}
</a-button> </a-button>
</div> </div>

View File

@ -711,6 +711,9 @@
function setStepMoreAction(items: ActionsItem[], node: MsTreeNodeData) { function setStepMoreAction(items: ActionsItem[], node: MsTreeNodeData) {
const _stepType = getStepType(node as ScenarioStepItem); const _stepType = getStepType(node as ScenarioStepItem);
if ((node as ScenarioStepItem).isQuoteScenarioStep) {
return [];
}
if ((node as ScenarioStepItem).stepType === ScenarioStepType.CUSTOM_REQUEST) { if ((node as ScenarioStepItem).stepType === ScenarioStepType.CUSTOM_REQUEST) {
// //
return [ return [
@ -746,9 +749,6 @@
}, },
]; ];
} }
if ((node as ScenarioStepItem).isQuoteScenarioStep) {
return [];
}
if (_stepType.isQuoteApi || _stepType.isCopyApi) { if (_stepType.isQuoteApi || _stepType.isCopyApi) {
return [ return [
{ {
@ -2096,6 +2096,8 @@
} }
.ms-tree-node-extra { .ms-tree-node-extra {
@apply !visible !w-auto; @apply !visible !w-auto;
margin-right: 24px;
} }
} }
.ms-form { .ms-form {

View File

@ -105,10 +105,11 @@
:field-config="{ :field-config="{
field: detail.name, field: detail.name,
placeholder: t('project.fileManagement.fileNamePlaceholder'), placeholder: t('project.fileManagement.fileNamePlaceholder'),
NotNullTip: t('project.fileManagement.fileNameNotNull'),
}" }"
:node-id="detail.id" :node-id="detail.id"
:all-names="[]" :all-names="[]"
@rename-finish="detailDrawerRef?.initDetail" @rename-finish="() => detailDrawerRef?.initDetail()"
> >
<MsButton class="!mr-0 ml-[8px]"> <MsButton class="!mr-0 ml-[8px]">
{{ t('common.rename') }} {{ t('common.rename') }}
@ -132,12 +133,13 @@
:field-config="{ :field-config="{
field: detail.desc, field: detail.desc,
placeholder: t('project.fileManagement.descPlaceholder'), placeholder: t('project.fileManagement.descPlaceholder'),
maxLength: 250, maxLength: 1000,
isTextArea: true, isTextArea: true,
}" }"
:node-id="detail.id" :node-id="detail.id"
:all-names="[]" :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> <MsButton class="ml-[8px]"> <MsIcon type="icon-icon_edit_outlined" /></MsButton>
</popConfirm> </popConfirm>
@ -170,7 +172,6 @@
v-bind="caseTableProps" v-bind="caseTableProps"
:data="caseList" :data="caseList"
no-disable no-disable
:action-config="caseBatchActions"
v-on="caseTableEvent" v-on="caseTableEvent"
> >
<template #id="{ record }"> <template #id="{ record }">
@ -403,8 +404,7 @@
title: 'project.fileManagement.caseType', title: 'project.fileManagement.caseType',
dataIndex: 'sourceType', dataIndex: 'sourceType',
slotName: 'sourceType', slotName: 'sourceType',
width: 100, width: 120,
showTooltip: true,
}, },
{ {
title: 'project.fileManagement.caseFileVersion', title: 'project.fileManagement.caseFileVersion',
@ -428,20 +428,20 @@
scroll: { x: 800 }, scroll: { x: 800 },
columns: caseColumns, columns: caseColumns,
heightUsed: 200, heightUsed: 200,
selectable: true, selectable: false,
showSelectAll: true, showSelectAll: false,
showPagination: false, showPagination: false,
}); });
const caseBatchActions = { // const caseBatchActions = {
baseAction: [ // baseAction: [
{ // {
label: 'project.fileManagement.updateCaseFile', // label: 'project.fileManagement.updateCaseFile',
eventTag: 'updateCaseFile', // eventTag: 'updateCaseFile',
permission: ['PROJECT_FILE_MANAGEMENT:READ+UPDATE'], // permission: ['PROJECT_FILE_MANAGEMENT:READ+UPDATE'],
}, // },
], // ],
}; // };
const keyword = ref(''); const keyword = ref('');
const caseList = computed(() => caseTableProps.value.data.filter((item) => item.sourceName.includes(keyword.value))); const caseList = computed(() => caseTableProps.value.data.filter((item) => item.sourceName.includes(keyword.value)));

View File

@ -20,7 +20,14 @@
<a-form-item <a-form-item
class="hidden-item" class="hidden-item"
field="field" 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 <a-textarea
v-if="props.fieldConfig?.isTextArea" v-if="props.fieldConfig?.isTextArea"
@ -68,6 +75,7 @@
placeholder?: string; placeholder?: string;
maxLength?: number; maxLength?: number;
isTextArea?: boolean; isTextArea?: boolean;
NotNullTip?: string;
} }
const props = defineProps<{ const props = defineProps<{
@ -79,6 +87,7 @@
fieldConfig?: FieldConfig; fieldConfig?: FieldConfig;
parentId?: string; // id parentId?: string; // id
nodeId?: string; // id nodeId?: string; // id
noRule?: boolean;
}>(); }>();
const emit = defineEmits(['update:visible', 'close', 'addFinish', 'renameFinish', 'updateDescFinish']); const emit = defineEmits(['update:visible', 'close', 'addFinish', 'renameFinish', 'updateDescFinish']);

View File

@ -227,7 +227,13 @@
}, },
}); });
} else { } else {
initModulesCount(tableFilterParams.value); initModulesCount({
...tableFilterParams.value,
combine: {
...tableFilterParams.value.combine,
storage: 'minio',
},
});
} }
} }

View File

@ -8,6 +8,7 @@ export default {
'project.fileManagement.addStorage': 'Add repository', 'project.fileManagement.addStorage': 'Add repository',
'project.fileManagement.rename': 'Rename', 'project.fileManagement.rename': 'Rename',
'project.fileManagement.nameNotNull': 'Module name cannot be empty', '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.namePlaceholder': 'Please enter the module name and press Enter to save',
'project.fileManagement.renameSuccess': 'Rename successful', 'project.fileManagement.renameSuccess': 'Rename successful',
'project.fileManagement.updateDescSuccess': 'File description updated successfully', 'project.fileManagement.updateDescSuccess': 'File description updated successfully',

View File

@ -8,6 +8,7 @@ export default {
'project.fileManagement.addStorage': '添加存储库', 'project.fileManagement.addStorage': '添加存储库',
'project.fileManagement.rename': '重命名', 'project.fileManagement.rename': '重命名',
'project.fileManagement.nameNotNull': '模块名称不能为空', 'project.fileManagement.nameNotNull': '模块名称不能为空',
'project.fileManagement.fileNameNotNull': '文件名称不能为空',
'project.fileManagement.namePlaceholder': '请输入模块名称,按回车键保存', 'project.fileManagement.namePlaceholder': '请输入模块名称,按回车键保存',
'project.fileManagement.renameSuccess': '重命名成功', 'project.fileManagement.renameSuccess': '重命名成功',
'project.fileManagement.updateDescSuccess': '文件描述更新成功', 'project.fileManagement.updateDescSuccess': '文件描述更新成功',

View File

@ -1,52 +1,81 @@
<template> <template>
<MsBaseTable <div>
v-bind="propsRes" <div class="mb-4 grid grid-cols-4 gap-2">
:action-config="tableBatchActions" <div class="col-span-2">
@selected-change="handleTableSelect" <a-button v-permission="['PROJECT_USER:READ+ADD']" class="mr-3" type="primary" @click="addMember">
v-on="propsEvent" {{ t('project.member.addMember') }}
@batch-action="handleTableBatch" </a-button>
>
<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>
<div v-else class="flex items-center text-[var(--color-text-4)]"> <div>
<MsIcon type="icon-icon_disable" class="mr-[2px]" /> <a-select v-model="roleIds" @change="changeSelect">
{{ t('organization.member.statusDisable') }} <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>
</template> <div>
<template #operation="{ record }"> <a-input-search
<MsRemoveButton v-model="keyword"
v-permission="['PROJECT_USER:READ+DELETE']" :max-length="255"
position="br" :placeholder="t('project.member.searchMember')"
:title="t('project.member.deleteMemberTip', { name: characterLimit(record.name) })" allow-clear
:sub-title-tip="t('project.member.subTitle')" @search="searchHandler"
:loading="deleteLoading" @press-enter="searchHandler"
@ok="removeMember(record)" @clear="searchHandler"
/> >
</template> </a-input-search>
</MsBaseTable> </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 <AddMemberModal
ref="projectMemberRef" ref="projectMemberRef"
v-model:visible="addMemberVisible" v-model:visible="addMemberVisible"
@ -95,12 +124,6 @@
} from '@/models/projectManagement/projectAndPermission'; } from '@/models/projectManagement/projectAndPermission';
import { TableKeyEnum } from '@/enums/tableEnum'; import { TableKeyEnum } from '@/enums/tableEnum';
const props = defineProps<{
roleIds: string;
userGroupOptions: ProjectUserOption[];
keyword: string;
}>();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
const { openModal } = useModal(); 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 () => { const initData = async () => {
await nextTick(); await nextTick();
setLoadListParams({ setLoadListParams({
filter: { filter: {
roleIds: props.roleIds ? [props.roleIds] : [], roleIds: roleIds.value ? [roleIds.value] : [],
}, },
projectId: lastProjectId.value, projectId: lastProjectId.value,
keyword: props.keyword, keyword: keyword.value,
}); });
await loadList(); await loadList();
}; };
const searchHandler = () => {
initData();
};
const changeSelect = () => {
initData();
};
// //
const selectData = ref<string[] | undefined>([]); const selectData = ref<string[] | undefined>([]);
@ -250,6 +298,7 @@
resetSelector(); resetSelector();
} }
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console
console.log(error); console.log(error);
} finally { } finally {
deleteLoading.value = false; deleteLoading.value = false;
@ -276,9 +325,9 @@
selectAll: !!selectAll, selectAll: !!selectAll,
excludeIds: excludeIds || [], excludeIds: excludeIds || [],
selectIds: selectedIds || [], selectIds: selectedIds || [],
keyword: props.keyword, keyword: keyword.value,
condition: { condition: {
keyword: props.keyword, keyword: keyword.value,
filter: propsRes.value.filter, filter: propsRes.value.filter,
combine: batchParams.value.condition, combine: batchParams.value.condition,
}, },
@ -288,6 +337,7 @@
initData(); initData();
resetSelector(); resetSelector();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console
console.log(error); console.log(error);
} }
}; };
@ -328,6 +378,7 @@
record.showUserSelect = false; record.showUserSelect = false;
loadList(); loadList();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console
console.log(error); console.log(error);
} }
}; };
@ -360,15 +411,11 @@
editProjectMember(record); editProjectMember(record);
}; };
onBeforeMount(() => { onBeforeMount(async () => {
await initOptions();
initData(); initData();
}); });
defineExpose({
initData,
addMember,
});
await tableStore.initColumn(TableKeyEnum.PROJECT_MEMBER, columns, 'drawer'); await tableStore.initColumn(TableKeyEnum.PROJECT_MEMBER, columns, 'drawer');
</script> </script>

View File

@ -1,87 +1,12 @@
<template> <template>
<div class="mb-4 grid grid-cols-4 gap-2"> <memberTable v-if="isMounted" />
<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"
/>
</template> </template>
<script setup lang="ts"> <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 项目管理-项目与权限-成员 * @description 项目管理-项目与权限-成员
*/ */
const appStore = useAppStore(); const memberTable = defineAsyncComponent(() => import('./components/memberTable.vue'));
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 isMounted = ref(false); const isMounted = ref(false);

View File

@ -34,6 +34,14 @@
></a-textarea> ></a-textarea>
</a-form-item> </a-form-item>
<a-form-item :label="t('system.resourcePool.serverUrl')" field="serverUrl" class="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 <a-input
v-model:model-value="form.serverUrl" v-model:model-value="form.serverUrl"
:placeholder="t('system.resourcePool.rootUrlPlaceholder')" :placeholder="t('system.resourcePool.rootUrlPlaceholder')"

View File

@ -26,7 +26,7 @@ export default {
'system.resourcePool.deletePoolCancel': 'Cancel', 'system.resourcePool.deletePoolCancel': 'Cancel',
'system.resourcePool.deletePoolSuccess': 'Deleted successfully', 'system.resourcePool.deletePoolSuccess': 'Deleted successfully',
'system.resourcePool.detailDesc': 'Description', 'system.resourcePool.detailDesc': 'Description',
'system.resourcePool.detailUrl': 'Current site URL', 'system.resourcePool.detailUrl': 'Intranet URL',
'system.resourcePool.detailRange': 'Applied organization', 'system.resourcePool.detailRange': 'Applied organization',
'system.resourcePool.detailUse': 'Use', 'system.resourcePool.detailUse': 'Use',
'system.resourcePool.detailMirror': 'Mirror', 'system.resourcePool.detailMirror': 'Mirror',
@ -41,7 +41,9 @@ export default {
'system.resourcePool.namePlaceholder': 'Please enter a resource pool name', 'system.resourcePool.namePlaceholder': 'Please enter a resource pool name',
'system.resourcePool.desc': 'Description', 'system.resourcePool.desc': 'Description',
'system.resourcePool.descPlaceholder': 'Please describe the resource pool', '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.rootUrlPlaceholder': 'MS deployment address',
'system.resourcePool.orgRange': 'Applied organization', 'system.resourcePool.orgRange': 'Applied organization',
'system.resourcePool.orgAll': 'All organization', 'system.resourcePool.orgAll': 'All organization',

View File

@ -25,7 +25,7 @@ export default {
'system.resourcePool.deletePoolCancel': '取消', 'system.resourcePool.deletePoolCancel': '取消',
'system.resourcePool.deletePoolSuccess': '删除成功', 'system.resourcePool.deletePoolSuccess': '删除成功',
'system.resourcePool.detailDesc': '描述', 'system.resourcePool.detailDesc': '描述',
'system.resourcePool.detailUrl': '当前站点 URL', 'system.resourcePool.detailUrl': '内网 URL',
'system.resourcePool.detailRange': '应用组织', 'system.resourcePool.detailRange': '应用组织',
'system.resourcePool.detailUse': '用途', 'system.resourcePool.detailUse': '用途',
'system.resourcePool.detailMirror': '镜像', 'system.resourcePool.detailMirror': '镜像',
@ -40,7 +40,8 @@ export default {
'system.resourcePool.namePlaceholder': '请输入资源池名称', 'system.resourcePool.namePlaceholder': '请输入资源池名称',
'system.resourcePool.desc': '描述', 'system.resourcePool.desc': '描述',
'system.resourcePool.descPlaceholder': '请对该资源池进行描述', 'system.resourcePool.descPlaceholder': '请对该资源池进行描述',
'system.resourcePool.serverUrl': '当前站点 URL', 'system.resourcePool.serverUrl': '内网 URL',
'system.resourcePool.serverUrlTip': '资源池部署在内网时,可走内网地址',
'system.resourcePool.rootUrlPlaceholder': 'MS的部署地址', 'system.resourcePool.rootUrlPlaceholder': 'MS的部署地址',
'system.resourcePool.orgRange': '应用组织', 'system.resourcePool.orgRange': '应用组织',
'system.resourcePool.orgAll': '全部组织', 'system.resourcePool.orgAll': '全部组织',

View File

@ -12,7 +12,7 @@
/> />
</template> </template>
<template #second> <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]"> <div class="flex flex-row items-center justify-between px-[24px]">
<a-tooltip :content="currentUserGroupItem.name"> <a-tooltip :content="currentUserGroupItem.name">
<div class="one-line-text max-w-[300px] font-medium">{{ currentUserGroupItem.name }}</div> <div class="one-line-text max-w-[300px] font-medium">{{ currentUserGroupItem.name }}</div>
@ -38,7 +38,7 @@
</a-radio-group> </a-radio-group>
</div> </div>
</div> </div>
<div class="mt-[16px]"> <div class="flex-1 overflow-hidden">
<UserTable <UserTable
v-if="currentTable === 'user' && couldShowUser" v-if="currentTable === 'user' && couldShowUser"
ref="userRef" ref="userRef"