@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.
Files changed (128) hide show
  1. package/.eslintrc +12 -0
  2. package/.eslintrc-auto-import.json +332 -0
  3. package/Components.code-workspace +143 -0
  4. package/LICENSE +21 -0
  5. package/dist/smart-docx-editor/index.d.ts +2 -0
  6. package/dist/smart-docx-editor/index.js +68 -0
  7. package/dist/smart-file-preview/index.d.ts +18 -0
  8. package/dist/smart-file-preview/index.js +37 -0
  9. package/dist/smart-upload/index.d.ts +2 -0
  10. package/dist/smart-upload/index.js +800 -0
  11. package/index.html +16 -0
  12. package/package.json +23 -0
  13. package/public/favicon.svg +6 -0
  14. package/scripts/components.vite.config.ts +96 -0
  15. package/scripts/shared.ts +9 -0
  16. package/src/App.vue +28 -0
  17. package/src/components/Logo/index.vue +15 -0
  18. package/src/components-private/.gitkeep +0 -0
  19. package/src/composables/useElementStyle.ts +23 -0
  20. package/src/composables/useNaiveStyle.ts +43 -0
  21. package/src/composables/useNaiveTheme.ts +71 -0
  22. package/src/composables/useSmart.ts +36 -0
  23. package/src/layouts/default.vue +3 -0
  24. package/src/main.ts +33 -0
  25. package/src/modules/pinia/index.ts +8 -0
  26. package/src/modules/progress/index.ts +12 -0
  27. package/src/modules/router/install.ts +9 -0
  28. package/src/modules/router/routes.ts +40 -0
  29. package/src/pages/[...all].vue +21 -0
  30. package/src/pages/frame/component/[name].vue +14 -0
  31. package/src/pages/frame/index.vue +81 -0
  32. package/src/pages/index/composables/useTabsManage.ts +46 -0
  33. package/src/pages/index/index.vue +111 -0
  34. package/src/pages/index/type.ts +13 -0
  35. package/src/pages/index/utils/index.ts +41 -0
  36. package/src/settings.ts +9 -0
  37. package/src/shared/components.ts +52 -0
  38. package/src/shared/env.ts +11 -0
  39. package/src/shared/unocss.theme.ts +1600 -0
  40. package/src/stores/theme.ts +29 -0
  41. package/src/styles/element.scss +3 -0
  42. package/src/styles/styles.scss +21 -0
  43. package/src/types.ts +20 -0
  44. package/src/utils/callCustomElementExposed.ts +6 -0
  45. package/src/utils/deepCloneESModule.ts +10 -0
  46. package/src/utils/defineCustomElements.ts +18 -0
  47. package/src/utils/formatComponentsGlob.ts +16 -0
  48. package/src/utils/getFileMD5.ts +31 -0
  49. package/src/utils/getFileNameAndExt.ts +11 -0
  50. package/src/utils/isFileEqual.ts +13 -0
  51. package/src/utils/jsonToFormData.ts +8 -0
  52. package/src/web-components/smart-docx-drive-page/App.vue +37 -0
  53. package/src/web-components/smart-docx-drive-page/apis/doc.ts +85 -0
  54. package/src/web-components/smart-docx-drive-page/apis/file.ts +278 -0
  55. package/src/web-components/smart-docx-drive-page/apis/folder.ts +72 -0
  56. package/src/web-components/smart-docx-drive-page/children/Home.vue +8 -0
  57. package/src/web-components/smart-docx-drive-page/children/Me.vue +47 -0
  58. package/src/web-components/smart-docx-drive-page/components/CustomImage.vue +26 -0
  59. package/src/web-components/smart-docx-drive-page/components/CustomPopover.vue +62 -0
  60. package/src/web-components/smart-docx-drive-page/components/DocxDir.vue +99 -0
  61. package/src/web-components/smart-docx-drive-page/components/DocxDoc.vue +132 -0
  62. package/src/web-components/smart-docx-drive-page/components/DocxDownloadPopoverItem.vue +41 -0
  63. package/src/web-components/smart-docx-drive-page/components/DocxFileList.vue +156 -0
  64. package/src/web-components/smart-docx-drive-page/components/DocxPreview.vue +33 -0
  65. package/src/web-components/smart-docx-drive-page/components/DocxUpload.vue +164 -0
  66. package/src/web-components/smart-docx-drive-page/components/FileIcon.vue +62 -0
  67. package/src/web-components/smart-docx-drive-page/components-private/Header.vue +65 -0
  68. package/src/web-components/smart-docx-drive-page/components-private/Logo.vue +15 -0
  69. package/src/web-components/smart-docx-drive-page/components-private/Menu.vue +34 -0
  70. package/src/web-components/smart-docx-drive-page/components-private/Navbar.vue +36 -0
  71. package/src/web-components/smart-docx-drive-page/composables/useFullscreenElDialog.ts +41 -0
  72. package/src/web-components/smart-docx-drive-page/composables/usePrompt.ts +73 -0
  73. package/src/web-components/smart-docx-drive-page/data.ts +10 -0
  74. package/src/web-components/smart-docx-drive-page/external-style/custom-popover.sass +8 -0
  75. package/src/web-components/smart-docx-drive-page/external-style/index.sass +1 -0
  76. package/src/web-components/smart-docx-drive-page/index.ts +20 -0
  77. package/src/web-components/smart-docx-drive-page/index.vue +39 -0
  78. package/src/web-components/smart-docx-drive-page/info.ts +2 -0
  79. package/src/web-components/smart-docx-drive-page/stores/menu.ts +60 -0
  80. package/src/web-components/smart-docx-drive-page/types.ts +51 -0
  81. package/src/web-components/smart-docx-drive-page/utils/file-actions.ts +63 -0
  82. package/src/web-components/smart-docx-drive-page/utils/file.ts +31 -0
  83. package/src/web-components/smart-docx-editor/App.vue +32 -0
  84. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/Markdown.vue +202 -0
  85. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/Menu.vue +100 -0
  86. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components/types.ts +6 -0
  87. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/Markdown.tsx +71 -0
  88. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/MarkdownElement.tsx +81 -0
  89. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Blockquote/index.sass +6 -0
  90. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Blockquote/index.tsx +12 -0
  91. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Heading/index.sass +14 -0
  92. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/Heading/index.tsx +17 -0
  93. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/List/index.scss +16 -0
  94. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/elements/List/index.tsx +39 -0
  95. package/src/web-components/smart-docx-editor/MarkdownShortcuts/components-react/types/custom-types.d.ts +69 -0
  96. package/src/web-components/smart-docx-editor/MarkdownShortcuts/composables/useTextSelection.ts +50 -0
  97. package/src/web-components/smart-docx-editor/MarkdownShortcuts/index.sass +19 -0
  98. package/src/web-components/smart-docx-editor/MarkdownShortcuts/index.vue +21 -0
  99. package/src/web-components/smart-docx-editor/MarkdownShortcuts/shared/const.ts +23 -0
  100. package/src/web-components/smart-docx-editor/MarkdownShortcuts/utils/slateHelpers.ts +23 -0
  101. package/src/web-components/smart-docx-editor/data.ts +38 -0
  102. package/src/web-components/smart-docx-editor/demo.vue +11 -0
  103. package/src/web-components/smart-docx-editor/index.md +3 -0
  104. package/src/web-components/smart-docx-editor/index.ts +5 -0
  105. package/src/web-components/smart-docx-editor/index.vue +12 -0
  106. package/src/web-components/smart-docx-editor/info.ts +2 -0
  107. package/src/web-components/smart-file-preview/category/Code.vue +171 -0
  108. package/src/web-components/smart-file-preview/category/Image.vue +49 -0
  109. package/src/web-components/smart-file-preview/category/Pdf.vue +14 -0
  110. package/src/web-components/smart-file-preview/category/Video.vue +27 -0
  111. package/src/web-components/smart-file-preview/demo.vue +34 -0
  112. package/src/web-components/smart-file-preview/index.md +5 -0
  113. package/src/web-components/smart-file-preview/index.ts +29 -0
  114. package/src/web-components/smart-file-preview/index.vue +56 -0
  115. package/src/web-components/smart-file-preview/info.ts +2 -0
  116. package/src/web-components/smart-file-preview/shared/const.ts +4 -0
  117. package/src/web-components/smart-file-preview/types.ts +38 -0
  118. package/src/web-components/smart-upload/index.ts +5 -0
  119. package/src/web-components/smart-upload/index.vue +101 -0
  120. package/src/web-components/smart-upload/info.ts +2 -0
  121. package/src/web-components/smart-upload/types.ts +28 -0
  122. package/tsconfig.json +15 -0
  123. package/types/auto-imports.d.ts +975 -0
  124. package/types/components.d.ts +14 -0
  125. package/types/env.d.ts +8 -0
  126. package/types/shims.d.ts +6 -0
  127. package/unocss.config.ts +23 -0
  128. 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,3 @@
