提交 3f6920f7 作者: Vben

perf(component): optimize tree and upload components

上级 fa828fd9
...@@ -2,4 +2,4 @@ import BasicTree from './src/Tree.vue'; ...@@ -2,4 +2,4 @@ import BasicTree from './src/Tree.vue';
export { BasicTree }; export { BasicTree };
export type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; export type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
export * from './src/types'; export * from './src/typing';
<script lang="tsx"> <script lang="tsx">
import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types'; import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './typing';
import { import {
defineComponent, defineComponent,
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
import { basicProps } from './props'; import { basicProps } from './props';
import { CreateContextOptions } from '/@/components/ContextMenu'; import { CreateContextOptions } from '/@/components/ContextMenu';
import { CheckEvent } from './types'; import { CheckEvent } from './typing';
interface State { interface State {
expandedKeys: Keys; expandedKeys: Keys;
......
...@@ -43,7 +43,14 @@ ...@@ -43,7 +43,14 @@
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { ToolbarEnum } from './enum'; enum ToolbarEnum {
SELECT_ALL,
UN_SELECT_ALL,
EXPAND_ALL,
UN_EXPAND_ALL,
CHECK_STRICTLY,
CHECK_UN_STRICTLY,
}
interface MenuInfo { interface MenuInfo {
key: ToolbarEnum; key: ToolbarEnum;
......
export enum ToolbarEnum {
SELECT_ALL,
UN_SELECT_ALL,
EXPAND_ALL,
UN_EXPAND_ALL,
CHECK_STRICTLY,
CHECK_UN_STRICTLY,
}
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { ReplaceFields, ActionItem, Keys, CheckKeys, ContextMenuOptions } from './types'; import type { ReplaceFields, ActionItem, Keys, CheckKeys, ContextMenuOptions } from './typing';
import type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; import type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
import { propTypes } from '/@/utils/propTypes'; import { propTypes } from '/@/utils/propTypes';
......
import type { InsertNodeParams, Keys, ReplaceFields } from './types'; import type { InsertNodeParams, Keys, ReplaceFields } from './typing';
import type { Ref, ComputedRef } from 'vue'; import type { Ref, ComputedRef } from 'vue';
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'; import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
......
export { default as BasicUpload } from './src/BasicUpload.vue'; import { withInstall } from '/@/utils';
import basicUpload from './src/BasicUpload.vue';
export const BasicUpload = withInstall(basicUpload);
...@@ -7,14 +7,14 @@ ...@@ -7,14 +7,14 @@
<Tooltip placement="bottom" v-if="showPreview"> <Tooltip placement="bottom" v-if="showPreview">
<template #title> <template #title>
{{ t('component.upload.uploaded') }} {{ t('component.upload.uploaded') }}
<template v-if="fileListRef.length"> <template v-if="fileList.length">
{{ fileListRef.length }} {{ fileList.length }}
</template> </template>
</template> </template>
<a-button @click="openPreviewModal"> <a-button @click="openPreviewModal">
<Icon icon="bi:eye" /> <Icon icon="bi:eye" />
<template v-if="fileListRef.length && showPreviewNumber"> <template v-if="fileList.length && showPreviewNumber">
{{ fileListRef.length }} {{ fileList.length }}
</template> </template>
</a-button> </a-button>
</Tooltip> </Tooltip>
...@@ -22,13 +22,14 @@ ...@@ -22,13 +22,14 @@
<UploadModal <UploadModal
v-bind="bindValue" v-bind="bindValue"
:previewFileList="fileListRef" :previewFileList="fileList"
@register="registerUploadModal" @register="registerUploadModal"
@change="handleChange" @change="handleChange"
@delete="handleDelete"
/> />
<UploadPreviewModal <UploadPreviewModal
:value="fileListRef" :value="fileList"
@register="registerPreviewModal" @register="registerPreviewModal"
@list-change="handlePreviewChange" @list-change="handlePreviewChange"
/> />
...@@ -36,14 +37,11 @@ ...@@ -36,14 +37,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, watch, unref, computed } from 'vue'; import { defineComponent, ref, watch, unref, computed } from 'vue';
import UploadModal from './UploadModal.vue'; import UploadModal from './UploadModal.vue';
import UploadPreviewModal from './UploadPreviewModal.vue'; import UploadPreviewModal from './UploadPreviewModal.vue';
import Icon from '/@/components/Icon'; import { Icon } from '/@/components/Icon';
import { Tooltip } from 'ant-design-vue'; import { Tooltip } from 'ant-design-vue';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { uploadContainerProps } from './props'; import { uploadContainerProps } from './props';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
...@@ -52,7 +50,7 @@ ...@@ -52,7 +50,7 @@
name: 'BasicUpload', name: 'BasicUpload',
components: { UploadModal, UploadPreviewModal, Icon, Tooltip }, components: { UploadModal, UploadPreviewModal, Icon, Tooltip },
props: uploadContainerProps, props: uploadContainerProps,
emits: ['change'], emits: ['change', 'delete'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
const { t } = useI18n(); const { t } = useI18n();
...@@ -62,12 +60,12 @@ ...@@ -62,12 +60,12 @@
// 预览modal // 预览modal
const [registerPreviewModal, { openModal: openPreviewModal }] = useModal(); const [registerPreviewModal, { openModal: openPreviewModal }] = useModal();
const fileListRef = ref<string[]>([]); const fileList = ref<string[]>([]);
const showPreview = computed(() => { const showPreview = computed(() => {
const { emptyHidePreview } = props; const { emptyHidePreview } = props;
if (!emptyHidePreview) return true; if (!emptyHidePreview) return true;
return emptyHidePreview ? fileListRef.value.length > 0 : true; return emptyHidePreview ? fileList.value.length > 0 : true;
}); });
const bindValue = computed(() => { const bindValue = computed(() => {
...@@ -78,21 +76,25 @@ ...@@ -78,21 +76,25 @@
watch( watch(
() => props.value, () => props.value,
(value = []) => { (value = []) => {
fileListRef.value = value; fileList.value = value;
}, },
{ immediate: true } { immediate: true }
); );
// 上传modal保存操作 // 上传modal保存操作
function handleChange(urls: string[]) { function handleChange(urls: string[]) {
fileListRef.value = [...unref(fileListRef), ...(urls || [])]; fileList.value = [...unref(fileList), ...(urls || [])];
emit('change', fileListRef.value); emit('change', fileList.value);
} }
// 预览modal保存操作 // 预览modal保存操作
function handlePreviewChange(urls: string[]) { function handlePreviewChange(urls: string[]) {
fileListRef.value = [...(urls || [])]; fileList.value = [...(urls || [])];
emit('change', fileListRef.value); emit('change', fileList.value);
}
function handleDelete(record: Recordable) {
emit('delete', record);
} }
return { return {
...@@ -102,9 +104,10 @@ ...@@ -102,9 +104,10 @@
handlePreviewChange, handlePreviewChange,
registerPreviewModal, registerPreviewModal,
openPreviewModal, openPreviewModal,
fileListRef, fileList,
showPreview, showPreview,
bindValue, bindValue,
handleDelete,
t, t,
}; };
}, },
......
.file-table {
width: 100%;
border-collapse: collapse;
.center {
text-align: center;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
&-th,
&-td {
padding: 12px 8px;
}
thead {
background-color: @background-color-light;
}
table,
td,
th {
border: 1px solid @border-color-base;
}
}
import { defineComponent, CSSProperties, watch, nextTick } from 'vue'; <script lang="tsx">
import { fileListProps } from './props'; import { defineComponent, CSSProperties, watch, nextTick } from 'vue';
import { isFunction } from '/@/utils/is'; import { fileListProps } from './props';
import './FileList.less'; import { isFunction } from '/@/utils/is';
import { useModalContext } from '/@/components/Modal/src/hooks/useModalContext'; import { useModalContext } from '/@/components/Modal/src/hooks/useModalContext';
export default defineComponent({ export default defineComponent({
name: 'FileList', name: 'FileList',
props: fileListProps, props: fileListProps,
setup(props) { setup(props) {
const modalFn = useModalContext(); const modalFn = useModalContext();
watch( watch(
() => props.dataSource, () => props.dataSource,
() => { () => {
nextTick(() => { nextTick(() => {
modalFn?.redoModalHeight?.(); modalFn?.redoModalHeight?.();
}); });
} }
); );
return () => { return () => {
const { columns, actionColumn, dataSource } = props; const { columns, actionColumn, dataSource } = props;
const columnList = [...columns, actionColumn]; const columnList = [...columns, actionColumn];
return ( return (
<table class="file-table"> <table class="file-table">
<colgroup> <colgroup>
{columnList.map((item) => {
const { width = 0, dataIndex } = item;
const style: CSSProperties = {
width: `${width}px`,
minWidth: `${width}px`,
};
return <col style={width ? style : {}} key={dataIndex} />;
})}
</colgroup>
<thead>
<tr class="file-table-tr">
{columnList.map((item) => { {columnList.map((item) => {
const { title = '', align = 'center', dataIndex } = item; const { width = 0, dataIndex } = item;
const style: CSSProperties = {
width: `${width}px`,
minWidth: `${width}px`,
};
return <col style={width ? style : {}} key={dataIndex} />;
})}
</colgroup>
<thead>
<tr class="file-table-tr">
{columnList.map((item) => {
const { title = '', align = 'center', dataIndex } = item;
return (
<th class={['file-table-th', align]} key={dataIndex}>
{title}
</th>
);
})}
</tr>
</thead>
<tbody>
{dataSource.map((record = {}, index) => {
return ( return (
<th class={['file-table-th', align]} key={dataIndex}> <tr class="file-table-tr" key={`${index + record.name || ''}`}>
{title} {columnList.map((item) => {
</th> const { dataIndex = '', customRender, align = 'center' } = item;
const render = customRender && isFunction(customRender);
return (
<td class={['file-table-td', align]} key={dataIndex}>
{render
? customRender?.({ text: record[dataIndex], record })
: record[dataIndex]}
</td>
);
})}
</tr>
); );
})} })}
</tr> </tbody>
</thead> </table>
<tbody> );
{dataSource.map((record = {}, index) => { };
return ( },
<tr class="file-table-tr" key={`${index + record.name || ''}`}> });
{columnList.map((item) => { </script>
const { dataIndex = '', customRender, align = 'center' } = item; <style lang="less">
const render = customRender && isFunction(customRender); .file-table {
return ( width: 100%;
<td class={['file-table-td', align]} key={dataIndex}> border-collapse: collapse;
{render
? customRender?.({ text: record[dataIndex], record }) .center {
: record[dataIndex]} text-align: center;
</td> }
);
})} .left {
</tr> text-align: left;
); }
})}
</tbody> .right {
</table> text-align: right;
); }
};
}, &-th,
}); &-td {
padding: 12px 8px;
}
thead {
background-color: @background-color-light;
}
table,
td,
th {
border: 1px solid @border-color-base;
}
}
</style>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
import { useUploadType } from './useUpload'; import { useUploadType } from './useUpload';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
// types // types
import { FileItem, UploadResultStatus } from './types'; import { FileItem, UploadResultStatus } from './typing';
import { basicProps } from './props'; import { basicProps } from './props';
import { createTableColumns, createActionColumn } from './data'; import { createTableColumns, createActionColumn } from './data';
// utils // utils
...@@ -58,9 +58,9 @@ ...@@ -58,9 +58,9 @@
import { buildUUID } from '/@/utils/uuid'; import { buildUUID } from '/@/utils/uuid';
import { isFunction } from '/@/utils/is'; import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log'; import { warn } from '/@/utils/log';
import FileList from './FileList'; import FileList from './FileList.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
components: { BasicModal, Upload, Alert, FileList }, components: { BasicModal, Upload, Alert, FileList },
props: { props: {
...@@ -70,20 +70,20 @@ ...@@ -70,20 +70,20 @@
default: () => [], default: () => [],
}, },
}, },
emits: ['change', 'register'], emits: ['change', 'register', 'delete'],
setup(props, { emit }) { setup(props, { emit }) {
const { t } = useI18n(); const state = reactive<{ fileList: FileItem[] }>({
fileList: [],
});
// 是否正在上传 // 是否正在上传
const isUploadingRef = ref(false); const isUploadingRef = ref(false);
const fileListRef = ref<FileItem[]>([]); const fileListRef = ref<FileItem[]>([]);
const state = reactive<{ fileList: FileItem[] }>({ const { accept, helpText, maxNumber, maxSize } = toRefs(props);
fileList: [],
});
const { t } = useI18n();
const [register, { closeModal }] = useModalInner(); const [register, { closeModal }] = useModalInner();
const { accept, helpText, maxNumber, maxSize } = toRefs(props);
const { getAccept, getStringAccept, getHelpText } = useUploadType({ const { getAccept, getStringAccept, getHelpText } = useUploadType({
acceptRef: accept, acceptRef: accept,
helpTextRef: helpText, helpTextRef: helpText,
...@@ -162,10 +162,12 @@ ...@@ -162,10 +162,12 @@
} }
return false; return false;
} }
// 删除 // 删除
function handleRemove(record: FileItem) { function handleRemove(record: FileItem) {
const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid); const index = fileListRef.value.findIndex((item) => item.uuid === record.uuid);
index !== -1 && fileListRef.value.splice(index, 1); index !== -1 && fileListRef.value.splice(index, 1);
emit('delete', record);
} }
// 预览 // 预览
......
...@@ -12,18 +12,15 @@ ...@@ -12,18 +12,15 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, watch, ref } from 'vue'; import { defineComponent, watch, ref } from 'vue';
// import { BasicTable, useTable } from '/@/components/Table'; // import { BasicTable, useTable } from '/@/components/Table';
import FileList from './FileList'; import FileList from './FileList.vue';
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import { previewProps } from './props'; import { previewProps } from './props';
import { PreviewFileItem } from './types'; import { PreviewFileItem } from './typing';
import { downloadByUrl } from '/@/utils/file/download'; import { downloadByUrl } from '/@/utils/file/download';
import { createPreviewColumns, createPreviewActionColumn } from './data'; import { createPreviewColumns, createPreviewActionColumn } from './data';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({ export default defineComponent({
components: { BasicModal, FileList }, components: { BasicModal, FileList },
props: previewProps, props: previewProps,
......
import type { BasicColumn, ActionItem } from '/@/components/Table'; import type { BasicColumn, ActionItem } from '/@/components/Table';
import { FileItem, PreviewFileItem, UploadResultStatus } from './typing';
import { FileItem, PreviewFileItem, UploadResultStatus } from './types';
import { import {
// checkImgType, // checkImgType,
isImgTypeByName, isImgTypeByName,
} from './helper'; } from './helper';
import { Progress, Tag } from 'ant-design-vue'; import { Progress, Tag } from 'ant-design-vue';
import TableAction from '/@/components/Table/src/components/TableAction.vue'; import TableAction from '/@/components/Table/src/components/TableAction.vue';
import ThumbUrl from './ThumbUrl.vue'; import ThumbUrl from './ThumbUrl.vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n(); const { t } = useI18n();
// 文件上传列表 // 文件上传列表
......
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import { FileBasicColumn } from './types'; import { FileBasicColumn } from './typing';
export const basicProps = { export const basicProps = {
helpText: { helpText: {
......
...@@ -3,20 +3,17 @@ import { useI18n } from '/@/hooks/web/useI18n'; ...@@ -3,20 +3,17 @@ import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n(); const { t } = useI18n();
export function useUploadType({ export function useUploadType({
acceptRef, acceptRef,
// uploadTypeRef,
helpTextRef, helpTextRef,
maxNumberRef, maxNumberRef,
maxSizeRef, maxSizeRef,
}: { }: {
acceptRef: Ref<string[]>; acceptRef: Ref<string[]>;
// uploadTypeRef: Ref<UploadTypeEnum>;
helpTextRef: Ref<string>; helpTextRef: Ref<string>;
maxNumberRef: Ref<number>; maxNumberRef: Ref<number>;
maxSizeRef: Ref<number>; maxSizeRef: Ref<number>;
}) { }) {
// 文件类型限制 // 文件类型限制
const getAccept = computed(() => { const getAccept = computed(() => {
// const uploadType = unref(uploadTypeRef);
const accept = unref(acceptRef); const accept = unref(acceptRef);
if (accept && accept.length > 0) { if (accept && accept.length > 0) {
return accept; return accept;
...@@ -28,6 +25,7 @@ export function useUploadType({ ...@@ -28,6 +25,7 @@ export function useUploadType({
.map((item) => `.${item}`) .map((item) => `.${item}`)
.join(','); .join(',');
}); });
// 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。
const getHelpText = computed(() => { const getHelpText = computed(() => {
const helpText = unref(helpTextRef); const helpText = unref(helpTextRef);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论