Carson 7 місяців тому
батько
коміт
f14aa2cd0e

+ 4 - 0
.vitepress/theme/custom-doc.scss

@@ -8,4 +8,8 @@
       margin-top: 5px;
     }
   }
+
+  video {
+    margin: auto;
+  }
 }

+ 96 - 9
components/Edit/VideoToolBarItem.vue

@@ -1,25 +1,112 @@
 <script setup lang="ts">
-import { defineProps } from "vue";
+import { ref, inject, type Ref } from "vue";
 import { VideoCamera } from "@element-plus/icons-vue";
-import { NormalToolbar } from "md-editor-v3";
+import { DropdownToolbar } from "md-editor-v3";
+import { Link } from "@element-plus/icons-vue";
+import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
+import { v4 as uuid4 } from "uuid";
+
+const prefix = "md-editor";
+
+const s3 = inject<InstanceType<typeof S3Client>>("s3");
+const editorLoading = inject<Ref<boolean>>("editorLoading");
 
 const props = defineProps<{
   insert?: Function;
 }>();
 
-const onClick = () => {
-  console.log("video");
-  // TODO
-  // emitHandler('video');
+const showDropdown = ref(false);
+const showDialog = ref(false);
+
+const linkInput = ref("");
+
+const onClickMenuLink = () => {
+  showDropdown.value = false;
+  showDialog.value = true;
+};
+
+const selectFile = (contentType: string, multiple: boolean) => {
+  return new Promise((resolve) => {
+    let input = document.createElement("input");
+    input.type = "file";
+    input.multiple = multiple;
+    input.accept = contentType;
+
+    input.onchange = (_) => {
+      let files = Array.from(input.files);
+      if (multiple) resolve(files);
+      else resolve(files[0]);
+    };
+
+    input.click();
+  });
+};
+
+const onClickMenuSelect = async () => {
+  showDropdown.value = false;
+  const file = await selectFile("video/*", false);
+
+  const key = `${uuid4()}::${file.name}`;
+  const command = new PutObjectCommand({
+    Bucket: import.meta.env.VITE_DOCS_MEDIA_BUCKET,
+    Key: key,
+    Body: file,
+    ACL: "public-read",
+  });
+  editorLoading!.value = true;
+  await s3!.send(command);
+  editorLoading!.value = false;
+  insert(
+    `https://${import.meta.env.VITE_DOCS_MEDIA_BUCKET}.s3.amazonaws.com/${key}`
+  );
+};
+
+const onConfirmDialog = async () => {
+  // FIXME check link format?
+  await insert(linkInput.value);
+  linkInput.value = "";
+  showDialog.value = false;
+};
+
+const insert = (url: string) => {
+  // TEST
+  // const url = `https://woolyss.com/f/av1-opus-sita.webm`;
   props.insert?.(() => ({
-    targetValue: "hello video",
+    targetValue: `<video src="${url}" width="100%" controls></video>`,
   }));
 };
 </script>
 <template>
-  <NormalToolbar title="video" @onClick="onClick">
+  <DropdownToolbar title="video" :visible="showDropdown" :onChange="(v) => (showDropdown = v)">
     <template #trigger>
       <VideoCamera width="18" />
     </template>
-  </NormalToolbar>
+    <template #overlay>
+      <ul :class="`${prefix}-menu`">
+        <li :class="`${prefix}-menu-item ${prefix}-menu-item-title`" @click="onClickMenuLink">
+          输入链接
+        </li>
+        <li :class="`${prefix}-menu-item ${prefix}-menu-item-title`" @click="onClickMenuSelect">
+          选择文件
+        </li>
+      </ul>
+    </template>
+  </DropdownToolbar>
+  <el-dialog v-model="showDialog" title="添加视频">
+    <div>
+      <el-input v-model="linkInput" placeholder="https://woolyss.com/f/av1-opus-sita.webm">
+        <template #prepend>
+          <el-icon>
+            <Link />
+          </el-icon>
+        </template>
+      </el-input>
+    </div>
+    <template #footer>
+      <div>
+        <el-button @click="showDialog = false">取消</el-button>
+        <el-button type="primary" @click="onConfirmDialog"> 确认 </el-button>
+      </div>
+    </template>
+  </el-dialog>
 </template>

+ 4 - 1
components/Edit/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, onMounted, computed, nextTick, onUnmounted } from "vue";
+import { ref, onMounted, computed, nextTick, onUnmounted, provide } from "vue";
 import _ from "lodash";
 import { v4 as uuid4 } from "uuid";
 import {
@@ -35,6 +35,8 @@ const s3 = new S3Client({
   region: import.meta.env.VITE_AWS_S3_REGION,
 });
 
+provide('s3', s3)
+
 const SIDEBAR_SORTED_MAP_KEY = "SIDEBAR_SORTED_MAP.json";
 
 const tree$ = ref<InstanceType<typeof ElTree>>();
@@ -135,6 +137,7 @@ const onNodeClick = async (data: TreeData, node) => {
 
 const text = ref("");
 const textLoading = ref(false);
+provide('editorLoading', textLoading)
 
 const onUploadImg = async (
   files: File[],