1
+ @forward 'element-plus/theme-chalk/src/mixins/config.scss' with (
2
+ $namespace: 'smart'
3
+ );
@@ -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,6 @@
1
+ /**
2
+ * 获取自定义元素导出变量及方法
3
+ */
4
+ export function getCustomElementExposed(self: any, methodName: string) {
5
+ return self._instance.exposed[methodName];
6
+ }
@@ -0,0 +1,10 @@
1
+ import { deepClone } from 'mixte';
2
+
3
+ /**
4
+ * 深拷贝 ES Module
5
+ */
6
+ export function deepCloneESModule<T>(value: T): T {
7
+ if (value)
8
+ return deepClone(Object.fromEntries(Object.entries(value))) as any;
9
+ return value;
10
+ }
@@ -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,8 @@
1
+ export function jsonToFormData(json: Record<string, any>) {
2
+ const formData = new FormData();
3
+
4
+ for (const key in json)
5
+ formData.append(key, json[key]);
6
+
7
+ return formData;
8
+ }
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <Header title="主页" mb6 />
3
+ <el-scrollbar class="-mr4" view-style="padding-right: 16px" height="100%" />
4
+ </template>
5
+
6
+ <script lang="ts" setup>
7
+ import Header from '../components-private/Header.vue';
8
+ </script>