feat(文件管理): 文件管理页面
This commit is contained in:
parent
eed7fdb0b2
commit
3e4b1ffcf2
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
'common.pleaseSelectMember': 'Please select member',
|
||||
'common.add': 'Add',
|
||||
'common.saveAndContinue': 'Save & Continue',
|
||||
'common.edit': 'Edit',
|
||||
'common.delete': 'Delete',
|
||||
'common.save': 'Save',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export default {
|
||||
'common.pleaseSelectMember': '请选择成员',
|
||||
'common.add': '添加',
|
||||
'common.saveAndContinue': '保存并继续添加',
|
||||
'common.edit': '编辑',
|
||||
'common.delete': '删除',
|
||||
'common.save': '保存',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// 路由白名单,无需校验权限与登录状态
|
||||
export const WHITE_LIST = [
|
||||
{ name: 'notFound', children: [] },
|
||||
{ name: 'invite', children: [] },
|
||||
{ name: 'notFound', path: '/notFound', children: [] },
|
||||
{ name: 'invite', path: '/invite', children: [] },
|
||||
];
|
||||
|
||||
// 左侧菜单底部对齐的菜单数组,数组项为一级路由的name
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { login as userLogin, logout as userLogout, isLogin as userIsLogin } from '@/api/modules/user';
|
||||
import { getHashParameters } from '@/utils';
|
||||
import { setToken, clearToken } from '@/utils/auth';
|
||||
import { removeRouteListener } from '@/utils/route-listener';
|
||||
import useAppStore from '../app';
|
||||
|
@ -98,14 +98,12 @@ const useUserStore = defineStore('user', {
|
|||
const appStore = useAppStore();
|
||||
setToken(res.sessionId, res.csrfToken);
|
||||
this.setInfo(res);
|
||||
const route = useRoute();
|
||||
const urlOrgId = route.query.organizationId;
|
||||
const urlProjectId = route.query.projectId;
|
||||
const { organizationId, projectId } = getHashParameters();
|
||||
// 如果访问页面的时候携带了组织 ID和项目 ID,则不设置
|
||||
if (!urlOrgId) {
|
||||
if (!organizationId) {
|
||||
appStore.setCurrentOrgId(res.lastOrganizationId || '');
|
||||
}
|
||||
if (!urlProjectId) {
|
||||
if (!projectId) {
|
||||
appStore.setCurrentProjectId(res.lastProjectId || '');
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -282,3 +282,23 @@ export const downloadUrlFile = (url: string, fileName: string) => {
|
|||
export const getTime = (time: string): string => {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取 URL 哈希参数
|
||||
* @returns 参数对象
|
||||
*/
|
||||
export const getHashParameters = (): Record<string, string> => {
|
||||
const query = window.location.hash.split('?')[1]; // 获取 URL 哈希参数部分
|
||||
const paramsArray = query.split('&'); // 将哈希参数字符串分割成数组
|
||||
const params: Record<string, string> = {};
|
||||
|
||||
// 遍历数组并解析参数
|
||||
paramsArray.forEach((param) => {
|
||||
const [key, value] = param.split('=');
|
||||
if (key && value) {
|
||||
params[key] = decodeURIComponent(value); // 解码参数值
|
||||
}
|
||||
});
|
||||
|
||||
return params;
|
||||
};
|
||||
|
|
|
@ -77,20 +77,31 @@
|
|||
</div>
|
||||
</template>
|
||||
<div class="flex h-full">
|
||||
<a-spin :loading="fileLoading">
|
||||
<div class="file-detail">
|
||||
<div class="file-detail-icon" @click="handleFileIconClick">
|
||||
<img
|
||||
v-if="fileType === 'image'"
|
||||
src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/6480dbc69be1b5de95010289787d64f1.png~tplv-uwbnlip3yd-webp.webp"
|
||||
class="h-full w-full"
|
||||
<div class="file-detail">
|
||||
<a-skeleton v-if="fileLoading" :loading="fileLoading" :animation="true">
|
||||
<a-skeleton-shape size="large" class="mb-[16px] h-[102px] w-[102px]" />
|
||||
<a-space direction="vertical" class="w-[28%]" size="large">
|
||||
<a-skeleton-line :rows="11" :line-height="24" />
|
||||
</a-space>
|
||||
<a-space direction="vertical" class="ml-[4%] w-[68%]" size="large">
|
||||
<a-skeleton-line :rows="11" :line-height="24" />
|
||||
</a-space>
|
||||
</a-skeleton>
|
||||
<template v-else>
|
||||
<div class="mb-[16px] w-[102px]">
|
||||
<MsPreviewCard
|
||||
mode="hover"
|
||||
:type="fileDetail?.type"
|
||||
:url="fileDetail?.url"
|
||||
:footer-text="t('project.fileManagement.replaceFile')"
|
||||
@click="handleFileIconClick"
|
||||
/>
|
||||
<MsIcon v-else :type="FileIconMap[fileType][UploadStatus.done]" class="h-full w-full" />
|
||||
<div class="file-detail-icon-footer">
|
||||
{{ t('project.fileManagement.replaceFile') }}
|
||||
</div>
|
||||
</div>
|
||||
<MsDescription :descriptions="fileDescriptions" label-width="80px" :add-tag-func="addFileTag">
|
||||
<MsDescription
|
||||
:descriptions="fileDescriptions"
|
||||
:label-width="currentLocale === 'zh-CN' ? '80px' : '100px'"
|
||||
:add-tag-func="addFileTag"
|
||||
>
|
||||
<template #value="{ item }">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<a-tooltip
|
||||
|
@ -99,7 +110,7 @@
|
|||
:disabled="item.value === undefined || item.value === null || item.value?.toString() === ''"
|
||||
mini
|
||||
>
|
||||
<div :class="['one-line-text', item.key === 'name' ? 'max-w-[100px]' : '']">
|
||||
<div :class="['one-line-text', 'flex-1']">
|
||||
{{
|
||||
item.value === undefined || item.value === null || item.value?.toString() === ''
|
||||
? '-'
|
||||
|
@ -108,18 +119,43 @@
|
|||
</div>
|
||||
</a-tooltip>
|
||||
<template v-if="item.key === 'name'">
|
||||
<popConfirm mode="rename" :title="t('common.rename')" :all-names="[]">
|
||||
<MsButton class="!mr-[4px] ml-[8px]">{{ t('common.rename') }}</MsButton>
|
||||
<popConfirm
|
||||
mode="rename"
|
||||
:field-config="{ placeholder: t('project.fileManagement.fileNamePlaceholder') }"
|
||||
:all-names="[]"
|
||||
>
|
||||
<MsButton class="!mr-0 ml-[8px]">{{ t('common.rename') }}</MsButton>
|
||||
</popConfirm>
|
||||
<template v-if="fileType === 'image'">
|
||||
<a-divider
|
||||
direction="vertical"
|
||||
class="mx-[8px] min-h-[12px] rounded-[var(--border-radius-small)]"
|
||||
/>
|
||||
<MsButton class="ml-0" @click="previewVisible = true">
|
||||
{{ t('common.preview') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="item.key === 'desc'">
|
||||
<popConfirm
|
||||
mode="rename"
|
||||
:title="t('project.fileManagement.desc')"
|
||||
:field-config="{
|
||||
field: item.value as string,
|
||||
placeholder: t('project.fileManagement.descPlaceholder'),
|
||||
maxLength: 250,
|
||||
isTextArea: true,
|
||||
}"
|
||||
:all-names="[]"
|
||||
>
|
||||
<MsButton class="ml-[8px]"><MsIcon type="icon-icon_edit_outlined"></MsIcon></MsButton>
|
||||
</popConfirm>
|
||||
<MsButton v-if="fileType === 'image'" class="ml-0" @click="previewVisible = true">
|
||||
{{ t('common.preview') }}
|
||||
</MsButton>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</MsDescription>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
</div>
|
||||
<div class="file-relation">
|
||||
<a-tabs v-model:active-key="activeTab" :disabled="fileLoading" class="no-content">
|
||||
<a-tab-pane key="case" :title="t('project.fileManagement.cases')" />
|
||||
|
@ -170,15 +206,16 @@
|
|||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import { getFileEnum, FileIconMap } from '@/components/pure/ms-upload/iconMap';
|
||||
import { getFileEnum } from '@/components/pure/ms-upload/iconMap';
|
||||
import MsDescription, { type Description } from '@/components/pure/ms-description/index.vue';
|
||||
import popConfirm from './popConfirm.vue';
|
||||
import { UploadStatus } from '@/enums/uploadEnum';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import { getFileDetail, getFileCases, getFileVersions } from '@/api/modules/project-management/fileManagement';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import MsPreviewCard from '@/components/business/ms-thumbnail-card/index.vue';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { downloadUrlFile } from '@/utils';
|
||||
import useLocale from '@/locale/useLocale';
|
||||
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
|
||||
|
@ -193,6 +230,7 @@
|
|||
|
||||
const { file: newFile, open } = useFileSystemAccess();
|
||||
const { t } = useI18n();
|
||||
const { currentLocale } = useLocale();
|
||||
|
||||
const innerVisible = ref(false);
|
||||
const fileDetail = ref();
|
||||
|
@ -228,6 +266,11 @@
|
|||
value: fileDetail.value.name,
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
label: t('project.fileManagement.desc'),
|
||||
value: fileDetail.value.desc,
|
||||
key: 'desc',
|
||||
},
|
||||
{
|
||||
label: t('project.fileManagement.type'),
|
||||
value: fileDetail.value.type,
|
||||
|
@ -289,9 +332,6 @@
|
|||
() => props.fileId,
|
||||
() => {
|
||||
initFileDetail();
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -414,14 +454,17 @@
|
|||
{
|
||||
title: 'project.fileManagement.record',
|
||||
dataIndex: 'record',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.creator',
|
||||
dataIndex: 'creator',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.createTime',
|
||||
dataIndex: 'createTime',
|
||||
width: 180,
|
||||
},
|
||||
];
|
||||
const {
|
||||
|
@ -436,10 +479,12 @@
|
|||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (activeTab.value === 'case') {
|
||||
loadCaseList();
|
||||
} else {
|
||||
loadVersionList();
|
||||
if (innerVisible.value) {
|
||||
if (activeTab.value === 'case') {
|
||||
loadCaseList();
|
||||
} else {
|
||||
loadVersionList();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -451,30 +496,6 @@
|
|||
padding: 16px;
|
||||
width: 300px;
|
||||
border-right: 1px solid var(--color-text-n8);
|
||||
.file-detail-icon {
|
||||
@apply relative inline-block cursor-pointer overflow-hidden;
|
||||
|
||||
margin-bottom: 16px;
|
||||
width: 102px;
|
||||
height: 102px;
|
||||
border-radius: var(--border-radius-small);
|
||||
background-color: var(--color-text-n9);
|
||||
&:hover {
|
||||
.file-detail-icon-footer {
|
||||
@apply visible;
|
||||
}
|
||||
}
|
||||
.file-detail-icon-footer {
|
||||
@apply invisible absolute w-full text-center;
|
||||
|
||||
bottom: 0;
|
||||
padding: 2px 0;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
background-color: #00000050;
|
||||
}
|
||||
}
|
||||
}
|
||||
.file-relation {
|
||||
width: 660px;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
v-model:model-value="moduleKeyword"
|
||||
:placeholder="t('project.fileManagement.folderSearchPlaceholder')"
|
||||
allow-clear
|
||||
class="mb-[8px]"
|
||||
class="mb-[16px]"
|
||||
></a-input>
|
||||
<MsTree
|
||||
v-model:focus-node-key="focusNodeKey"
|
||||
|
@ -13,7 +13,8 @@
|
|||
:node-more-actions="folderMoreActions"
|
||||
:expand-all="props.isExpandAll"
|
||||
:empty-text="t('project.fileManagement.noFolder')"
|
||||
draggable
|
||||
:draggable="!props.isModal"
|
||||
:virtual-list-props="virtualListProps"
|
||||
block-node
|
||||
@select="folderNodeSelect"
|
||||
@more-action-select="handleFolderMoreSelect"
|
||||
|
@ -21,15 +22,15 @@
|
|||
>
|
||||
<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>
|
||||
<span v-if="!props.isModal" class="ml-[4px] text-[var(--color-text-4)]">({{ nodeData.count }})</span>
|
||||
</template>
|
||||
<template #extra="nodeData">
|
||||
<template v-if="!props.isModal" #extra="nodeData">
|
||||
<popConfirm mode="add" :all-names="[]" @close="resetFocusNodeKey">
|
||||
<MsButton type="icon" 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">
|
||||
<popConfirm mode="rename" :field-config="{ field: renameFolderTitle }" :all-names="[]" @close="resetFocusNodeKey">
|
||||
<span :id="`renameSpan${nodeData.key}`" class="relative"></span>
|
||||
</popConfirm>
|
||||
</template>
|
||||
|
@ -37,7 +38,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
@ -52,12 +53,23 @@
|
|||
const props = defineProps<{
|
||||
isExpandAll: boolean;
|
||||
selectedKeys?: Array<string | number>; // 选中的节点 key
|
||||
isModal?: boolean; // 是否是弹窗模式
|
||||
}>();
|
||||
const emit = defineEmits(['update:selectedKeys', 'folderNodeSelect']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openModal } = useModal();
|
||||
|
||||
const virtualListProps = computed(() => {
|
||||
if (props.isModal) {
|
||||
return {
|
||||
height: 'calc(60vh - 190px)',
|
||||
};
|
||||
}
|
||||
return {
|
||||
height: 'calc(100vh - 320px)',
|
||||
};
|
||||
});
|
||||
const moduleKeyword = ref('');
|
||||
const folderTree = ref([
|
||||
{
|
||||
|
@ -70,6 +82,36 @@
|
|||
key: 'node2',
|
||||
count: 28,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -87,6 +129,36 @@
|
|||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node4',
|
||||
count: 138,
|
||||
},
|
||||
{
|
||||
title: 'Leaf',
|
||||
key: 'node5',
|
||||
count: 108,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -11,21 +11,35 @@
|
|||
>
|
||||
<template #content>
|
||||
<div class="mb-[8px] font-medium">
|
||||
{{ props.mode === 'add' ? t('project.fileManagement.addSubModule') : t('project.fileManagement.rename') }}
|
||||
{{
|
||||
props.title ||
|
||||
(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"
|
||||
field="field"
|
||||
: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')"
|
||||
<a-textarea
|
||||
v-if="props.fieldConfig?.isTextArea"
|
||||
v-model:model-value="form.field"
|
||||
:max-length="props.fieldConfig?.maxLength"
|
||||
:auto-size="{ maxRows: 4 }"
|
||||
:placeholder="props.fieldConfig?.placeholder || t('project.fileManagement.namePlaceholder')"
|
||||
class="w-[245px]"
|
||||
@press-enter="beforeConfirm(undefined)"
|
||||
></a-input>
|
||||
>
|
||||
</a-textarea>
|
||||
<a-input
|
||||
v-else
|
||||
v-model:model-value="form.field"
|
||||
:max-length="props.fieldConfig?.maxLength"
|
||||
:placeholder="props.fieldConfig?.placeholder || t('project.fileManagement.namePlaceholder')"
|
||||
class="w-[245px]"
|
||||
@press-enter="beforeConfirm(undefined)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</template>
|
||||
|
@ -34,11 +48,19 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { onBeforeMount, ref, watch } from 'vue';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import type { FormInstance, FieldRule } from '@arco-design/web-vue';
|
||||
|
||||
interface FieldConfig {
|
||||
field?: string;
|
||||
rules?: FieldRule[];
|
||||
placeholder?: string;
|
||||
maxLength?: number;
|
||||
isTextArea?: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
mode: 'add' | 'rename';
|
||||
|
@ -46,7 +68,7 @@
|
|||
title?: string;
|
||||
allNames: string[];
|
||||
popupContainer?: string;
|
||||
placeholder?: string;
|
||||
fieldConfig?: FieldConfig;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'close']);
|
||||
|
@ -55,15 +77,15 @@
|
|||
|
||||
const innerVisible = ref(props.visible || false);
|
||||
const form = ref({
|
||||
name: props.title || '',
|
||||
field: props.fieldConfig?.field || '',
|
||||
});
|
||||
const formRef = ref<FormInstance>();
|
||||
const loading = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.title,
|
||||
() => props.fieldConfig?.field,
|
||||
(val) => {
|
||||
form.value.name = val || '';
|
||||
form.value.field = val || '';
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -117,7 +139,7 @@
|
|||
}
|
||||
|
||||
function reset() {
|
||||
form.value.name = '';
|
||||
form.value.field = '';
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
<template>
|
||||
<div class="p-[24px]">
|
||||
<div class="flex h-[calc(100vh-88px)] flex-col overflow-hidden p-[24px]">
|
||||
<div class="header">
|
||||
<a-button type="primary" @click="uploadDrawerVisible = true">{{ t('project.fileManagement.addFile') }}</a-button>
|
||||
<a-button type="primary" @click="handleAddClick">{{ t('project.fileManagement.addFile') }}</a-button>
|
||||
<div class="header-right">
|
||||
<a-select v-model="tableFileType" class="w-[240px]" @change="searchList">
|
||||
<a-option key="" value="">{{ t('common.all') }}</a-option>
|
||||
<a-option v-for="item of tableFileTypeOptions" :key="item" :value="item">
|
||||
{{ item }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<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-if="props.activeFolderType === 'folder'"
|
||||
v-model:model-value="fileType"
|
||||
|
@ -25,7 +31,15 @@
|
|||
</a-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<ms-base-table
|
||||
v-if="showType === 'list'"
|
||||
v-bind="propsRes"
|
||||
:action-config="fileType === 'module' ? moduleFileBatchActions : storageFileBatchActions"
|
||||
no-disable
|
||||
v-on="propsEvent"
|
||||
@selected-change="handleTableSelect"
|
||||
@batch-action="handleTableBatch"
|
||||
>
|
||||
<template #name="{ record, rowIndex }">
|
||||
<a-button type="text" class="px-0" @click="openFileDetail(record.id, rowIndex)">{{ record.name }}</a-button>
|
||||
</template>
|
||||
|
@ -38,18 +52,37 @@
|
|||
</MsButton>
|
||||
<MsTableMoreAction
|
||||
:list="record.type === 'JAR' ? jarFileActions : normalFileActions"
|
||||
@select="handleSelect($event, record)"
|
||||
></MsTableMoreAction>
|
||||
@select="handleMoreActionSelect($event, record)"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="keyword.trim() === ''" #empty>
|
||||
<div class="flex items-center justify-center p-[8px] text-[var(--color-text-4)]">
|
||||
{{ t('project.fileManagement.tableNoFile') }}
|
||||
<MsButton class="ml-[8px]" @click="uploadDrawerVisible = true">
|
||||
<MsButton class="ml-[8px]" @click="handleAddClick">
|
||||
{{ t('project.fileManagement.addFile') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<MsCardList
|
||||
v-else-if="showType === 'card'"
|
||||
mode="remote"
|
||||
:remote-func="getFileList"
|
||||
:shadow-limit="50"
|
||||
:card-min-width="102"
|
||||
class="flex-1"
|
||||
>
|
||||
<template #item="{ item, index }">
|
||||
<MsThumbnailCard
|
||||
:type="item.type"
|
||||
:url="item.url"
|
||||
:footer-text="item.name"
|
||||
:more-actions="item.type === 'JAR' ? jarFileActions : normalFileActions"
|
||||
@click="openFileDetail(item.id, index)"
|
||||
@action-select="handleMoreActionSelect($event, item)"
|
||||
/>
|
||||
</template>
|
||||
</MsCardList>
|
||||
</div>
|
||||
<MsDrawer v-model:visible="uploadDrawerVisible" :title="t('project.fileManagement.addFile')" :width="680">
|
||||
<div class="mb-[8px] flex items-center justify-between text-[var(--color-text-1)]">
|
||||
|
@ -123,6 +156,86 @@
|
|||
</a-button>
|
||||
</template>
|
||||
</MsDrawer>
|
||||
<a-modal
|
||||
v-model:visible="storageDialogVisible"
|
||||
:title="t('project.fileManagement.addFile')"
|
||||
title-align="start"
|
||||
class="ms-modal-form ms-modal-medium"
|
||||
:mask-closable="false"
|
||||
@close="handleStorageModalCancel"
|
||||
>
|
||||
<a-form ref="storageFormRef" class="rounded-[4px]" :model="storageForm" layout="vertical">
|
||||
<a-form-item
|
||||
field="branch"
|
||||
:label="t('project.fileManagement.gitBranch')"
|
||||
:rules="[{ required: true, message: t('project.fileManagement.gitBranchNotNull') }]"
|
||||
required
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="storageForm.branch"
|
||||
:placeholder="t('project.fileManagement.gitBranchPlaceholder')"
|
||||
:max-length="250"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="path"
|
||||
:label="t('project.fileManagement.gitFilePath')"
|
||||
:rules="[{ required: true, message: t('project.fileManagement.gitFilePathNotNull') }]"
|
||||
required
|
||||
asterisk-position="end"
|
||||
>
|
||||
<a-input
|
||||
v-model:model-value="storageForm.path"
|
||||
:placeholder="t('project.fileManagement.gitFilePathPlaceholder')"
|
||||
:max-length="250"
|
||||
></a-input>
|
||||
<MsFormItemSub :text="t('project.fileManagement.gitFilePathSub')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-button type="secondary" :disabled="storageModalLoading" @click="handleStorageModalCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button type="secondary" :loading="storageModalLoading" @click="saveAndContinue">
|
||||
{{ t('common.saveAndContinue') }}
|
||||
</a-button>
|
||||
<a-button type="primary" :loading="storageModalLoading" @click="beforeAddStorageFile">
|
||||
{{ t('common.add') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
<a-modal
|
||||
v-model:visible="moveModalVisible"
|
||||
title-align="start"
|
||||
class="ms-modal-no-padding ms-modal-small"
|
||||
:mask-closable="false"
|
||||
:ok-text="t('project.fileManagement.batchMoveConfirm')"
|
||||
:ok-button-props="{ disabled: selectedModuleKeys.length === 0 }"
|
||||
:cancel-button-props="{ disabled: batchMoveFileLoading }"
|
||||
:on-before-ok="batchMoveFile"
|
||||
@close="handleMoveFileModalCancel"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center">
|
||||
{{ isBatchMove ? t('project.fileManagement.batchMoveTitle') : t('project.fileManagement.singleMoveTitle') }}
|
||||
<div class="ml-[4px] text-[var(--color-text-4)]">
|
||||
{{
|
||||
isBatchMove
|
||||
? t('project.fileManagement.batchMoveTitleSub', { count: tableSelected.length })
|
||||
: `(${activeFile?.name})`
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<folderTree
|
||||
v-if="moveModalVisible"
|
||||
v-model:selected-keys="selectedModuleKeys"
|
||||
:is-expand-all="true"
|
||||
is-modal
|
||||
@folder-node-select="folderNodeSelect"
|
||||
/>
|
||||
</a-modal>
|
||||
<fileDetailDrawerVue
|
||||
v-model:visible="showDetailDrawer"
|
||||
:file-id="activeFileId"
|
||||
|
@ -136,7 +249,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { Message, ValidatedError } from '@arco-design/web-vue';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useTableStore from '@/store/modules/ms-table';
|
||||
|
@ -156,9 +269,14 @@
|
|||
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import MsTagGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
import MsThumbnailCard from '@/components/business/ms-thumbnail-card/index.vue';
|
||||
import MsCardList from '@/components/business/ms-card-list/index.vue';
|
||||
import fileDetailDrawerVue from './fileDetailDrawer.vue';
|
||||
import folderTree from './folderTree.vue';
|
||||
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import type { BatchActionParams, MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import type { MsFileItem, UploadType } from '@/components/pure/ms-upload/types';
|
||||
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
|
||||
|
@ -172,9 +290,8 @@
|
|||
const asyncTaskStore = useAsyncTaskStore();
|
||||
const { openModal } = useModal();
|
||||
|
||||
const keyword = ref('');
|
||||
const fileType = ref('module');
|
||||
const acceptType = ref<UploadType>('none');
|
||||
const fileType = ref('module'); // 当前查看的文件类型,模块/存储库
|
||||
const acceptType = ref<UploadType>('none'); // 模块-上传文件类型
|
||||
const isUploading = ref(false);
|
||||
|
||||
watch(
|
||||
|
@ -188,15 +305,11 @@
|
|||
}
|
||||
);
|
||||
|
||||
function changeFileType() {
|
||||
console.log(fileType.value);
|
||||
}
|
||||
function changeFileType() {}
|
||||
|
||||
const showType = ref('list');
|
||||
const showType = ref<'list' | 'card'>('list'); // 文件列表展示形式
|
||||
|
||||
function changeShowType() {
|
||||
console.log(showType.value);
|
||||
}
|
||||
function changeShowType() {}
|
||||
|
||||
function getCardClass(type: 'none' | 'jar') {
|
||||
if (acceptType.value !== type && isUploading.value) {
|
||||
|
@ -209,6 +322,13 @@
|
|||
}
|
||||
|
||||
const normalFileActions: ActionsItem[] = [
|
||||
{
|
||||
label: 'project.fileManagement.move',
|
||||
eventTag: 'move',
|
||||
},
|
||||
{
|
||||
isDivider: true,
|
||||
},
|
||||
{
|
||||
label: 'project.fileManagement.delete',
|
||||
eventTag: 'delete',
|
||||
|
@ -217,6 +337,10 @@
|
|||
];
|
||||
|
||||
const jarFileActions: ActionsItem[] = [
|
||||
{
|
||||
label: 'project.fileManagement.move',
|
||||
eventTag: 'move',
|
||||
},
|
||||
{
|
||||
label: 'common.disable',
|
||||
eventTag: 'disabled',
|
||||
|
@ -241,6 +365,7 @@
|
|||
{
|
||||
title: 'project.fileManagement.type',
|
||||
dataIndex: 'type',
|
||||
width: 90,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.tag',
|
||||
|
@ -251,20 +376,22 @@
|
|||
{
|
||||
title: 'project.fileManagement.creator',
|
||||
dataIndex: 'creator',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.updater',
|
||||
dataIndex: 'updater',
|
||||
showTooltip: true,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.updateTime',
|
||||
dataIndex: 'updateTime',
|
||||
width: 170,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: 'project.fileManagement.createTime',
|
||||
dataIndex: 'createTime',
|
||||
width: 170,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: 'common.operation',
|
||||
|
@ -276,32 +403,67 @@
|
|||
];
|
||||
const tableStore = useTableStore();
|
||||
tableStore.initColumn(TableKeyEnum.FILE_MANAGEMENT_FILE, columns, 'drawer');
|
||||
const { propsRes, propsEvent, loadList } = useTable(getFileList, {
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getFileList, {
|
||||
tableKey: TableKeyEnum.FILE_MANAGEMENT_FILE,
|
||||
columns,
|
||||
showSetting: true,
|
||||
selectable: true,
|
||||
showSelectAll: true,
|
||||
});
|
||||
const moduleFileBatchActions = {
|
||||
baseAction: [
|
||||
{
|
||||
label: 'project.fileManagement.download',
|
||||
eventTag: 'download',
|
||||
},
|
||||
{
|
||||
label: 'project.fileManagement.move',
|
||||
eventTag: 'move',
|
||||
},
|
||||
{
|
||||
label: 'project.fileManagement.delete',
|
||||
eventTag: 'delete',
|
||||
danger: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
const storageFileBatchActions = {
|
||||
baseAction: [
|
||||
{
|
||||
label: 'project.fileManagement.download',
|
||||
eventTag: 'download',
|
||||
},
|
||||
{
|
||||
label: 'project.fileManagement.delete',
|
||||
eventTag: 'delete',
|
||||
danger: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
const tableSelected = ref<(string | number)[]>([]);
|
||||
|
||||
watch(
|
||||
() => props.activeFolder,
|
||||
() => {
|
||||
keyword.value = '';
|
||||
debounce(loadList, 200)();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function downloadFile(url: string, name: string) {
|
||||
downloadUrlFile(url, name);
|
||||
/**
|
||||
* 处理表格选中
|
||||
*/
|
||||
function handleTableSelect(arr: (string | number)[]) {
|
||||
tableSelected.value = arr;
|
||||
}
|
||||
|
||||
function batchDownload() {}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*/
|
||||
function delFile(record: any) {
|
||||
function delFile(record: any, isBatch?: boolean) {
|
||||
let title = t('project.fileManagement.deleteFileTipTitle', { name: characterLimit(record?.name) });
|
||||
let selectIds = [record?.id];
|
||||
if (isBatch) {
|
||||
title = t('project.fileManagement.batchDeleteFileTipTitle', { count: tableSelected.value.length });
|
||||
selectIds = tableSelected.value as string[];
|
||||
}
|
||||
openModal({
|
||||
type: 'error',
|
||||
title: t('project.fileManagement.deleteFileTipTitle', { name: characterLimit(record.name) }),
|
||||
title,
|
||||
content: t('project.fileManagement.deleteFileTipContent'),
|
||||
okText: t('common.confirmDelete'),
|
||||
cancelText: t('common.cancel'),
|
||||
|
@ -311,6 +473,8 @@
|
|||
maskClosable: false,
|
||||
onBeforeOk: async () => {
|
||||
try {
|
||||
console.log(selectIds);
|
||||
|
||||
Message.success(t('common.deleteSuccess'));
|
||||
loadList();
|
||||
} catch (error) {
|
||||
|
@ -321,6 +485,102 @@
|
|||
});
|
||||
}
|
||||
|
||||
const moveModalVisible = ref(false); // 移动文件弹窗
|
||||
const selectedModuleKeys = ref<(string | number)[]>([]); // 移动文件搜索关键字
|
||||
const isBatchMove = ref(false); // 是否批量移动文件
|
||||
const activeFile = ref<any>(null); // 当前查看的文件信息
|
||||
|
||||
/**
|
||||
* 处理文件夹树节点选中事件
|
||||
*/
|
||||
function folderNodeSelect(keys: (string | number)[]) {
|
||||
selectedModuleKeys.value = keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理表格选中后批量操作
|
||||
* @param event 批量操作事件对象
|
||||
*/
|
||||
function handleTableBatch(event: BatchActionParams) {
|
||||
switch (event.eventTag) {
|
||||
case 'download':
|
||||
batchDownload();
|
||||
break;
|
||||
case 'move':
|
||||
moveModalVisible.value = true;
|
||||
isBatchMove.value = true;
|
||||
break;
|
||||
case 'delete':
|
||||
delFile(null, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const batchMoveFileLoading = ref(false);
|
||||
/**
|
||||
* 单个/批量移动文件
|
||||
*/
|
||||
async function batchMoveFile() {
|
||||
try {
|
||||
batchMoveFileLoading.value = true;
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => resolve(true), 2000);
|
||||
});
|
||||
Message.success(t('project.fileManagement.batchMoveSuccess'));
|
||||
if (isBatchMove.value) {
|
||||
tableSelected.value = [];
|
||||
isBatchMove.value = false;
|
||||
} else {
|
||||
activeFile.value = null;
|
||||
}
|
||||
loadList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
batchMoveFileLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleMoveFileModalCancel() {
|
||||
moveModalVisible.value = false;
|
||||
selectedModuleKeys.value = [];
|
||||
}
|
||||
|
||||
const keyword = ref('');
|
||||
const tableFileType = ref('');
|
||||
const tableFileTypeOptions = ref(['JPG', 'PNG']);
|
||||
|
||||
const searchList = debounce(() => {
|
||||
setLoadListParams({
|
||||
fileType: tableFileType.value,
|
||||
keyword: keyword.value,
|
||||
});
|
||||
loadList();
|
||||
}, 300);
|
||||
|
||||
watch(
|
||||
() => props.activeFolder,
|
||||
() => {
|
||||
keyword.value = '';
|
||||
searchList();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => keyword.value,
|
||||
() => {
|
||||
searchList();
|
||||
}
|
||||
);
|
||||
|
||||
function downloadFile(url: string, name: string) {
|
||||
downloadUrlFile(url, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用 jar 文件
|
||||
*/
|
||||
|
@ -348,8 +608,13 @@
|
|||
* 处理表格更多按钮事件
|
||||
* @param item
|
||||
*/
|
||||
function handleSelect(item: ActionsItem, record: any) {
|
||||
function handleMoreActionSelect(item: ActionsItem, record: any) {
|
||||
switch (item.eventTag) {
|
||||
case 'move':
|
||||
isBatchMove.value = false;
|
||||
activeFile.value = record;
|
||||
moveModalVisible.value = true;
|
||||
break;
|
||||
case 'delete':
|
||||
delFile(record);
|
||||
break;
|
||||
|
@ -376,7 +641,7 @@
|
|||
propsRes.value.msPagination!.total
|
||||
);
|
||||
|
||||
async function openFileDetail(id: string, index: number) {
|
||||
async function openFileDetail(id: string | number, index: number) {
|
||||
showDetailDrawer.value = true;
|
||||
activeFileId.value = id;
|
||||
activeFileIndex.value = index;
|
||||
|
@ -414,15 +679,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
const uploadDrawerVisible = ref(false);
|
||||
const uploadDrawerVisible = ref(false); // 模块-上传文件抽屉
|
||||
const fileList = ref<MsFileItem[]>(asyncTaskStore.uploadFileTask.fileList);
|
||||
|
||||
// 是否非上传中状态
|
||||
const noWaitingUpload = computed(
|
||||
() =>
|
||||
fileList.value.filter((e) => e.status && (e.status === UploadStatus.init || e.status === UploadStatus.uploading))
|
||||
.length === 0
|
||||
);
|
||||
|
||||
/**
|
||||
* 设置上传文件类型
|
||||
* @param type 文件类型
|
||||
*/
|
||||
function setAcceptType(type: UploadType) {
|
||||
if (isUploading.value) return;
|
||||
acceptType.value = type;
|
||||
|
@ -461,6 +730,9 @@
|
|||
isUploading.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台上传
|
||||
*/
|
||||
function backstageUpload() {
|
||||
fileListRef.value?.backstageUpload();
|
||||
uploadDrawerVisible.value = false;
|
||||
|
@ -525,6 +797,81 @@
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
const storageDialogVisible = ref(false); // 存储库-上传文件弹窗
|
||||
const storageForm = ref({
|
||||
branch: '',
|
||||
path: '',
|
||||
}); // 存储库-上传文件表单
|
||||
const storageFormRef = ref<FormInstance>(); // 存储库-上传文件表单ref
|
||||
const storageModalLoading = ref(false); // 存储库-上传文件弹窗loading
|
||||
|
||||
/**
|
||||
* 处理添加文件按钮点击事件,根据当前查看的文件类型,打开不同的弹窗
|
||||
*/
|
||||
function handleAddClick() {
|
||||
if (fileType.value === 'module') {
|
||||
uploadDrawerVisible.value = true;
|
||||
} else if (fileType.value === 'storage') {
|
||||
storageDialogVisible.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleStorageModalCancel() {
|
||||
storageFormRef.value?.resetFields();
|
||||
storageDialogVisible.value = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储库-添加文件
|
||||
* @param isContinue 是否继续添加
|
||||
*/
|
||||
async function addStorageFile(isContinue?: boolean) {
|
||||
const params = {
|
||||
branch: storageForm.value.branch,
|
||||
path: storageForm.value.path,
|
||||
};
|
||||
// await batchCreateUser(params);
|
||||
Message.success(t('common.addSuccess'));
|
||||
if (!isContinue) {
|
||||
storageDialogVisible.value = false;
|
||||
}
|
||||
loadList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储库-触发添加文件表单校验
|
||||
* @param cb 校验通过后执行回调
|
||||
*/
|
||||
function storageFormValidate(cb: () => Promise<any>) {
|
||||
storageFormRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
storageModalLoading.value = true;
|
||||
await cb();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
storageModalLoading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveAndContinue() {
|
||||
storageFormValidate(async () => {
|
||||
await addStorageFile(true);
|
||||
storageFormRef.value?.resetFields();
|
||||
});
|
||||
}
|
||||
|
||||
function beforeAddStorageFile() {
|
||||
storageFormValidate(async () => {
|
||||
await addStorageFile();
|
||||
handleStorageModalCancel();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -583,4 +930,13 @@
|
|||
color: var(--color-text-4);
|
||||
}
|
||||
}
|
||||
.card-list {
|
||||
@apply grid flex-1 overflow-auto;
|
||||
.ms-scroll-bar();
|
||||
.ms-container--shadow();
|
||||
|
||||
gap: 24px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(102px, 1fr));
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -29,7 +29,12 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #itemAction="{ item }">
|
||||
<popConfirm mode="rename" :title="renameStorageTitle" :all-names="[]" @close="resetFocusItemKey">
|
||||
<popConfirm
|
||||
mode="rename"
|
||||
:field-config="{ field: renameStorageTitle }"
|
||||
:all-names="[]"
|
||||
@close="resetFocusItemKey"
|
||||
>
|
||||
<span :id="`renameSpan${item.key}`" class="relative"></span>
|
||||
</popConfirm>
|
||||
</template>
|
||||
|
|
|
@ -68,7 +68,11 @@ export default {
|
|||
'After cancellation, files that have not been successfully uploaded will not be saved, so please operate with caution!',
|
||||
'project.fileManagement.cancelConfirm': 'Cancel upload',
|
||||
'project.fileManagement.name': 'File name',
|
||||
'project.fileManagement.type': 'Type',
|
||||
'project.fileManagement.type': 'Format',
|
||||
'project.fileManagement.fileNamePlaceholder': 'Please enter the file name and press Enter to save',
|
||||
'project.fileManagement.desc': 'Description',
|
||||
'project.fileManagement.updateDesc': 'Update description',
|
||||
'project.fileManagement.descPlaceholder': 'Please enter content',
|
||||
'project.fileManagement.tag': 'Tag',
|
||||
'project.fileManagement.creator': 'Creator',
|
||||
'project.fileManagement.updater': 'Updater',
|
||||
|
@ -76,10 +80,47 @@ export default {
|
|||
'project.fileManagement.createTime': 'Created time',
|
||||
'project.fileManagement.download': 'Download',
|
||||
'project.fileManagement.disabled': 'Disable',
|
||||
'project.fileManagement.move': 'Move to',
|
||||
'project.fileManagement.deleteFileTipTitle': 'Are you sure you want to delete the file {name}?',
|
||||
'project.fileManagement.deleteFileTipContent':
|
||||
'After deletion, the use cases associated with the file will fail to execute. Please operate with caution!',
|
||||
'project.fileManagement.disabledFileTipTitle': 'Are you sure you want to disable the file {name}?',
|
||||
'project.fileManagement.disabledFileTipContent':
|
||||
'After being disabled, the custom script associated with the file will fail to execute, so please operate with caution!',
|
||||
'project.fileManagement.batchDeleteFileTipTitle': 'Are you sure you want to delete these {count} files?',
|
||||
'project.fileManagement.batchDeleteFileTipContent':
|
||||
'After deletion, the use cases associated with these files will fail to execute, so please operate with caution!',
|
||||
'project.fileManagement.detail': 'File details',
|
||||
'project.fileManagement.prev': 'Prev',
|
||||
'project.fileManagement.noPrev': 'Currently the first',
|
||||
'project.fileManagement.next': 'Next',
|
||||
'project.fileManagement.noNext': 'Currently the last one',
|
||||
'project.fileManagement.updateFile': 'Update file',
|
||||
'project.fileManagement.replaceFile': 'Replace file',
|
||||
'project.fileManagement.replaceFileSuccess': 'File replacement successful',
|
||||
'project.fileManagement.size': 'File size',
|
||||
'project.fileManagement.fileModule': 'Module',
|
||||
'project.fileManagement.gitBranch': 'Git branch',
|
||||
'project.fileManagement.gitPath': 'Git path',
|
||||
'project.fileManagement.gitVersion': 'Git version',
|
||||
'project.fileManagement.cases': 'Related Use Cases',
|
||||
'project.fileManagement.versionHistory': 'Version history',
|
||||
'project.fileManagement.updateCaseFile': 'Update use case file',
|
||||
'project.fileManagement.id': 'ID',
|
||||
'project.fileManagement.fileVersion': 'File version',
|
||||
'project.fileManagement.record': 'Update history',
|
||||
'project.fileManagement.caseList': 'Use case list',
|
||||
'project.fileManagement.search': 'Enter name to search',
|
||||
'project.fileManagement.gitBranchNotNull': 'Git branch cannot be empty',
|
||||
'project.fileManagement.gitBranchPlaceholder': 'Please enter the git branch, such as: master',
|
||||
'project.fileManagement.gitFilePath': 'File path',
|
||||
'project.fileManagement.gitFilePathNotNull': 'File path cannot be empty',
|
||||
'project.fileManagement.gitFilePathPlaceholder': 'Please enter the file path, such as: /xxxxxx.xx',
|
||||
'project.fileManagement.gitFilePathSub': 'No need to add file path separator before root directory: /',
|
||||
'project.fileManagement.batchMoveTitle': 'Batch move',
|
||||
'project.fileManagement.singleMoveTitle': 'Move',
|
||||
'project.fileManagement.batchMoveTitleSub': '({count} files selected)',
|
||||
'project.fileManagement.batchMoveSearchPlaceholder': 'Please enter the module name to search',
|
||||
'project.fileManagement.batchMoveConfirm': 'Move to selected module',
|
||||
'project.fileManagement.batchMoveSuccess': 'File moved successfully',
|
||||
};
|
||||
|
|
|
@ -64,7 +64,11 @@ export default {
|
|||
'project.fileManagement.cancelTipContent': '取消后,未上传成功的文件不会被保存,请谨慎操作!',
|
||||
'project.fileManagement.cancelConfirm': '取消上传',
|
||||
'project.fileManagement.name': '文件名称',
|
||||
'project.fileManagement.type': '文件类型',
|
||||
'project.fileManagement.fileNamePlaceholder': '请输入文件名称,按回车键保存',
|
||||
'project.fileManagement.desc': '文件描述',
|
||||
'project.fileManagement.updateDesc': '更新描述',
|
||||
'project.fileManagement.descPlaceholder': '请输入内容',
|
||||
'project.fileManagement.type': '文件格式',
|
||||
'project.fileManagement.tag': '标签',
|
||||
'project.fileManagement.creator': '创建人',
|
||||
'project.fileManagement.updater': '更新人',
|
||||
|
@ -72,10 +76,12 @@ export default {
|
|||
'project.fileManagement.createTime': '创建时间',
|
||||
'project.fileManagement.download': '下载',
|
||||
'project.fileManagement.disabled': '禁用',
|
||||
'project.fileManagement.move': '移动',
|
||||
'project.fileManagement.deleteFileTipTitle': '确认删除 {name} 这个文件吗?',
|
||||
'project.fileManagement.deleteFileTipContent': '删除后,导致关联该文件的用例执行失败,请谨慎操作!',
|
||||
'project.fileManagement.deleteFileTipContent': '删除后,会导致关联该文件的用例执行失败,请谨慎操作!',
|
||||
'project.fileManagement.disabledFileTipTitle': '确认禁用 {name} 这个文件吗?',
|
||||
'project.fileManagement.disabledFileTipContent': '禁用后,会导致关联该文件的自定义脚本执行失败,请谨慎操作!',
|
||||
'project.fileManagement.batchDeleteFileTipTitle': '确认删除这 {count} 个文件吗?',
|
||||
'project.fileManagement.detail': '文件详情',
|
||||
'project.fileManagement.prev': '上一个',
|
||||
'project.fileManagement.noPrev': '当前已是第一个',
|
||||
|
@ -97,4 +103,16 @@ export default {
|
|||
'project.fileManagement.record': '更新历史',
|
||||
'project.fileManagement.caseList': '用例列表',
|
||||
'project.fileManagement.search': '输入名称搜索',
|
||||
'project.fileManagement.gitBranchNotNull': 'git 分支不能为空',
|
||||
'project.fileManagement.gitBranchPlaceholder': '请输入 git 分支,如:master',
|
||||
'project.fileManagement.gitFilePath': '文件路径',
|
||||
'project.fileManagement.gitFilePathNotNull': '文件路径不能为空',
|
||||
'project.fileManagement.gitFilePathPlaceholder': '请输入文件路径,如:/xxxxxx.xx',
|
||||
'project.fileManagement.gitFilePathSub': '根目录前无需添加文件路径分隔符:/',
|
||||
'project.fileManagement.batchMoveTitle': '批量移动',
|
||||
'project.fileManagement.singleMoveTitle': '移动',
|
||||
'project.fileManagement.batchMoveTitleSub': '(已选 {count} 个文件)',
|
||||
'project.fileManagement.batchMoveSearchPlaceholder': '请输入模块名称进行搜索',
|
||||
'project.fileManagement.batchMoveConfirm': '移动至所选模块',
|
||||
'project.fileManagement.batchMoveSuccess': '文件移动成功',
|
||||
};
|
||||
|
|
|
@ -157,13 +157,14 @@
|
|||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.config.email.from')" field="from" asterisk-position="end" :rules="[emailRule]">
|
||||
<a-form-item :label="t('system.config.email.from')" field="from" asterisk-position="end">
|
||||
<a-input
|
||||
v-model:model-value="emailConfigForm.from"
|
||||
:max-length="250"
|
||||
:placeholder="t('system.config.email.fromPlaceholder')"
|
||||
allow-clear
|
||||
></a-input>
|
||||
<MsFormItemSub :text="t('system.config.email.fromTip')" :show-fill-icon="false" />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="t('system.config.email.recipient')"
|
||||
|
|
|
@ -39,6 +39,8 @@ export default {
|
|||
'system.config.email.passwordRequired': 'SMTP password cannot be empty',
|
||||
'system.config.email.passwordPlaceholder': 'Please enter SMTP password',
|
||||
'system.config.email.fromPlaceholder': 'Please enter the designated sender email',
|
||||
'system.config.email.fromTip':
|
||||
'Note: It must be an email address that has been verified by the mail server, otherwise it will be sent by SMTP account by default',
|
||||
'system.config.email.recipientPlaceholder': 'Please enter the email address of the test recipient',
|
||||
'system.config.email.sslTip': 'If the SMTP port is 465, SSL needs to be enabled',
|
||||
'system.config.email.tslTip': 'If the SMTP port is 587, TSL needs to be enabled',
|
||||
|
|
|
@ -39,6 +39,7 @@ export default {
|
|||
'system.config.email.passwordRequired': 'SMTP 密码不能为空',
|
||||
'system.config.email.passwordPlaceholder': '请输入SMTP 密码',
|
||||
'system.config.email.fromPlaceholder': '请输入指定发件人邮箱',
|
||||
'system.config.email.fromTip': '注:必须是邮件服务器验证通过的邮箱,否则默认为 SMTP 账户发送',
|
||||
'system.config.email.recipientPlaceholder': '请输入测试收件人邮箱',
|
||||
'system.config.email.sslTip': '若 SMTP 端口是 465,需要启用 SSL',
|
||||
'system.config.email.tslTip': '若 SMTP 端口是 587,需要启用 TSL',
|
||||
|
|
|
@ -345,7 +345,6 @@
|
|||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBatchForm from '@/components/business/ms-batch-form/index.vue';
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
|
||||
import JobTemplateDrawer from './components/jobTemplateDrawer.vue';
|
||||
import { getYaml, YamlType, job } from './template';
|
||||
|
|
|
@ -298,9 +298,9 @@
|
|||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
function resetPassword(record: any, isBatch?: boolean) {
|
||||
function resetPassword(record?: UserListItem, isBatch?: boolean) {
|
||||
let title = t('system.user.resetPswTip', { name: characterLimit(record?.name) });
|
||||
let selectIds = [record?.id];
|
||||
let selectIds = [record?.id || ''];
|
||||
if (isBatch) {
|
||||
title = t('system.user.batchResetPswTip', { count: tableSelected.value.length });
|
||||
selectIds = tableSelected.value as string[];
|
||||
|
@ -330,9 +330,9 @@
|
|||
/**
|
||||
* 禁用用户
|
||||
*/
|
||||
function disabledUser(record: any, isBatch?: boolean) {
|
||||
function disabledUser(record?: UserListItem, isBatch?: boolean) {
|
||||
let title = t('system.user.disableUserTip', { name: characterLimit(record?.name) });
|
||||
let selectIds = [record?.id];
|
||||
let selectIds = [record?.id || ''];
|
||||
if (isBatch) {
|
||||
title = t('system.user.batchDisableUserTip', { count: tableSelected.value.length });
|
||||
selectIds = tableSelected.value as string[];
|
||||
|
@ -365,9 +365,9 @@
|
|||
/**
|
||||
* 启用用户
|
||||
*/
|
||||
function enableUser(record: any, isBatch?: boolean) {
|
||||
function enableUser(record?: UserListItem, isBatch?: boolean) {
|
||||
let title = t('system.user.enableUserTip', { name: characterLimit(record?.name) });
|
||||
let selectIds = [record?.id];
|
||||
let selectIds = [record?.id || ''];
|
||||
if (isBatch) {
|
||||
title = t('system.user.batchEnableUserTip', { count: tableSelected.value.length });
|
||||
selectIds = tableSelected.value as string[];
|
||||
|
@ -400,9 +400,9 @@
|
|||
/**
|
||||
* 删除用户
|
||||
*/
|
||||
function deleteUser(record: any, isBatch?: boolean) {
|
||||
function deleteUser(record?: UserListItem, isBatch?: boolean) {
|
||||
let title = t('system.user.deleteUserTip', { name: characterLimit(record?.name) });
|
||||
let selectIds = [record?.id];
|
||||
let selectIds = [record?.id || ''];
|
||||
if (isBatch) {
|
||||
title = t('system.user.batchDeleteUserTip', { count: tableSelected.value.length });
|
||||
selectIds = tableSelected.value as string[];
|
||||
|
@ -508,16 +508,16 @@
|
|||
showBatchModal.value = true;
|
||||
break;
|
||||
case 'resetPassword':
|
||||
resetPassword(null, true);
|
||||
resetPassword(undefined, true);
|
||||
break;
|
||||
case 'disabled':
|
||||
disabledUser(null, true);
|
||||
disabledUser(undefined, true);
|
||||
break;
|
||||
case 'enable':
|
||||
enableUser(null, true);
|
||||
enableUser(undefined, true);
|
||||
break;
|
||||
case 'delete':
|
||||
deleteUser(null, true);
|
||||
deleteUser(undefined, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -528,7 +528,7 @@
|
|||
* 处理表格更多按钮事件
|
||||
* @param item
|
||||
*/
|
||||
function handleSelect(item: ActionsItem, record: any) {
|
||||
function handleSelect(item: ActionsItem, record: UserListItem) {
|
||||
switch (item.eventTag) {
|
||||
case 'resetPassword':
|
||||
resetPassword(record);
|
||||
|
|
Loading…
Reference in New Issue