@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,17 @@
1
+ import type { RenderElementProps } from 'slate-react';
2
+ import type { HeadingElement } from '../../types/custom-types';
3
+
4
+ /**
5
+ * 标题
6
+ */
7
+ export function Heading(props: RenderElementProps) {
8
+ const element = props.element as HeadingElement;
9
+
10
+ const Tag = `docx-h${element.level}`;
11
+
12
+ return (
13
+ <Tag {...props.attributes}>
14
+ {props.children}
15
+ </Tag>
16
+ );
17
+ }
@@ -0,0 +1,16 @@
1
+ docx-ul, docx-ol, docx-li {
2
+ @apply block;
3
+ }
4
+
5
+ docx-li {
6
+ @apply list-none;
7
+
8
+ &:not(.ordered) {
9
+ @apply relative lh-6 pl-6;
10
+ @apply before-(
11
+ content-[''] size-6 i-ci-dot-01-xs pointer-events-none
12
+ flex justify-left items-center
13
+ absolute top-0 left-0 -translate-x-1/4
14
+ )
15
+ }
16
+ }
@@ -0,0 +1,39 @@
1
+ import type { RenderElementProps } from 'slate-react';
2
+ import { createContext } from 'react';
3
+ import type { ListElement } from '../../types/custom-types';
4
+
5
+ const { Provider, Consumer } = createContext(false);
6
+
7
+ /**
8
+ * 列表
9
+ */
10
+ export function List(props: RenderElementProps) {
11
+ const element = props.element as ListElement;
12
+
13
+ const Tag = `docx-${element.ordered ? 'o' : 'u'}l`;
14
+
15
+ return (
16
+ <Provider value={!!element.ordered}>
17
+ <Tag {...props.attributes}>
18
+ {props.children}
19
+ </Tag>
20
+ </Provider>
21
+ );
22
+ }
23
+
24
+ /**
25
+ * 列表项
26
+ */
27
+ export function ListItem(props: RenderElementProps) {
28
+ return (
29
+ <Consumer>
30
+ {(ordered) => {
31
+ return (
32
+ <docx-li className={ordered ? 'ordered' : ''}>
33
+ {props.children}
34
+ </docx-li>
35
+ );
36
+ }}
37
+ </Consumer>
38
+ );
39
+ }
@@ -0,0 +1,69 @@
1
+ import { BaseEditor, BaseRange, Descendant } from "slate"
2
+ import { ReactEditor } from "slate-react"
3
+ import { HistoryEditor } from 'slate-history'
4
+
5
+ /** 段落节点 */
6
+ export type ParagraphElement = {
7
+ type: 'paragraph'
8
+ align?: string
9
+ children: Descendant[]
10
+ }
11
+
12
+ /** 标题节点 - H1 ~ H6 */
13
+ export type HeadingElement = {
14
+ type: 'heading'
15
+ level: string
16
+ children: Descendant[]
17
+ }
18
+
19
+ /** 引用节点 */
20
+ export type BlockquoteElement = {
21
+ type: 'blockquote'
22
+ children: Descendant[]
23
+ }
24
+
25
+ /** 列表节点 */
26
+ export type ListElement = {
27
+ type: 'list'
28
+ /** 是否为有序列表 */
29
+ ordered?: boolean
30
+ children: Descendant[]
31
+ }
32
+ /** 列表项节点 */
33
+ export type ListItemElement = {
34
+ type: 'list-item'
35
+ children: Descendant[]
36
+ }
37
+
38
+ /** 所有自定义节点 */
39
+ type CustomElement = ParagraphElement |
40
+ HeadingElement |
41
+ BlockquoteElement |
42
+ ListElement |
43
+ ListItemElement;
44
+
45
+ /** 文本节点 */
46
+ type CustomText = {
47
+ bold?: boolean
48
+ italic?: boolean
49
+ del?: boolean
50
+ underline?: boolean
51
+ code?: boolean
52
+ text: string
53
+ }
54
+
55
+ /** 扩展的编辑器类型 */
56
+ type CustomEditor = BaseEditor & ReactEditor & HistoryEditor & {
57
+ nodeToDecorations?: Map<Element, Range[]>
58
+ }
59
+
60
+ declare module 'slate' {
61
+ interface CustomTypes {
62
+ Editor: CustomEditor
63
+ Element: CustomElement
64
+ Text: CustomText
65
+ Range: BaseRange & {
66
+ [key: string]: unknown
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,50 @@
1
+ import { shadowRoot } from '../shared/const';
2
+
3
+ /**
4
+ * 获取文档选区
5
+ */
6
+ export const useTextSelection = createSharedComposable(() => {
7
+ const selection = ref<Selection | null>(null);
8
+ const text = computed(() => selection.value?.toString() ?? '');
9
+
10
+ const isValidSelection = computed(() => text.value && selection.value!.rangeCount > 0);
11
+
12
+ const range = computed(() => {
13
+ if (isValidSelection.value) {
14
+ const range = selection.value!.getRangeAt(0).cloneRange();
15
+
16
+ // 使选区在鼠标终点处折叠
17
+ range.collapse(range.startContainer === selection.value!.focusNode);
18
+
19
+ // 修复选区在文本开头时,无法获取到正确的位置的问题
20
+ if (range.startOffset === 0) {
21
+ const newRange = document.createRange();
22
+ newRange.setStart(range.startContainer, 0);
23
+ newRange.setEnd(range.startContainer, 1);
24
+ return newRange;
25
+ }
26
+
27
+ return range;
28
+ }
29
+ });
30
+
31
+ const onValidSelectionUpdate = createEventHook<Selection>();
32
+
33
+ wheneverEffectScopeImmediate(shadowRoot, () => {
34
+ useEventListener(window.document, 'selectionchange', () => {
35
+ selection.value = null;
36
+ selection.value = shadowRoot.value.getSelection();
37
+
38
+ if (isValidSelection.value)
39
+ onValidSelectionUpdate.trigger(selection.value!);
40
+ });
41
+ });
42
+
43
+ return {
44
+ text,
45
+ isValidSelection,
46
+ range,
47
+ selection,
48
+ onValidSelectionUpdate: onValidSelectionUpdate.on,
49
+ };
50
+ });
@@ -0,0 +1,19 @@
1
+ /* Markdown 样式 */
2
+
3
+ // 代码转换层
4
+ [data-use-vue-component-wrap], [__use_react_component_wrap]
5
+ @apply contents
6
+
7
+ // 根元素
8
+ .smart-docx-editor
9
+ @apply root size-full outline-none
10
+
11
+ > :first-child
12
+ @apply important:mt-0
13
+
14
+ // 标题
15
+ @import "./components-react/elements/Heading/index.sass"
16
+ // 引用
17
+ @import "./components-react/elements/Blockquote/index.sass"
18
+ // 列表 | 列表项
19
+ @import "./components-react/elements/List/index.scss"
@@ -0,0 +1,21 @@
1
+ <!-- Markdown 功能组装层 / 样式层 -->
2
+
3
+ <template>
4
+ <!-- Markdown -->
5
+ <Markdown v-bind="props" />
6
+
7
+ <!-- 菜单栏 -->
8
+ <Menu />
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ import Menu from './components/Menu.vue';
13
+ import Markdown from './components/Markdown.vue';
14
+ import type { MarkdownProps } from './components/types';
15
+
16
+ const props = defineProps<Pick<MarkdownProps, 'initialValue'>>();
17
+ </script>
18
+
19
+ <style lang="sass">
20
+ @import url("./index.sass")
21
+ </style>
@@ -0,0 +1,23 @@
1
+ import type { BaseSelection, Descendant, Editor } from 'slate';
2
+
3
+ /**
4
+ * 编辑器实例
5
+ */
6
+ export const editor = ref() as Ref<Editor>;
7
+
8
+ /**
9
+ * 编辑器 ShadowRoot
10
+ */
11
+ export const shadowRoot = ref() as Ref<ShadowRoot & {
12
+ getSelection: Window['getSelection']
13
+ }>;
14
+
15
+ /**
16
+ * 选区变化的事件回调
17
+ */
18
+ export const selectionChange = createEventHook<BaseSelection>();
19
+
20
+ /**
21
+ * 值变化的事件回调
22
+ */
23
+ export const valueChange = createEventHook<Descendant[]>();
@@ -0,0 +1,23 @@
1
+ import { Editor } from 'slate';
2
+ import { editor } from '../shared/const';
3
+ import type { CustomText } from '../components-react/types/custom-types';
4
+
5
+ /**
6
+ * 判断当前选区是否是某个格式
7
+ */
8
+ export function isMarkActive(format: keyof Omit<CustomText, 'text'>) {
9
+ const marks = Editor.marks(editor.value);
10
+ return marks ? marks[format] === true : false;
11
+ }
12
+
13
+ /**
14
+ * 切换选区的格式
15
+ */
16
+ export function toggleMark(format: keyof Omit<CustomText, 'text'>) {
17
+ const isActive = isMarkActive(format);
18
+
19
+ if (isActive)
20
+ Editor.removeMark(editor.value, format);
21
+ else
22
+ Editor.addMark(editor.value, format, true);
23
+ }
@@ -0,0 +1,38 @@
1
+ export const 所有支持的节点 = {
2
+ initialValue: [
3
+ {
4
+ type: 'paragraph',
5
+ children: [
6
+ { text: '段落, 可以是' },
7
+ { text: '粗体, ', bold: true },
8
+ { text: '也可以' },
9
+ { text: '是斜体', italic: true },
10
+ { text: '和包含' },
11
+ { text: '删除线', del: true },
12
+ { text: '及' },
13
+ { text: '下划线', underline: true },
14
+ { text: '的文本' },
15
+ ],
16
+ },
17
+ { type: 'heading', level: '1', children: [{ text: 'H1 标题' }] },
18
+ { type: 'heading', level: '2', children: [{ text: 'H2 标题' }] },
19
+ { type: 'heading', level: '3', children: [{ text: 'H3 标题' }] },
20
+ { type: 'heading', level: '4', children: [{ text: 'H4 标题' }] },
21
+ { type: 'heading', level: '5', children: [{ text: 'H5 标题' }] },
22
+ { type: 'heading', level: '6', children: [{ text: 'H6 标题' }] },
23
+ {
24
+ type: 'blockquote',
25
+ children: [
26
+ { type: 'paragraph', children: [{ text: '引用' }] },
27
+ { type: 'paragraph', children: [{ text: '节点' }] },
28
+ ],
29
+ },
30
+ {
31
+ type: 'list',
32
+ children: [
33
+ { type: 'list-item', children: [{ text: '无序' }] },
34
+ { type: 'list-item', children: [{ text: '列表' }] },
35
+ ],
36
+ },
37
+ ],
38
+ };
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <smart-docx-editor
3
+ :initialValue.prop="所有支持的节点.initialValue"
4
+ />
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import '@smartos-lib/core/dist/componentsProvider';
9
+ import '@@/dist/smart-docx-editor/index';
10
+ import { 所有支持的节点 } from './data';
11
+ </script>
@@ -0,0 +1,3 @@
1
+ ::: warning
2
+ 开发中
3
+ :::
@@ -0,0 +1,5 @@
1
+ import App from './App.vue';
2
+ import { tag } from './info';
3
+ import { defineCustomElements } from '@/utils/defineCustomElements';
4
+
5
+ defineCustomElements(tag, App);
@@ -0,0 +1,12 @@
1
+ <!-- Markdown 预留层 -->
2
+
3
+ <template>
4
+ <MarkdownShortcuts v-bind="props" />
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import type { MarkdownProps } from './MarkdownShortcuts/components/types';
9
+ import MarkdownShortcuts from './MarkdownShortcuts/index.vue';
10
+
11
+ const props = defineProps<Pick<MarkdownProps, 'initialValue'>>();
12
+ </script>
@@ -0,0 +1,2 @@
1
+ export const name = '云文档编辑器';
2
+ export const tag = 'smart-docx-editor';
@@ -0,0 +1,171 @@
1
+ <!-- 代码 / 文件预览 -->
2
+
3
+ <template>
4
+ <div size-full flex="~ justify-center items-center" pos="absolute top-0 left-0" c-neutral bg-dark>
5
+ <!-- 加载中 -->
6
+ <div v-if="isFileReading || isEditorLoading" flex="~ col gap-2 items-center">
7
+ <i-svg-spinners-blocks-shuffle-3 />
8
+ <div v-if="isFileReading" text-sm>文件读取中, 当前进度 {{ fileReadPercent }}%</div>
9
+ </div>
10
+ <!-- 不支持的文件 -->
11
+ <div v-else-if="isUnsupportedLanguage" flex="~ col justify-center items-center gap-3" c-gray>
12
+ <i-codicon-warning class="text-(5xl yellow)" />
13
+ 此文件类型暂不支持预览
14
+ <el-button class="rounded-sm" color="#3177d3" @click="initEditor()">仍然打开</el-button>
15
+ </div>
16
+ <!-- 编辑器 -->
17
+ <div v-else ref="editorRef" size-full />
18
+ </div>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ import type * as Monaco from 'monaco-editor';
23
+ import loader from '@monaco-editor/loader';
24
+ import { getFastestCDN } from '@mixte/snippets/getFastestCDN';
25
+ import { shadowRoot } from '../shared/const';
26
+ import { devDependencies } from '../../../../../../package.json';
27
+
28
+ const props = defineProps<{ file: Blob; fileExt: string }>();
29
+
30
+ const editorRef = ref();
31
+ const { width, height } = useElementSize(editorRef);
32
+
33
+ const isFileReading = ref(false);
34
+ const isEditorLoading = ref(false);
35
+ const isUnsupportedLanguage = ref(false);
36
+
37
+ const monaco = shallowRef<typeof Monaco>();
38
+ const editor = shallowRef<Monaco.editor.IStandaloneCodeEditor>();
39
+ const languages = shallowRef<Monaco.languages.ILanguageExtensionPoint[]>();
40
+ const language = ref<string>();
41
+ const value = ref<string>();
42
+ const fileReadPercent = ref(0);
43
+
44
+ const fastestCDN = useRequestReactive(
45
+ () => getFastestCDN('monaco-editor', {
46
+ version: devDependencies['monaco-editor'].replace(/^\^/g, ''),
47
+ file: `/min/vs/basic-languages/css/css.js`,
48
+ }),
49
+ { immediate: true },
50
+ );
51
+
52
+ /**
53
+ * 创建 monaco 编辑器
54
+ */
55
+ async function createEditor() {
56
+ editor.value?.dispose();
57
+ isEditorLoading.value = true;
58
+ isUnsupportedLanguage.value = false;
59
+
60
+ await until(() => fastestCDN.isSuccess).toBeTruthy();
61
+
62
+ loader.config({
63
+ "vs/nls": {
64
+ availableLanguages: {
65
+ '*': 'zh-cn',
66
+ },
67
+ },
68
+ "paths": {
69
+ vs: `${fastestCDN.response}/min/vs`,
70
+ },
71
+ });
72
+
73
+ try {
74
+ monaco.value = await loader.init();
75
+ languages.value ??= monaco.value.languages.getLanguages();
76
+ language.value = languages.value.find(lang => lang.extensions?.includes(`.${props.fileExt}`))?.id;
77
+ isEditorLoading.value = false;
78
+
79
+ if (!(isUnsupportedLanguage.value = !language.value))
80
+ initEditor();
81
+ }
82
+ catch (error) {
83
+ isEditorLoading.value = false;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 初始化 monaco 编辑器
89
+ */
90
+ async function initEditor() {
91
+ isUnsupportedLanguage.value = false;
92
+ await nextTick();
93
+
94
+ editor.value = monaco.value!.editor.create(editorRef.value!, {
95
+ // 值
96
+ value: value.value,
97
+ // 编辑器语言
98
+ language: language.value || 'plaintext',
99
+ // 一个制表符等于的空格数
100
+ tabSize: 2,
101
+ // 在通过鼠标添加多个光标时使用的修改键
102
+ multiCursorModifier: 'ctrlCmd',
103
+ // 代码缩略地图
104
+ minimap: {
105
+ showSlider: 'always',
106
+ },
107
+ // 代码编辑器边距
108
+ padding: {
109
+ top: 10,
110
+ },
111
+ // 自动换行
112
+ wordWrap: 'on',
113
+ // 主题
114
+ theme: 'vs-dark',
115
+ // 只读
116
+ readOnly: true,
117
+ });
118
+ }
119
+
120
+ // 读取文件内容
121
+ wheneverEffectScopeImmediate(() => props.file, (file, _, onCleanup) => {
122
+ isFileReading.value = true;
123
+
124
+ const reader = new FileReader();
125
+
126
+ reader.onprogress = (event) => {
127
+ fileReadPercent.value = Math.round((event.loaded / event.total) * 100);
128
+ };
129
+ reader.onload = async (event) => {
130
+ isFileReading.value = false;
131
+ value.value = event.target?.result as string;
132
+ createEditor();
133
+ };
134
+
135
+ reader.readAsText(file);
136
+
137
+ onCleanup(() => {
138
+ reader.abort();
139
+ });
140
+ });
141
+
142
+ // 挂载 monaco 编辑器样式至 web components 组件中
143
+ wheneverEffectScopeImmediate(shadowRoot, (shadowRoot) => {
144
+ const selectors = `[data-name^="vs/editor"]`;
145
+
146
+ function insertStyle() {
147
+ shadowRoot!.querySelectorAll(selectors).forEach((link) => {
148
+ shadowRoot!.removeChild(link);
149
+ });
150
+
151
+ Array.from(document.querySelectorAll(selectors)).forEach((link) => {
152
+ shadowRoot.appendChild(link.cloneNode(true));
153
+ });
154
+ }
155
+
156
+ useMutationObserver(document.head, insertStyle, {
157
+ childList: true,
158
+ });
159
+
160
+ insertStyle();
161
+ });
162
+
163
+ // 动态调整编辑器大小
164
+ watchDeep(() => ({ width: width.value, height: height.value }), (size) => {
165
+ editor.value?.layout(size);
166
+ });
167
+
168
+ onUnmounted(() => {
169
+ editor.value?.dispose();
170
+ });
171
+ </script>
@@ -0,0 +1,49 @@
1
+ <!-- 图片预览 -->
2
+
3
+ <template>
4
+ <div size-full>
5
+ <div ref="rootRef" size-full>
6
+ <img v-if="url" :src="url" hidden>
7
+ </div>
8
+ </div>
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ import Viewer from 'viewerjs';
13
+
14
+ const props = defineProps<{ file: Blob }>();
15
+
16
+ const rootRef = ref();
17
+
18
+ const url = useObjectUrl(() => props.file);
19
+
20
+ wheneverEffectScopeImmediate(() => rootRef.value && url.value, (_, __, onCleanup) => {
21
+ const viewer = new Viewer(rootRef.value, {
22
+ inline: true,
23
+ navbar: false,
24
+ title: false,
25
+ transition: false,
26
+ toolbar: {
27
+ reset: true,
28
+ rotateLeft: true,
29
+ rotateRight: true,
30
+ zoomIn: true,
31
+ zoomOut: true,
32
+ flipHorizontal: true,
33
+ flipVertical: true,
34
+ },
35
+ });
36
+
37
+ onCleanup(() => {
38
+ viewer.destroy();
39
+ });
40
+ });
41
+ </script>
42
+
43
+ <style lang="scss">
44
+ @import "viewerjs/dist/viewer.css";
45
+
46
+ .viewer-button.viewer-fullscreen {
47
+ display: none;
48
+ }
49
+ </style>
@@ -0,0 +1,14 @@
1
+ <!-- PDF 预览 -->
2
+
3
+ <template>
4
+ <iframe
5
+ size-full
6
+ frameborder="0"
7
+ :src="url"
8
+ />
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ const props = defineProps<{ file: Blob }>();
13
+ const url = useObjectUrl(() => props.file);
14
+ </script>
@@ -0,0 +1,27 @@
1
+ <!-- 视频预览 -->
2
+
3
+ <template>
4
+ <video
5
+ ref="videoRef"
6
+ class="size-full absolute top-0 left-0"
7
+ controls
8
+ />
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ const props = defineProps<{ file: Blob }>();
13
+
14
+ const videoRef = ref();
15
+
16
+ const url = useObjectUrl(() => props.file);
17
+
18
+ wheneverEffectScopeImmediate(url, () => {
19
+ const { playing } = useMediaControls(videoRef, {
20
+ src: () => url.value!,
21
+ });
22
+
23
+ nextTick(() => {
24
+ playing.value = true;
25
+ });
26
+ });
27
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div mb2>
3
+ <el-button @click="open()">点击选择文件进行预览</el-button>
4
+ </div>
5
+
6
+ <div flex="~ justify-center items-center gap2" text="xs teal-5">
7
+ <i-ic-twotone-keyboard-arrow-down class="size-6" />
8
+ 文件预览组件
9
+ <i-ic-twotone-keyboard-arrow-down class="size-6" />
10
+ </div>
11
+
12
+ <div b="1 solid neutral-2 dark:neutral-6" aspect-ratio-video>
13
+ <smart-file-preview ref="smartFilePreviewRef" v-bind="attrs" />
14
+ </div>
15
+ </template>
16
+
17
+ <script lang="ts" setup>
18
+ import type { SmartFilePreviewElement } from '@@/dist/smart-file-preview/index';
19
+ import '@smartos-lib/core/dist/componentsProvider';
20
+ import '@@/dist/smart-file-preview/index';
21
+
22
+ const attrs = useAttrs();
23
+
24
+ const smartFilePreviewRef = ref<SmartFilePreviewElement>();
25
+
26
+ const { files, open } = useFileDialog({
27
+ multiple: false,
28
+ });
29
+
30
+ watch(() => files.value?.[0], (file) => {
31
+ if (file)
32
+ smartFilePreviewRef.value!.previewFile(file.name, file);
33
+ });
34
+ </script>
@@ -0,0 +1,5 @@
1
+ 支持对多种文件类型的预览
2
+
3
+ ::: warning
4
+ 开发中
5
+ :::