@smartos-lib/components 1.7.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ :::