@smartos-lib/components 1.7.0-beta.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintrc +12 -0
- package/.eslintrc-auto-import.json +332 -0
- package/Components.code-workspace +143 -0
- package/LICENSE +21 -0
- package/dist/smart-docx-editor/index.d.ts +2 -0
- package/dist/smart-docx-editor/index.js +68 -0
- package/dist/smart-file-preview/index.d.ts +18 -0
- package/dist/smart-file-preview/index.js +37 -0
- package/dist/smart-upload/index.d.ts +2 -0
- package/dist/smart-upload/index.js +800 -0
- package/index.html +16 -0
- package/package.json +23 -0
- package/public/favicon.svg +6 -0
- package/scripts/components.vite.config.ts +96 -0
- package/scripts/shared.ts +9 -0
- package/src/App.vue +28 -0
- package/src/components/Logo/index.vue +15 -0
- package/src/components-private/.gitkeep +0 -0
- package/src/composables/useElementStyle.ts +23 -0
- package/src/composables/useNaiveStyle.ts +43 -0
- package/src/composables/useNaiveTheme.ts +71 -0
- package/src/composables/useSmart.ts +36 -0
- package/src/layouts/default.vue +3 -0
- package/src/main.ts +33 -0
- package/src/modules/pinia/index.ts +8 -0
- package/src/modules/progress/index.ts +12 -0
- package/src/modules/router/install.ts +9 -0
- package/src/modules/router/routes.ts +40 -0
- package/src/pages/[...all].vue +21 -0
- package/src/pages/frame/component/[name].vue +14 -0
- package/src/pages/frame/index.vue +81 -0
- package/src/pages/index/composables/useTabsManage.ts +46 -0
- package/src/pages/index/index.vue +111 -0
- package/src/pages/index/type.ts +13 -0
- package/src/pages/index/utils/index.ts +41 -0
- package/src/settings.ts +9 -0
- package/src/shared/components.ts +52 -0
- package/src/shared/env.ts +11 -0
- package/src/shared/unocss.theme.ts +1600 -0
- package/src/stores/theme.ts +29 -0
- package/src/styles/element.scss +3 -0
- package/src/styles/styles.scss +21 -0
- package/src/types.ts +20 -0
- package/src/utils/callCustomElementExposed.ts +6 -0
- package/src/utils/deepCloneESModule.ts +10 -0
- package/src/utils/defineCustomElements.ts +18 -0
- package/src/utils/formatComponentsGlob.ts +16 -0
- package/src/utils/getFileMD5.ts +31 -0
- package/src/utils/getFileNameAndExt.ts +11 -0
- package/src/utils/isFileEqual.ts +13 -0
- package/src/utils/jsonToFormData.ts +8 -0
- package/src/web-components/smart-docx-drive-page/App.vue +37 -0
- package/src/web-components/smart-docx-drive-page/apis/doc.ts +85 -0
- package/src/web-components/smart-docx-drive-page/apis/file.ts +278 -0
- package/src/web-components/smart-docx-drive-page/apis/folder.ts +72 -0
- package/src/web-components/smart-docx-drive-page/children/Home.vue +8 -0
- package/src/web-components/smart-docx-drive-page/children/Me.vue +47 -0
- package/src/web-components/smart-docx-drive-page/components/CustomImage.vue +26 -0
- package/src/web-components/smart-docx-drive-page/components/CustomPopover.vue +62 -0
- package/src/web-components/smart-docx-drive-page/components/DocxDir.vue +99 -0
- package/src/web-components/smart-docx-drive-page/components/DocxDoc.vue +132 -0
- package/src/web-components/smart-docx-drive-page/components/DocxDownloadPopoverItem.vue +41 -0
- package/src/web-components/smart-docx-drive-page/components/DocxFileList.vue +156 -0
- package/src/web-components/smart-docx-drive-page/components/DocxPreview.vue +33 -0
- package/src/web-components/smart-docx-drive-page/components/DocxUpload.vue +164 -0
- package/src/web-components/smart-docx-drive-page/components/FileIcon.vue +62 -0
- package/src/web-components/smart-docx-drive-page/components-private/Header.vue +65 -0
- package/src/web-components/smart-docx-drive-page/components-private/Logo.vue +15 -0
- package/src/web-components/smart-docx-drive-page/components-private/Menu.vue +34 -0
- package/src/web-components/smart-docx-drive-page/components-private/Navbar.vue +36 -0
- package/src/web-components/smart-docx-drive-page/composables/useFullscreenElDialog.ts +41 -0
- package/src/web-components/smart-docx-drive-page/composables/usePrompt.ts +73 -0
- package/src/web-components/smart-docx-drive-page/data.ts +10 -0
- package/src/web-components/smart-docx-drive-page/external-style/custom-popover.sass +8 -0
- package/src/web-components/smart-docx-drive-page/external-style/index.sass +1 -0
- package/src/web-components/smart-docx-drive-page/index.ts +20 -0
- package/src/web-components/smart-docx-drive-page/index.vue +39 -0
- package/src/web-components/smart-docx-drive-page/info.ts +2 -0
- package/src/web-components/smart-docx-drive-page/stores/menu.ts +60 -0
- package/src/web-components/smart-docx-drive-page/types.ts +51 -0
- package/src/web-components/smart-docx-drive-page/utils/file-actions.ts +63 -0
- package/src/web-components/smart-docx-drive-page/utils/file.ts +31 -0
- package/src/web-components/smart-docx-editor/App.vue +32 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/Markdown.vue +202 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/Menu.vue +100 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/types.ts +6 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/Markdown.tsx +71 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/MarkdownElement.tsx +81 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Blockquote/index.sass +6 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Blockquote/index.tsx +12 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Heading/index.sass +14 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Heading/index.tsx +17 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/List/index.scss +16 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/List/index.tsx +39 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/types/custom-types.d.ts +69 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/composables/useTextSelection.ts +50 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/index.sass +19 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/index.vue +21 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/shared/const.ts +23 -0
- package/src/web-components/smart-docx-editor/MarkdownShortcuts/utils/slateHelpers.ts +23 -0
- package/src/web-components/smart-docx-editor/data.ts +38 -0
- package/src/web-components/smart-docx-editor/demo.vue +11 -0
- package/src/web-components/smart-docx-editor/index.md +3 -0
- package/src/web-components/smart-docx-editor/index.ts +5 -0
- package/src/web-components/smart-docx-editor/index.vue +12 -0
- package/src/web-components/smart-docx-editor/info.ts +2 -0
- package/src/web-components/smart-file-preview/category/Code.vue +171 -0
- package/src/web-components/smart-file-preview/category/Image.vue +49 -0
- package/src/web-components/smart-file-preview/category/Pdf.vue +14 -0
- package/src/web-components/smart-file-preview/category/Video.vue +27 -0
- package/src/web-components/smart-file-preview/demo.vue +34 -0
- package/src/web-components/smart-file-preview/index.md +5 -0
- package/src/web-components/smart-file-preview/index.ts +29 -0
- package/src/web-components/smart-file-preview/index.vue +56 -0
- package/src/web-components/smart-file-preview/info.ts +2 -0
- package/src/web-components/smart-file-preview/shared/const.ts +4 -0
- package/src/web-components/smart-file-preview/types.ts +38 -0
- package/src/web-components/smart-upload/index.ts +5 -0
- package/src/web-components/smart-upload/index.vue +101 -0
- package/src/web-components/smart-upload/info.ts +2 -0
- package/src/web-components/smart-upload/types.ts +28 -0
- package/tsconfig.json +15 -0
- package/types/auto-imports.d.ts +975 -0
- package/types/components.d.ts +14 -0
- package/types/env.d.ts +8 -0
- package/types/shims.d.ts +6 -0
- package/unocss.config.ts +23 -0
- package/vite.config.ts +60 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
import type { BasicColorSchema } from '@vueuse/core';
|
2
|
+
import { defineStore } from 'pinia';
|
3
|
+
|
4
|
+
export const useThemeStore = defineStore('theme', () => {
|
5
|
+
/** 主题模式状态 */
|
6
|
+
const mode = useColorMode<BasicColorSchema>({
|
7
|
+
storageKey: 'st-color-scheme',
|
8
|
+
initialValue: 'light',
|
9
|
+
});
|
10
|
+
|
11
|
+
/** 主题切换 */
|
12
|
+
const { state, next } = useCycleList<BasicColorSchema>(['light', 'dark', 'auto'], {
|
13
|
+
initialValue: mode.store,
|
14
|
+
});
|
15
|
+
|
16
|
+
/** 是否是深色模式 */
|
17
|
+
const dark = computed({
|
18
|
+
get: () => mode.value === 'dark',
|
19
|
+
set: v => (mode.value = v ? 'dark' : 'light'),
|
20
|
+
});
|
21
|
+
|
22
|
+
syncRef(mode.store, state);
|
23
|
+
|
24
|
+
return {
|
25
|
+
value: mode.store,
|
26
|
+
dark,
|
27
|
+
toggle: next,
|
28
|
+
};
|
29
|
+
});
|
@@ -0,0 +1,21 @@
|
|
1
|
+
body {
|
2
|
+
@apply scrollbar scrollbar-rounded;
|
3
|
+
|
4
|
+
-moz-osx-font-smoothing: grayscale;
|
5
|
+
-webkit-font-smoothing: antialiased;
|
6
|
+
text-rendering: optimizeLegibility;
|
7
|
+
}
|
8
|
+
|
9
|
+
a,
|
10
|
+
a:focus,
|
11
|
+
a:hover {
|
12
|
+
@apply cursor-pointer text-primary underline;
|
13
|
+
}
|
14
|
+
|
15
|
+
a,
|
16
|
+
a:focus,
|
17
|
+
a:active,
|
18
|
+
div,
|
19
|
+
div:focus {
|
20
|
+
@apply outline-none;
|
21
|
+
}
|
package/src/types.ts
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import type { App } from 'vue';
|
2
|
+
import type { Router } from 'vue-router';
|
3
|
+
|
4
|
+
interface UserModuleContext {
|
5
|
+
app: App<Element>
|
6
|
+
router: Router
|
7
|
+
}
|
8
|
+
|
9
|
+
/**
|
10
|
+
* 用户模块定义方法
|
11
|
+
*/
|
12
|
+
export type UserModule = (ctx: UserModuleContext) => void;
|
13
|
+
|
14
|
+
/**
|
15
|
+
* 项目配置
|
16
|
+
*/
|
17
|
+
export interface Settings {
|
18
|
+
/** 项目名称 */
|
19
|
+
name: string
|
20
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { defineCustomElement } from 'vue';
|
2
|
+
import { isBrowser } from 'mixte';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* 定义自定义组件
|
6
|
+
* @param tag 自定义组件标签名
|
7
|
+
* @param component Vue 组件对象
|
8
|
+
*/
|
9
|
+
export function defineCustomElements(tag: string, component: any) {
|
10
|
+
if (isBrowser && !customElements.get(tag)) {
|
11
|
+
customElements.define(
|
12
|
+
tag,
|
13
|
+
component.prototype?.connectedCallback
|
14
|
+
? component
|
15
|
+
: defineCustomElement(component),
|
16
|
+
);
|
17
|
+
}
|
18
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* 格式化导入文件的信息
|
3
|
+
*/
|
4
|
+
export function formatComponentsGlob(files: Record<string, any> | string[]) {
|
5
|
+
if (Array.isArray(files)) {
|
6
|
+
return Object.fromEntries(
|
7
|
+
files.map(path => [path.split('/').reverse()[1], path]),
|
8
|
+
);
|
9
|
+
}
|
10
|
+
|
11
|
+
return Object.fromEntries(
|
12
|
+
Object.entries(files).map(([path, value]) => {
|
13
|
+
return [path.split('/').reverse()[1], value];
|
14
|
+
}),
|
15
|
+
);
|
16
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import SparkMd5 from 'spark-md5';
|
2
|
+
|
3
|
+
const FileSlice = File.prototype.slice || (File.prototype as any).mozSlice || (File.prototype as any).webkitSlice;
|
4
|
+
const FileChunkSize = 1024 * 1024 * 2; // 2MB
|
5
|
+
|
6
|
+
export function getFileMD5(file: File) {
|
7
|
+
return new Promise<string>((resolve) => {
|
8
|
+
const chunks = Math.ceil(file.size / FileChunkSize);
|
9
|
+
const spark = new SparkMd5.ArrayBuffer();
|
10
|
+
const fileReader = new FileReader();
|
11
|
+
let currentChunk = 0;
|
12
|
+
|
13
|
+
fileReader.onload = function (e) {
|
14
|
+
spark.append(e.target!.result as ArrayBuffer); // Append array buffer
|
15
|
+
currentChunk++;
|
16
|
+
|
17
|
+
if (currentChunk < chunks)
|
18
|
+
loadNext();
|
19
|
+
else
|
20
|
+
resolve(spark.end());
|
21
|
+
};
|
22
|
+
|
23
|
+
function loadNext() {
|
24
|
+
const start = currentChunk * FileChunkSize;
|
25
|
+
const end = start + FileChunkSize >= file.size ? file.size : start + FileChunkSize;
|
26
|
+
fileReader.readAsArrayBuffer(FileSlice.call(file, start, end));
|
27
|
+
}
|
28
|
+
|
29
|
+
loadNext();
|
30
|
+
});
|
31
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/**
|
2
|
+
* 获取文件名和后缀名
|
3
|
+
*/
|
4
|
+
export function getFileNameAndExt(fileName: string) {
|
5
|
+
const name = fileName.split('.');
|
6
|
+
|
7
|
+
return {
|
8
|
+
name: name.length > 1 ? name.slice(0, -1).join('.') : fileName,
|
9
|
+
ext: name.length > 1 ? name.reverse()[0].toLowerCase() : '',
|
10
|
+
};
|
11
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/**
|
2
|
+
* 比较两个文件是否相等
|
3
|
+
* @param file 文件1
|
4
|
+
* @param file2 文件2
|
5
|
+
*/
|
6
|
+
export function isFileEqual(file: File, file2: File) {
|
7
|
+
if (file.name !== file2.name) return false;
|
8
|
+
if (file.size !== file2.size) return false;
|
9
|
+
if (file.type !== file2.type) return false;
|
10
|
+
if (file.lastModified !== file2.lastModified) return false;
|
11
|
+
if (file.webkitRelativePath !== file2.webkitRelativePath) return false;
|
12
|
+
return true;
|
13
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<!--
|
2
|
+
1. element plus 命名空间设置
|
3
|
+
2. 参数默认值处理
|
4
|
+
-->
|
5
|
+
|
6
|
+
<template>
|
7
|
+
<NConfigProvider :cls-prefix="classPrefix" :theme-overrides="commonOverrides" abstract>
|
8
|
+
<NMessageProvider>
|
9
|
+
<ElConfigProvider :locale="zhCn" namespace="smart">
|
10
|
+
<Index v-bind="props" />
|
11
|
+
</ElConfigProvider>
|
12
|
+
</NMessageProvider>
|
13
|
+
</NConfigProvider>
|
14
|
+
</template>
|
15
|
+
|
16
|
+
<script lang="ts" setup>
|
17
|
+
// @ts-expect-error
|
18
|
+
import zhCn from 'element-plus/dist/locale/zh-cn';
|
19
|
+
import { NConfigProvider, NMessageProvider } from 'naive-ui';
|
20
|
+
import Index from './index.vue';
|
21
|
+
import { menuStore } from './stores/menu';
|
22
|
+
import type { SmartDocxDrivePageProps } from './types';
|
23
|
+
|
24
|
+
const props = defineProps<SmartDocxDrivePageProps>();
|
25
|
+
|
26
|
+
const { activeMenu } = menuStore();
|
27
|
+
|
28
|
+
const { classPrefix } = useSmart();
|
29
|
+
|
30
|
+
// 当前激活的菜单项, 默认为主页
|
31
|
+
syncRef(activeMenu, toRef(props, 'activeMenu'), {
|
32
|
+
direction: 'rtl',
|
33
|
+
transform: {
|
34
|
+
rtl: value => value || (import.meta.env.DEV ? 'me' : 'home'),
|
35
|
+
},
|
36
|
+
});
|
37
|
+
</script>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import type { UseRequestOptions } from '@mixte/use';
|
2
|
+
import type { Descendant } from 'slate';
|
3
|
+
import { getConfigProvider } from '@smartos-lib/core';
|
4
|
+
import type { MaybeRefOrGetter } from 'vue';
|
5
|
+
import type { ResponseData } from '@smartos-lib/types';
|
6
|
+
import type { CancelTokenSource } from 'axios';
|
7
|
+
import { menuStore } from '../stores/menu';
|
8
|
+
|
9
|
+
const { meActiveMenuInfo } = menuStore();
|
10
|
+
|
11
|
+
/** 创建在线文档接口传参 */
|
12
|
+
export interface CreateOnlineDocxParams {
|
13
|
+
/** 文档标题 */
|
14
|
+
fileName?: string
|
15
|
+
/** 文档内容 */
|
16
|
+
docContext?: {
|
17
|
+
value?: Descendant[]
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
/** 创建在线文档 */
|
22
|
+
export function createOnlineDocx(params?: CreateOnlineDocxParams, options?: UseRequestOptions) {
|
23
|
+
return useRequestReactive<{
|
24
|
+
data: ResponseData<string>
|
25
|
+
}>(
|
26
|
+
(overlapParams?: CreateOnlineDocxParams) => {
|
27
|
+
const docContext: NonNullable<CreateOnlineDocxParams['docContext']> = overlapParams?.docContext ?? params?.docContext ?? {};
|
28
|
+
docContext.value ??= [];
|
29
|
+
|
30
|
+
return getConfigProvider('request')!({
|
31
|
+
url: '/de/file/createOnlineDoc',
|
32
|
+
method: 'post',
|
33
|
+
data: Object.assign({ filePath: meActiveMenuInfo.value?.filePath ?? '/' }, params, overlapParams, {
|
34
|
+
docContext: JSON.stringify(docContext),
|
35
|
+
}),
|
36
|
+
});
|
37
|
+
},
|
38
|
+
options,
|
39
|
+
);
|
40
|
+
}
|
41
|
+
|
42
|
+
/** 修改在线文档接口传参 */
|
43
|
+
export interface UpdateOnlineDocxParams extends Partial<CreateOnlineDocxParams> {
|
44
|
+
userFileId: string
|
45
|
+
}
|
46
|
+
|
47
|
+
/** 修改在线文档 */
|
48
|
+
export function updateOnlineDocx(_params?: MaybeRefOrGetter<UpdateOnlineDocxParams>, options?: UseRequestOptions) {
|
49
|
+
let cancelToken: CancelTokenSource | undefined;
|
50
|
+
|
51
|
+
return useRequestReactive<{ data: string }>(
|
52
|
+
(_overlapParams?: MaybeRefOrGetter<UpdateOnlineDocxParams>) => {
|
53
|
+
const params = toValue(_params);
|
54
|
+
const overlapParams = toValue(_overlapParams);
|
55
|
+
|
56
|
+
const docContext: NonNullable<CreateOnlineDocxParams['docContext']> = overlapParams?.docContext ?? params?.docContext ?? {};
|
57
|
+
docContext.value ??= [];
|
58
|
+
|
59
|
+
cancelToken?.cancel();
|
60
|
+
cancelToken = getConfigProvider('request')!.CancelToken.source();
|
61
|
+
|
62
|
+
return getConfigProvider('request')!({
|
63
|
+
url: '/de/file/updateOnlineFile',
|
64
|
+
method: 'post',
|
65
|
+
data: Object.assign({}, params, overlapParams, {
|
66
|
+
docContext: JSON.stringify(docContext),
|
67
|
+
}),
|
68
|
+
cancelToken: cancelToken.token,
|
69
|
+
});
|
70
|
+
},
|
71
|
+
options,
|
72
|
+
);
|
73
|
+
}
|
74
|
+
|
75
|
+
/** 获取在线文档 */
|
76
|
+
export function getOnlineDocx(params?: { userFileId: string }, options?: UseRequestOptions) {
|
77
|
+
return useRequestReactive<{ data: ResponseData<{ docContext: string }> }>(
|
78
|
+
(overlapParams?: { userFileId: string }) => getConfigProvider('request')!({
|
79
|
+
url: '/de/file/queryOnlineFile',
|
80
|
+
method: 'get',
|
81
|
+
params: Object.assign({}, params, overlapParams),
|
82
|
+
}),
|
83
|
+
options,
|
84
|
+
);
|
85
|
+
}
|
@@ -0,0 +1,278 @@
|
|
1
|
+
import { ImageExtEnum, type ResponseData, type ResponseListData } from '@smartos-lib/types';
|
2
|
+
import type { UseRequestOptions } from '@mixte/use';
|
3
|
+
import { getConfigProvider } from '@smartos-lib/core';
|
4
|
+
import { deepMerge, isNumeric } from 'mixte';
|
5
|
+
import type { MaybeRefOrGetter } from 'vue';
|
6
|
+
import { pick } from 'lodash-es';
|
7
|
+
import type { CancelToken, CancelTokenSource } from 'axios';
|
8
|
+
import type { FileItem } from '../types';
|
9
|
+
import { menuStore } from '../stores/menu';
|
10
|
+
import { getFileMD5 } from '@/utils/getFileMD5';
|
11
|
+
import { jsonToFormData } from '@/utils/jsonToFormData';
|
12
|
+
|
13
|
+
const { meActiveMenuInfo } = menuStore();
|
14
|
+
|
15
|
+
/** 文件信息 */
|
16
|
+
export interface FileInfo {
|
17
|
+
/** 文件 ID */
|
18
|
+
fileId: string
|
19
|
+
/** 用户文件 ID */
|
20
|
+
userFileId: string
|
21
|
+
/** 文件所在路径 */
|
22
|
+
filePath: string
|
23
|
+
/** 文件名 */
|
24
|
+
fileName: string
|
25
|
+
/** 文件后缀 */
|
26
|
+
extendName: string
|
27
|
+
/** 文件大小 */
|
28
|
+
fileSize: number
|
29
|
+
/**
|
30
|
+
* 是否是文件夹
|
31
|
+
* - 0: 否 ( 是文件 )
|
32
|
+
* - 1: 是 ( 是文件夹 )
|
33
|
+
*/
|
34
|
+
isDir: 0 | 1
|
35
|
+
/**
|
36
|
+
* 文件类型
|
37
|
+
* - 1: 普通文件
|
38
|
+
* - 2: 在线文档
|
39
|
+
*/
|
40
|
+
fileType: 1 | 2
|
41
|
+
/** 所有者 */
|
42
|
+
userId: string
|
43
|
+
/** 上传时间 */
|
44
|
+
uploadTime: string
|
45
|
+
|
46
|
+
/** 文件名 + 文件后缀 - 扩展字段 */
|
47
|
+
fileFullName: string
|
48
|
+
/** 下载请求 - 扩展字段 */
|
49
|
+
download: ReturnType<typeof downloadFile>
|
50
|
+
/** 下载进度 - 扩展字段 */
|
51
|
+
downloadProgress: Ref<number>
|
52
|
+
/** 图片预览 - 扩展字段 */
|
53
|
+
imagePreview?: ReturnType<typeof getPreviewFile>
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* 上传文件
|
58
|
+
*/
|
59
|
+
export function uploadFile(fileItem: Ref<FileItem | undefined>) {
|
60
|
+
return useRequestReactive(async () => {
|
61
|
+
const axios = getConfigProvider('request')!;
|
62
|
+
|
63
|
+
fileItem.value!.isReading = true;
|
64
|
+
|
65
|
+
const file = fileItem.value!.file;
|
66
|
+
const data = {
|
67
|
+
identifier: await getFileMD5(file),
|
68
|
+
filename: file.name,
|
69
|
+
relativePath: file.webkitRelativePath || file.name,
|
70
|
+
totalSize: file.size,
|
71
|
+
filePath: meActiveMenuInfo.value?.filePath ?? '/',
|
72
|
+
// 分片
|
73
|
+
// chunkNumber: 1,
|
74
|
+
// totalChunks: 1,
|
75
|
+
// currentChunkSize: file.size,
|
76
|
+
// chunkSize: file.size + 1,
|
77
|
+
};
|
78
|
+
|
79
|
+
const quickUpload = await axios({ url: '/de/filetransfer/uploadfile', method: 'get', params: data });
|
80
|
+
|
81
|
+
fileItem.value!.isReading = false;
|
82
|
+
|
83
|
+
// 服务器已有该文件, 不需要上传
|
84
|
+
if (quickUpload.data.data.skipUpload) {
|
85
|
+
fileItem.value!.isUploadSuccess = true;
|
86
|
+
fileItem.value!.isQuickUpload = true;
|
87
|
+
fileItem.value!.progress = 100;
|
88
|
+
return;
|
89
|
+
}
|
90
|
+
|
91
|
+
fileItem.value!.isUploading = true;
|
92
|
+
|
93
|
+
return axios({
|
94
|
+
url: '/de/filetransfer/uploadfile',
|
95
|
+
method: 'post',
|
96
|
+
data: jsonToFormData({ ...data, file }),
|
97
|
+
onUploadProgress: (progressEvent) => {
|
98
|
+
fileItem.value!.progress = Math.round((progressEvent.loaded * 100) / progressEvent.total!);
|
99
|
+
},
|
100
|
+
})
|
101
|
+
.then(() => {
|
102
|
+
fileItem.value!.isUploading = false;
|
103
|
+
fileItem.value!.isUploadSuccess = true;
|
104
|
+
})
|
105
|
+
.catch(() => {
|
106
|
+
fileItem.value!.isUploading = false;
|
107
|
+
fileItem.value!.isUploadError = true;
|
108
|
+
});
|
109
|
+
});
|
110
|
+
}
|
111
|
+
|
112
|
+
/** 获取文件列表接口传参 */
|
113
|
+
export interface GetFileListParams {
|
114
|
+
/** 当前页码 @default 1 */
|
115
|
+
currentPage?: number
|
116
|
+
/** 每页条数 @default 10 */
|
117
|
+
pageCount?: number
|
118
|
+
/** 文件路径 @default '/' */
|
119
|
+
filePath?: string
|
120
|
+
/**
|
121
|
+
* 文件类型
|
122
|
+
* - 0: 全部
|
123
|
+
* - 1: 音乐
|
124
|
+
* - 2: 图片
|
125
|
+
* @default '0'
|
126
|
+
*/
|
127
|
+
fileType?: '0' | '1' | '2'
|
128
|
+
/**
|
129
|
+
* 是否是文件夹
|
130
|
+
* - 0: 否 ( 是文件 )
|
131
|
+
* - 1: 是 ( 是文件夹 )
|
132
|
+
*/
|
133
|
+
isDir?: 0 | 1
|
134
|
+
}
|
135
|
+
/**
|
136
|
+
* 获取文件列表 ( 不包含文件夹 )
|
137
|
+
*/
|
138
|
+
export function getFileList(params?: MaybeRefOrGetter<GetFileListParams>, options?: UseRequestOptions) {
|
139
|
+
const result = useRequestReactive<{
|
140
|
+
data: ResponseData<ResponseListData<FileInfo>>
|
141
|
+
}>(
|
142
|
+
(overlapParams?: MaybeRefOrGetter<GetFileListParams>) => {
|
143
|
+
return getConfigProvider('request')!({
|
144
|
+
url: '/de/file/getFileList',
|
145
|
+
method: 'get',
|
146
|
+
params: deepMerge({ currentPage: 1, pageCount: 1000, filePath: '/', fileType: '0' }, toValue(params), toValue(overlapParams), { isDir: 0 }),
|
147
|
+
});
|
148
|
+
},
|
149
|
+
options,
|
150
|
+
);
|
151
|
+
|
152
|
+
result.onSuccess(() => {
|
153
|
+
if (!result.data?.data.records.length) return;
|
154
|
+
|
155
|
+
// 字段格式化
|
156
|
+
result.data.data.records.forEach((row) => {
|
157
|
+
row.fileSize = isNumeric(row.fileSize) ? Number(row.fileSize) : row.fileSize;
|
158
|
+
row.fileFullName = `${row.fileName ?? ''}${row.extendName ? `.${row.extendName}` : ''}`;
|
159
|
+
});
|
160
|
+
|
161
|
+
// 写入预览相关
|
162
|
+
result.data.data.records.forEach((row) => {
|
163
|
+
if (ImageExtEnum.includes(row.extendName)) {
|
164
|
+
row.imagePreview = getPreviewFile({ userFileId: row.userFileId, isMin: true });
|
165
|
+
row.imagePreview.execute();
|
166
|
+
}
|
167
|
+
});
|
168
|
+
|
169
|
+
// 写入下载相关
|
170
|
+
result.data.data.records.forEach((row) => {
|
171
|
+
const progress = ref<number>(0);
|
172
|
+
const download = downloadFile(() => pick(row, ['userFileId']), { progress });
|
173
|
+
|
174
|
+
row.downloadProgress = progress;
|
175
|
+
row.download = download;
|
176
|
+
|
177
|
+
download.onSuccess(() => {
|
178
|
+
const url = URL.createObjectURL(download.data!);
|
179
|
+
const a = document.createElement('a');
|
180
|
+
a.href = url;
|
181
|
+
a.download = `${row.fileFullName}`;
|
182
|
+
a.setAttribute('target', '_blank');
|
183
|
+
a.click();
|
184
|
+
URL.revokeObjectURL(url);
|
185
|
+
});
|
186
|
+
});
|
187
|
+
});
|
188
|
+
|
189
|
+
return result;
|
190
|
+
}
|
191
|
+
|
192
|
+
/** 获取预览文件接口传参 */
|
193
|
+
export interface GetPreviewFileParams {
|
194
|
+
/** 文件 ID */
|
195
|
+
userFileId: string
|
196
|
+
/** 是否是小尺寸图片 */
|
197
|
+
isMin?: boolean
|
198
|
+
}
|
199
|
+
/**
|
200
|
+
* 获取预览文件
|
201
|
+
*/
|
202
|
+
export function getPreviewFile(params?: MaybeRefOrGetter<GetPreviewFileParams>, options?: UseRequestOptions) {
|
203
|
+
return useRequestReactive<ResponseData<Blob>>(
|
204
|
+
(overlapParams?: MaybeRefOrGetter<GetPreviewFileParams>) => {
|
205
|
+
return getConfigProvider('request')!({
|
206
|
+
url: '/de/filetransfer/preview',
|
207
|
+
method: 'get',
|
208
|
+
params: deepMerge({}, toValue(params), toValue(overlapParams)),
|
209
|
+
responseType: 'blob',
|
210
|
+
});
|
211
|
+
},
|
212
|
+
options,
|
213
|
+
);
|
214
|
+
}
|
215
|
+
|
216
|
+
/** 获取预览文件接口选项 */
|
217
|
+
export interface DownloadFileOptions {
|
218
|
+
/** 下载进度 */
|
219
|
+
progress?: Ref<number>
|
220
|
+
/** 取消请求 */
|
221
|
+
cancelToken?: CancelToken
|
222
|
+
}
|
223
|
+
/** 下载文件 */
|
224
|
+
export function downloadFile(params?: MaybeRefOrGetter<{ userFileId: string }>, options?: DownloadFileOptions, requestOptions?: UseRequestOptions) {
|
225
|
+
return useRequestReactive<ResponseData<Blob>>(
|
226
|
+
(overlapParams?: MaybeRefOrGetter<{ userFileId: string }>, overlapOptions?: DownloadFileOptions) => {
|
227
|
+
const progress = overlapOptions?.progress ?? options?.progress;
|
228
|
+
const cancelToken = overlapOptions?.cancelToken ?? options?.cancelToken;
|
229
|
+
|
230
|
+
return getConfigProvider('request')!({
|
231
|
+
url: '/de/filetransfer/downloadfile',
|
232
|
+
method: 'get',
|
233
|
+
params: deepMerge({}, toValue(params), toValue(overlapParams)),
|
234
|
+
responseType: 'blob',
|
235
|
+
cancelToken,
|
236
|
+
onDownloadProgress: (progressEvent) => {
|
237
|
+
if (progress)
|
238
|
+
progress.value = Math.round((progressEvent.loaded * 100) / progressEvent.total!);
|
239
|
+
},
|
240
|
+
});
|
241
|
+
},
|
242
|
+
requestOptions,
|
243
|
+
);
|
244
|
+
}
|
245
|
+
|
246
|
+
/** 重命名文件 */
|
247
|
+
export function renameFile(params?: MaybeRefOrGetter<{ userFileId: string; fileName: string }>, options?: UseRequestOptions) {
|
248
|
+
let cancelToken: CancelTokenSource | undefined;
|
249
|
+
|
250
|
+
return useRequestReactive<ResponseData>(
|
251
|
+
(overlapParams?: MaybeRefOrGetter<{ userFileId: string; fileName: string }>) => {
|
252
|
+
cancelToken?.cancel();
|
253
|
+
cancelToken = getConfigProvider('request')!.CancelToken.source();
|
254
|
+
|
255
|
+
return getConfigProvider('request')!({
|
256
|
+
url: '/de/file/renamefile',
|
257
|
+
method: 'post',
|
258
|
+
data: deepMerge({}, toValue(params), toValue(overlapParams)),
|
259
|
+
cancelToken: cancelToken.token,
|
260
|
+
});
|
261
|
+
},
|
262
|
+
options,
|
263
|
+
);
|
264
|
+
}
|
265
|
+
|
266
|
+
/** 删除文件 */
|
267
|
+
export function deleteFile(params?: MaybeRefOrGetter<{ userFileId: string }>, options?: UseRequestOptions) {
|
268
|
+
return useRequestReactive<ResponseData>(
|
269
|
+
(overlapParams?: MaybeRefOrGetter<{ userFileId: string }>) => {
|
270
|
+
return getConfigProvider('request')!({
|
271
|
+
url: '/de/file/deletefile',
|
272
|
+
method: 'post',
|
273
|
+
data: deepMerge({}, toValue(params), toValue(overlapParams)),
|
274
|
+
});
|
275
|
+
},
|
276
|
+
options,
|
277
|
+
);
|
278
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import type { UseRequestOptions } from '@mixte/use';
|
2
|
+
import type { ResponseData, ResponseListData } from '@smartos-lib/types';
|
3
|
+
import { getConfigProvider } from '@smartos-lib/core';
|
4
|
+
import { deepMerge } from 'mixte';
|
5
|
+
import type { MaybeRefOrGetter } from 'vue';
|
6
|
+
import { menuStore } from '../stores/menu';
|
7
|
+
import type { FileInfo, GetFileListParams } from './file';
|
8
|
+
|
9
|
+
let meActiveMenuInfo: ReturnType<typeof menuStore>['meActiveMenuInfo'];
|
10
|
+
|
11
|
+
// 有循环引用的问题, 延迟再获取
|
12
|
+
Promise.resolve().then(() => {
|
13
|
+
meActiveMenuInfo = menuStore().meActiveMenuInfo;
|
14
|
+
});
|
15
|
+
|
16
|
+
/** 文件夹信息 */
|
17
|
+
export interface DirInfo {
|
18
|
+
/** 文件夹 ID */
|
19
|
+
id: string
|
20
|
+
/** 文件夹名称 */
|
21
|
+
label: string
|
22
|
+
/** 文件夹路径 */
|
23
|
+
filePath: string
|
24
|
+
/** 子文件夹 */
|
25
|
+
children: DirInfo[]
|
26
|
+
}
|
27
|
+
|
28
|
+
/** 获取目录树 */
|
29
|
+
export function getDirTree(options?: UseRequestOptions) {
|
30
|
+
return useRequestReactive<{
|
31
|
+
data: ResponseData<DirInfo>
|
32
|
+
}>(
|
33
|
+
() => getConfigProvider('request')!({ url: '/de/file/getfiletree', method: 'get' }),
|
34
|
+
options,
|
35
|
+
);
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* 获取文件夹列表 ( 不包含文件 )
|
40
|
+
*/
|
41
|
+
export function getFolderList(params?: MaybeRefOrGetter<GetFileListParams>, options?: UseRequestOptions) {
|
42
|
+
return useRequestReactive<{
|
43
|
+
data: ResponseData<ResponseListData<FileInfo>>
|
44
|
+
}>(
|
45
|
+
(overlapParams?: MaybeRefOrGetter<GetFileListParams>) => {
|
46
|
+
return getConfigProvider('request')!({
|
47
|
+
url: '/de/file/getFileList',
|
48
|
+
method: 'get',
|
49
|
+
params: deepMerge({ currentPage: 1, pageCount: 50, filePath: '/', fileType: '0' }, toValue(params), toValue(overlapParams), { isDir: 1 }),
|
50
|
+
});
|
51
|
+
},
|
52
|
+
options,
|
53
|
+
);
|
54
|
+
}
|
55
|
+
|
56
|
+
/** 创建文件夹 */
|
57
|
+
export function createDir(params?: { fileName: string }, options?: UseRequestOptions) {
|
58
|
+
return useRequestReactive<{
|
59
|
+
data: ResponseData<DirInfo>
|
60
|
+
}>(
|
61
|
+
(overlapParams?: { fileName: string }) => getConfigProvider('request')!({
|
62
|
+
url: '/de/file/createFold',
|
63
|
+
method: 'post',
|
64
|
+
data: deepMerge(
|
65
|
+
{ filePath: meActiveMenuInfo.value?.filePath ?? '/' },
|
66
|
+
toValue(params),
|
67
|
+
toValue(overlapParams),
|
68
|
+
),
|
69
|
+
}),
|
70
|
+
options,
|
71
|
+
);
|
72
|
+
}
|