feat(文件管理): 文件管理页面&树组件调整
This commit is contained in:
parent
5f6bf18411
commit
8c7ab91265
|
@ -42,6 +42,14 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-tree>
|
</a-tree>
|
||||||
|
<slot name="empty">
|
||||||
|
<div
|
||||||
|
v-show="treeData.length === 0 && props.emptyText"
|
||||||
|
class="rounded-[var(--border-radius-small)] bg-[var(--color-fill-1)] p-[8px] text-[12px] text-[var(--color-text-4)]"
|
||||||
|
>
|
||||||
|
{{ props.emptyText }}
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -69,6 +77,7 @@
|
||||||
focusNodeKey?: string | number; // 聚焦的节点 key
|
focusNodeKey?: string | number; // 聚焦的节点 key
|
||||||
nodeMoreActions?: ActionsItem[]; // 节点展示在省略号按钮内的更多操作
|
nodeMoreActions?: ActionsItem[]; // 节点展示在省略号按钮内的更多操作
|
||||||
expandAll?: boolean; // 是否展开/折叠所有节点,true 为全部展开,false 为全部折叠
|
expandAll?: boolean; // 是否展开/折叠所有节点,true 为全部展开,false 为全部折叠
|
||||||
|
emptyText?: string; // 空数据时的文案
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
searchDebounce: 300,
|
searchDebounce: 300,
|
||||||
|
@ -256,7 +265,7 @@
|
||||||
watch(
|
watch(
|
||||||
() => innerFocusNodeKey.value,
|
() => innerFocusNodeKey.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val) {
|
if (val.toString() !== '') {
|
||||||
focusEl.value = treeRef.value?.$el.querySelector(`[data-key=${val}]`);
|
focusEl.value = treeRef.value?.$el.querySelector(`[data-key=${val}]`);
|
||||||
if (focusEl.value) {
|
if (focusEl.value) {
|
||||||
focusEl.value.style.backgroundColor = 'rgb(var(--primary-1))';
|
focusEl.value.style.backgroundColor = 'rgb(var(--primary-1))';
|
||||||
|
@ -334,6 +343,7 @@
|
||||||
.ms-tree-node-extra__btn,
|
.ms-tree-node-extra__btn,
|
||||||
.ms-tree-node-extra__more {
|
.ms-tree-node-extra__more {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
border-radius: var(--border-radius-mini);
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgb(var(--primary-9));
|
background-color: rgb(var(--primary-9));
|
||||||
.arco-icon {
|
.arco-icon {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center gap-[4px]">
|
||||||
|
<popConfirm mode="add" :all-names="[]" @close="emit('close')">
|
||||||
|
<MsButton type="text" size="mini" class="action-btn" @click="emit('add', props.item)">
|
||||||
|
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
||||||
|
</MsButton>
|
||||||
|
</popConfirm>
|
||||||
|
<MsTableMoreAction
|
||||||
|
:list="folderMoreActions"
|
||||||
|
trigger="click"
|
||||||
|
@select="emit('select', $event, props.item)"
|
||||||
|
@close="emit('actionsClose')"
|
||||||
|
>
|
||||||
|
<MsButton type="text" size="mini" class="action-btn" @click="emit('clickMore', props.item)">
|
||||||
|
<MsIcon type="icon-icon_more_outlined" size="14" class="text-[var(--color-text-4)]" />
|
||||||
|
</MsButton>
|
||||||
|
</MsTableMoreAction>
|
||||||
|
<popConfirm mode="rename" :title="item.name" :all-names="[]" @close="emit('close')">
|
||||||
|
<span ref="renameSpanRef" class="relative"></span>
|
||||||
|
</popConfirm>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import popConfirm from './popConfirm.vue';
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
|
|
||||||
|
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
item: Record<string, any>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['add', 'close', 'select', 'actionsClose', 'clickMore']);
|
||||||
|
|
||||||
|
const folderMoreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'project.fileManagement.rename',
|
||||||
|
eventTag: 'rename',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'project.fileManagement.delete',
|
||||||
|
eventTag: 'delete',
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.action-btn {
|
||||||
|
@apply !mr-0;
|
||||||
|
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: var(--border-radius-mini);
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(var(--primary-9));
|
||||||
|
.arco-icon {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,125 @@
|
||||||
|
<template>
|
||||||
|
<a-popconfirm
|
||||||
|
v-model:popup-visible="innerVisible"
|
||||||
|
class="ms-pop-confirm--hidden-icon"
|
||||||
|
position="bottom"
|
||||||
|
:ok-loading="loading"
|
||||||
|
:cancel-button-props="{ disabled: loading }"
|
||||||
|
:on-before-ok="beforeConfirm"
|
||||||
|
:popup-container="props.popupContainer || 'body'"
|
||||||
|
@popup-visible-change="reset"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="mb-[8px] font-medium">
|
||||||
|
{{ props.mode === 'add' ? t('project.fileManagement.addSubModule') : t('project.fileManagement.rename') }}
|
||||||
|
</div>
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
class="hidden-item"
|
||||||
|
field="name"
|
||||||
|
:rules="[{ required: true, message: t('project.fileManagement.nameNotNull') }, { validator: validateName }]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="form.name"
|
||||||
|
:max-length="50"
|
||||||
|
:placeholder="props.placeholder || t('project.fileManagement.namePlaceholder')"
|
||||||
|
class="w-[245px]"
|
||||||
|
@press-enter="beforeConfirm(undefined)"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
<slot></slot>
|
||||||
|
</a-popconfirm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
import type { FormInstance } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
mode: 'add' | 'rename';
|
||||||
|
visible?: boolean;
|
||||||
|
title?: string;
|
||||||
|
allNames: string[];
|
||||||
|
popupContainer?: string;
|
||||||
|
placeholder?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible', 'close']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const innerVisible = ref(props.visible || false);
|
||||||
|
const form = ref({
|
||||||
|
name: props.title || '',
|
||||||
|
});
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.title,
|
||||||
|
(val) => {
|
||||||
|
form.value.name = val || '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
innerVisible.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => innerVisible.value,
|
||||||
|
(val) => {
|
||||||
|
if (!val) {
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
emit('update:visible', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function beforeConfirm(done?: (closed: boolean) => void) {
|
||||||
|
formRef.value?.validate(async (errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
if (props.mode === 'add') {
|
||||||
|
Message.success(t('project.fileManagement.addSubModuleSuccess'));
|
||||||
|
} else {
|
||||||
|
Message.success(t('project.fileManagement.renameSuccess'));
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
done(true);
|
||||||
|
} else {
|
||||||
|
innerVisible.value = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
} else if (done) {
|
||||||
|
done(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateName(value: any, callback: (error?: string | undefined) => void) {
|
||||||
|
if (props.allNames.includes(value)) {
|
||||||
|
callback(t('project.fileManagement.nameExist'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
form.value.name = '';
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<template>
|
||||||
|
<div class="p-[24px]">
|
||||||
|
<div class="header">
|
||||||
|
<a-button type="primary">{{ t('project.fileManagement.addFile') }}</a-button>
|
||||||
|
<div class="header-right">
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('project.fileManagement.folderSearchPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="w-[240px]"
|
||||||
|
></a-input-search>
|
||||||
|
<a-radio-group v-model:model-value="fileType" type="button" class="file-show-type" @change="changeFileType">
|
||||||
|
<a-radio value="Module" class="show-type-icon">{{ t('project.fileManagement.module') }}</a-radio>
|
||||||
|
<a-radio value="Storage" class="show-type-icon">{{ t('project.fileManagement.storage') }}</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type" @change="changeShowType">
|
||||||
|
<a-radio value="list" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_view-list_outlined" /></a-radio>
|
||||||
|
<a-radio value="card" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_card_outlined" /></a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const keyword = ref('');
|
||||||
|
const fileType = ref('Module');
|
||||||
|
|
||||||
|
function changeFileType() {
|
||||||
|
console.log(fileType.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showType = ref('list');
|
||||||
|
|
||||||
|
function changeShowType() {
|
||||||
|
console.log(showType.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.header {
|
||||||
|
@apply flex items-center justify-between;
|
||||||
|
.header-right {
|
||||||
|
@apply ml-auto flex items-center justify-end;
|
||||||
|
|
||||||
|
width: 70%;
|
||||||
|
gap: 8px;
|
||||||
|
.show-type-icon {
|
||||||
|
:deep(.arco-radio-button-content) {
|
||||||
|
@apply flex;
|
||||||
|
|
||||||
|
padding: 4px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,664 @@
|
||||||
|
<template>
|
||||||
|
<div class="page">
|
||||||
|
<MsSplitBox>
|
||||||
|
<template #left>
|
||||||
|
<div class="p-[24px]">
|
||||||
|
<div class="folder" @click="setActiveFolder('my')">
|
||||||
|
<div :class="getFolderClass('my')">
|
||||||
|
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||||
|
<div class="folder-name">{{ t('project.fileManagement.myFile') }}</div>
|
||||||
|
<div class="folder-count">({{ myFileCount }})</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="folder">
|
||||||
|
<div :class="getFolderClass('all')" @click="setActiveFolder('all')">
|
||||||
|
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||||
|
<div class="folder-name">{{ t('project.fileManagement.allFile') }}</div>
|
||||||
|
<div class="folder-count">({{ allFileCount }})</div>
|
||||||
|
</div>
|
||||||
|
<div class="ml-auto flex items-center">
|
||||||
|
<a-tooltip
|
||||||
|
:content="isExpandAll ? t('project.fileManagement.collapseAll') : t('project.fileManagement.expandAll')"
|
||||||
|
>
|
||||||
|
<a-button type="text" size="mini" class="p-[4px]" @click="changeExpand">
|
||||||
|
<MsIcon :type="isExpandAll ? 'icon-icon_folder_collapse1' : 'icon-icon_folder_expansion'" />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<popConfirm mode="add" :all-names="[]">
|
||||||
|
<a-tooltip :content="t('project.fileManagement.addSubModule')">
|
||||||
|
<a-button type="text" size="mini" class="p-[2px]">
|
||||||
|
<MsIcon type="icon-icon_create_planarity" size="18" />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</popConfirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-divider class="my-[8px]" />
|
||||||
|
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type" @change="changeShowType">
|
||||||
|
<a-radio value="Module">{{ t('project.fileManagement.module') }}</a-radio>
|
||||||
|
<a-radio value="Storage">{{ t('project.fileManagement.storage') }}</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
<div v-show="showType === 'Module'">
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="moduleKeyword"
|
||||||
|
:placeholder="t('project.fileManagement.folderSearchPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="mb-[8px]"
|
||||||
|
></a-input>
|
||||||
|
<MsTree
|
||||||
|
v-model:focus-node-key="focusNodeKey"
|
||||||
|
:data="folderTree"
|
||||||
|
:keyword="moduleKeyword"
|
||||||
|
:node-more-actions="folderMoreActions"
|
||||||
|
:expand-all="isExpandAll"
|
||||||
|
:empty-text="t('project.fileManagement.noFolder')"
|
||||||
|
draggable
|
||||||
|
block-node
|
||||||
|
@select="folderNodeSelect"
|
||||||
|
@more-action-select="handleFolderMoreSelect"
|
||||||
|
@more-actions-close="moreActionsClose"
|
||||||
|
>
|
||||||
|
<template #title="nodeData">
|
||||||
|
<span class="text-[var(--color-text-1)]">{{ nodeData.title }}</span>
|
||||||
|
<span class="ml-[4px] text-[var(--color-text-4)]">({{ nodeData.count }})</span>
|
||||||
|
</template>
|
||||||
|
<template #extra="nodeData">
|
||||||
|
<popConfirm mode="add" :all-names="[]" @close="resetFocusNodeKey">
|
||||||
|
<MsButton
|
||||||
|
type="text"
|
||||||
|
size="mini"
|
||||||
|
class="ms-tree-node-extra__btn !mr-0"
|
||||||
|
@click="setFocusNodeKe(nodeData)"
|
||||||
|
>
|
||||||
|
<MsIcon type="icon-icon_add_outlined" size="14" class="text-[var(--color-text-4)]" />
|
||||||
|
</MsButton>
|
||||||
|
</popConfirm>
|
||||||
|
<popConfirm mode="rename" :title="renameFolderTitle" :all-names="[]" @close="resetFocusNodeKey">
|
||||||
|
<span :id="`renameSpan${nodeData.key}`" class="relative"></span>
|
||||||
|
</popConfirm>
|
||||||
|
</template>
|
||||||
|
</MsTree>
|
||||||
|
</div>
|
||||||
|
<div v-show="showType === 'Storage'">
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="storageKeyword"
|
||||||
|
:placeholder="t('project.fileManagement.folderSearchPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="mb-[8px]"
|
||||||
|
></a-input>
|
||||||
|
<a-list
|
||||||
|
:virtual-list-props="{
|
||||||
|
height: 'calc(100vh - 310px)',
|
||||||
|
}"
|
||||||
|
:data="storageList"
|
||||||
|
:bordered="false"
|
||||||
|
:split="false"
|
||||||
|
>
|
||||||
|
<template #item="{ item, index }">
|
||||||
|
<div
|
||||||
|
:key="index"
|
||||||
|
:class="['folder', focusNodeKey === item.key ? 'ms-tree-node-extra--focus' : '']"
|
||||||
|
@click="setActiveFolder(item.key)"
|
||||||
|
>
|
||||||
|
<div :class="getFolderClass(item.key)">
|
||||||
|
<MsIcon type="icon-icon_git" class="folder-icon" />
|
||||||
|
<div class="folder-name">{{ item.title }}</div>
|
||||||
|
<div class="folder-count">({{ item.count }})</div>
|
||||||
|
</div>
|
||||||
|
<itemActions
|
||||||
|
:item="item"
|
||||||
|
@click.stop
|
||||||
|
@add="setFocusNodeKe"
|
||||||
|
@close="resetFocusNodeKey"
|
||||||
|
@actions-close="moreActionsClose"
|
||||||
|
@click-more="setFocusNodeKe"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #empty>
|
||||||
|
<div
|
||||||
|
class="rounded-[var(--border-radius-small)] bg-[var(--color-fill-1)] p-[8px] text-[12px] text-[var(--color-text-4)]"
|
||||||
|
>
|
||||||
|
{{ t('project.fileManagement.noStorage') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #right>
|
||||||
|
<rightBox />
|
||||||
|
</template>
|
||||||
|
</MsSplitBox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { Message } from '@arco-design/web-vue';
|
||||||
|
import { debounce } from 'lodash-es';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
|
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||||
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import useModal from '@/hooks/useModal';
|
||||||
|
import popConfirm from './components/popConfirm.vue';
|
||||||
|
import rightBox from './components/rightBox.vue';
|
||||||
|
import itemActions from './components/itemActions.vue';
|
||||||
|
|
||||||
|
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||||
|
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openModal } = useModal();
|
||||||
|
|
||||||
|
const myFileCount = ref(0);
|
||||||
|
const allFileCount = ref(0);
|
||||||
|
const isExpandAll = ref(false);
|
||||||
|
const activeFolderType = ref<'folder' | 'module' | 'storage'>('folder');
|
||||||
|
|
||||||
|
function changeExpand() {
|
||||||
|
isExpandAll.value = !isExpandAll.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeFolder = ref<string | number>('all');
|
||||||
|
|
||||||
|
function setActiveFolder(id: string) {
|
||||||
|
activeFolder.value = id;
|
||||||
|
if (['my', 'all'].includes(id)) {
|
||||||
|
activeFolderType.value = 'folder';
|
||||||
|
} else {
|
||||||
|
activeFolderType.value = 'storage';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFolderClass(id: string) {
|
||||||
|
return activeFolder.value === id ? 'folder-text folder-text--active' : 'folder-text';
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileShowType = 'Module' | 'Storage';
|
||||||
|
const showType = ref<FileShowType>('Module');
|
||||||
|
|
||||||
|
function changeShowType(val: string | number | boolean) {
|
||||||
|
showType.value = val as FileShowType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleKeyword = ref('');
|
||||||
|
|
||||||
|
const folderTree = ref([
|
||||||
|
{
|
||||||
|
title: 'Trunk',
|
||||||
|
key: 'node1',
|
||||||
|
count: 18,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Leaf',
|
||||||
|
key: 'node2',
|
||||||
|
count: 28,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Trunk',
|
||||||
|
key: 'node3',
|
||||||
|
count: 180,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
title: 'Leaf',
|
||||||
|
key: 'node4',
|
||||||
|
count: 138,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Leaf',
|
||||||
|
key: 'node5',
|
||||||
|
count: 108,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Trunk',
|
||||||
|
key: 'node6',
|
||||||
|
children: [],
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const focusNodeKey = ref<string | number>('');
|
||||||
|
|
||||||
|
function setFocusNodeKe(node: MsTreeNodeData) {
|
||||||
|
focusNodeKey.value = node.key || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderMoreActions: ActionsItem[] = [
|
||||||
|
{
|
||||||
|
label: 'project.fileManagement.rename',
|
||||||
|
eventTag: 'rename',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'project.fileManagement.delete',
|
||||||
|
eventTag: 'delete',
|
||||||
|
danger: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const renamePopVisible = ref(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件夹
|
||||||
|
* @param node 节点信息
|
||||||
|
*/
|
||||||
|
function deleteFolder(node: MsTreeNodeData) {
|
||||||
|
openModal({
|
||||||
|
type: 'error',
|
||||||
|
title: t('project.fileManagement.deleteTipTitle', { name: node.title }),
|
||||||
|
content: t('project.fileManagement.deleteTipContent'),
|
||||||
|
okText: t('project.fileManagement.deleteConfirm'),
|
||||||
|
okButtonProps: {
|
||||||
|
status: 'danger',
|
||||||
|
},
|
||||||
|
maskClosable: false,
|
||||||
|
onBeforeOk: async () => {
|
||||||
|
try {
|
||||||
|
Message.success(t('project.fileManagement.deleteSuccess'));
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideCancel: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const renameFolderTitle = ref(''); // 重命名的文件夹名称
|
||||||
|
|
||||||
|
function resetFocusNodeKey() {
|
||||||
|
focusNodeKey.value = '';
|
||||||
|
renamePopVisible.value = false;
|
||||||
|
renameFolderTitle.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理文件夹树节点选中事件
|
||||||
|
*/
|
||||||
|
function folderNodeSelect(selectedKeys: (string | number)[]) {
|
||||||
|
[activeFolder.value] = selectedKeys;
|
||||||
|
activeFolderType.value = 'module';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理树节点更多按钮事件
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
function handleFolderMoreSelect(item: ActionsItem, node: MsTreeNodeData) {
|
||||||
|
switch (item.eventTag) {
|
||||||
|
case 'delete':
|
||||||
|
deleteFolder(node);
|
||||||
|
resetFocusNodeKey();
|
||||||
|
break;
|
||||||
|
case 'rename':
|
||||||
|
renameFolderTitle.value = node.title || '';
|
||||||
|
renamePopVisible.value = true;
|
||||||
|
document.querySelector(`#renameSpan${node.key}`)?.dispatchEvent(new Event('click'));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function moreActionsClose() {
|
||||||
|
if (!renamePopVisible.value) {
|
||||||
|
// 当下拉菜单关闭时,若不是触发重命名气泡显示,则清空聚焦节点 key
|
||||||
|
resetFocusNodeKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const storageKeyword = ref('');
|
||||||
|
const originStorageList = ref([
|
||||||
|
{
|
||||||
|
title: 'storage1',
|
||||||
|
key: '1',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage2',
|
||||||
|
key: '2',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage1',
|
||||||
|
key: '1',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage2',
|
||||||
|
key: '2',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage1',
|
||||||
|
key: '1',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage2',
|
||||||
|
key: '2',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'storage3',
|
||||||
|
key: '3',
|
||||||
|
count: 129,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const storageList = ref(originStorageList.value);
|
||||||
|
|
||||||
|
const searchStorage = debounce(() => {
|
||||||
|
storageList.value = originStorageList.value.filter((item) => item.title.includes(storageKeyword.value));
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => storageKeyword.value,
|
||||||
|
() => {
|
||||||
|
if (storageKeyword.value === '') {
|
||||||
|
storageList.value = originStorageList.value;
|
||||||
|
}
|
||||||
|
searchStorage();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.page {
|
||||||
|
@apply bg-white;
|
||||||
|
|
||||||
|
height: calc(100vh - 88px);
|
||||||
|
border-radius: var(--border-radius-large);
|
||||||
|
.folder {
|
||||||
|
@apply flex cursor-pointer items-center justify-between;
|
||||||
|
|
||||||
|
padding: 8px 4px;
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(var(--primary-1));
|
||||||
|
}
|
||||||
|
.folder-text {
|
||||||
|
@apply flex cursor-pointer items-center;
|
||||||
|
.folder-icon {
|
||||||
|
margin-right: 4px;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
}
|
||||||
|
.folder-name {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
.folder-count {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--color-text-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.folder-text--active {
|
||||||
|
.folder-icon,
|
||||||
|
.folder-name,
|
||||||
|
.folder-count {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-show-type {
|
||||||
|
@apply grid grid-cols-2;
|
||||||
|
|
||||||
|
margin-bottom: 8px;
|
||||||
|
:deep(.arco-radio-button-content) {
|
||||||
|
@apply text-center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1 @@
|
||||||
|
export default {};
|
|
@ -0,0 +1,25 @@
|
||||||
|
export default {
|
||||||
|
'project.fileManagement.myFile': '我的文件',
|
||||||
|
'project.fileManagement.allFile': '全部文件',
|
||||||
|
'project.fileManagement.defaultFile': '默认文件',
|
||||||
|
'project.fileManagement.expandAll': '展开全部子模块',
|
||||||
|
'project.fileManagement.collapseAll': '收起全部子模块',
|
||||||
|
'project.fileManagement.addSubModule': '添加子模块',
|
||||||
|
'project.fileManagement.rename': '重命名',
|
||||||
|
'project.fileManagement.nameNotNull': '名字不能为空',
|
||||||
|
'project.fileManagement.namePlaceholder': '请输入分组名称,按回车键保存',
|
||||||
|
'project.fileManagement.renameSuccess': '重命名成功',
|
||||||
|
'project.fileManagement.addSubModuleSuccess': '添加成功',
|
||||||
|
'project.fileManagement.nameExist': '该层级已有此模块名称',
|
||||||
|
'project.fileManagement.module': '模块',
|
||||||
|
'project.fileManagement.storage': '存储库',
|
||||||
|
'project.fileManagement.folderSearchPlaceholder': '输入名称搜索',
|
||||||
|
'project.fileManagement.delete': '删除',
|
||||||
|
'project.fileManagement.deleteSuccess': '删除成功',
|
||||||
|
'project.fileManagement.deleteTipTitle': '是否删除 `{name}` 模块?',
|
||||||
|
'project.fileManagement.deleteTipContent': '该操作会删除模块及其下所有资源,请谨慎操作!',
|
||||||
|
'project.fileManagement.deleteConfirm': '确认删除',
|
||||||
|
'project.fileManagement.noFolder': '暂无匹配的相关模块',
|
||||||
|
'project.fileManagement.noStorage': '暂无匹配的相关存储库',
|
||||||
|
'project.fileManagement.addFile': '添加文件',
|
||||||
|
};
|
|
@ -437,11 +437,27 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
function searchLog() {
|
function searchLog() {
|
||||||
const ranges = operateRange.value.map((e) => e);
|
const ranges = operateRange.value.map((e) => {
|
||||||
|
if (typeof e === 'object') {
|
||||||
|
return e.value;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
let projectIds = [];
|
||||||
|
let organizationIds = [];
|
||||||
|
|
||||||
|
if (!MENU_LEVEL.includes(level.value) && typeof operateRange.value[0] === 'object') {
|
||||||
|
if (operateRange.value[0].level === MENU_LEVEL[1]) {
|
||||||
|
organizationIds = ranges;
|
||||||
|
} else if (operateRange.value[0].level === MENU_LEVEL[2]) {
|
||||||
|
projectIds = ranges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setLoadListParams({
|
setLoadListParams({
|
||||||
operUser: operUser.value,
|
operUser: operUser.value,
|
||||||
projectIds: level.value === 'PROJECT' && ranges[0] !== 'PROJECT' ? ranges : [],
|
projectIds,
|
||||||
organizationIds: level.value === 'ORGANIZATION' && ranges[0] !== 'ORGANIZATION' ? ranges : [],
|
organizationIds,
|
||||||
type: type.value,
|
type: type.value,
|
||||||
module: _module.value,
|
module: _module.value,
|
||||||
content: content.value,
|
content: content.value,
|
||||||
|
|
|
@ -268,12 +268,14 @@
|
||||||
title: 'system.user.tableColumnOrg',
|
title: 'system.user.tableColumnOrg',
|
||||||
slotName: 'organization',
|
slotName: 'organization',
|
||||||
dataIndex: 'organizationList',
|
dataIndex: 'organizationList',
|
||||||
|
showTooltip: true,
|
||||||
showInTable: true,
|
showInTable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'system.user.tableColumnUserGroup',
|
title: 'system.user.tableColumnUserGroup',
|
||||||
slotName: 'userRole',
|
slotName: 'userRole',
|
||||||
dataIndex: 'userRoleList',
|
dataIndex: 'userRoleList',
|
||||||
|
showTooltip: true,
|
||||||
showInTable: true,
|
showInTable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -297,7 +299,6 @@
|
||||||
{
|
{
|
||||||
tableKey: TableKeyEnum.SYSTEM_USER,
|
tableKey: TableKeyEnum.SYSTEM_USER,
|
||||||
columns,
|
columns,
|
||||||
scroll: { y: 'auto' },
|
|
||||||
selectable: true,
|
selectable: true,
|
||||||
},
|
},
|
||||||
(record) => ({
|
(record) => ({
|
||||||
|
|
Loading…
Reference in New Issue