refactor editor
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<t-popconfirm
|
||||
class="tiptap-media-button"
|
||||
v-model:visible="visible"
|
||||
trigger="click"
|
||||
:confirm-btn="{ content: '插入', theme: 'primary' }"
|
||||
:cancel-btn="{ content: '取消', theme: 'default' }"
|
||||
placement="bottom"
|
||||
@visible-change="onVisibleChange"
|
||||
@confirm="onConfirm"
|
||||
>
|
||||
<template #icon><span></span></template>
|
||||
<template #content>
|
||||
<t-form class="popconfirm-content" label-align="left" label-width="auto">
|
||||
<t-form-item label="文件">
|
||||
<t-button size="small" variant="outline" @click="pickFile">
|
||||
<span
|
||||
class="file-name keep-text"
|
||||
v-text="selectedFile ? '重新选择' : `选择${typeLabel}`"
|
||||
/>
|
||||
</t-button>
|
||||
<span class="word-space gray" v-text="selectedFile?.name || `未选择${typeLabel}文件`" />
|
||||
</t-form-item>
|
||||
<t-form-item label="标题">
|
||||
<t-input v-model="mediaForm.title" :placeholder="`请输入${typeLabel}标题,可留空`" />
|
||||
</t-form-item>
|
||||
<t-form-item label="宽度">
|
||||
<t-input v-model="mediaForm.width" placeholder="请输入宽度,默认 100%" />
|
||||
</t-form-item>
|
||||
<t-form-item label="高度">
|
||||
<t-input v-model="mediaForm.height" placeholder="请输入高度,默认 auto" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</template>
|
||||
<t-button variant="outline" :theme="active ? 'primary' : 'default'" :loading="uploading">
|
||||
<t-icon :name="iconName" />
|
||||
</t-button>
|
||||
</t-popconfirm>
|
||||
<input ref="fileInputRef" class="file-input" type="file" :accept="accept" @change="onFileChange" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { showPlatformMessage } from "../message";
|
||||
import type { TiptapEditorInstance, UploadResult } from "timi-web/editor";
|
||||
|
||||
defineOptions({
|
||||
name: "TiptapMediaButton"
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
editor?: TiptapEditorInstance;
|
||||
active: boolean;
|
||||
type: "image" | "video";
|
||||
upload?: (file: File) => Promise<UploadResult>;
|
||||
}>();
|
||||
|
||||
const visible = ref(false);
|
||||
const uploading = ref(false);
|
||||
const selectedFile = ref<File>();
|
||||
const fileInputRef = ref<HTMLInputElement>();
|
||||
const mediaForm = reactive({
|
||||
title: "",
|
||||
width: "100%",
|
||||
height: ""
|
||||
});
|
||||
|
||||
const typeLabel = computed(() => {
|
||||
return props.type === "image" ? "图片" : "视频";
|
||||
});
|
||||
|
||||
const iconName = computed(() => {
|
||||
return props.type === "image" ? "image-add" : "video";
|
||||
});
|
||||
|
||||
const accept = computed(() => {
|
||||
return props.type === "image" ? "image/*" : "video/*";
|
||||
});
|
||||
|
||||
function buildDefaultHeight() {
|
||||
return "auto";
|
||||
}
|
||||
|
||||
function pickFile(event?: MouseEvent) {
|
||||
event?.stopPropagation();
|
||||
fileInputRef.value?.click();
|
||||
}
|
||||
|
||||
function onVisibleChange(nextVisible: boolean) {
|
||||
if (!nextVisible || !props.editor) {
|
||||
return;
|
||||
}
|
||||
selectedFile.value = undefined;
|
||||
const attributes = props.editor.getAttributes(props.type);
|
||||
mediaForm.title = (attributes.title as string) || "";
|
||||
mediaForm.width = (attributes.width as string) || "100%";
|
||||
mediaForm.height = (attributes.height as string) || buildDefaultHeight();
|
||||
}
|
||||
|
||||
function onFileChange(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
selectedFile.value = target.files?.[0];
|
||||
target.value = "";
|
||||
if (!selectedFile.value) {
|
||||
return;
|
||||
}
|
||||
if (!mediaForm.title.trim()) {
|
||||
mediaForm.title = selectedFile.value.name;
|
||||
}
|
||||
}
|
||||
|
||||
async function onConfirm() {
|
||||
if (!props.editor) {
|
||||
return;
|
||||
}
|
||||
if (!props.upload) {
|
||||
await showPlatformMessage("warning", `${typeLabel.value}上传功能未启用`);
|
||||
return;
|
||||
}
|
||||
if (!selectedFile.value) {
|
||||
await showPlatformMessage("warning", `请选择${typeLabel.value}文件`);
|
||||
return;
|
||||
}
|
||||
uploading.value = true;
|
||||
try {
|
||||
const file = selectedFile.value;
|
||||
const result = await props.upload(file);
|
||||
const title = mediaForm.title.trim() || result.name || file.name;
|
||||
const attrs = {
|
||||
src: result.url,
|
||||
title,
|
||||
width: mediaForm.width.trim() || "100%",
|
||||
height: mediaForm.height.trim() || buildDefaultHeight(),
|
||||
attachmentId: result.id,
|
||||
tempFileId: result.id
|
||||
};
|
||||
if (props.type === "image") {
|
||||
props.editor
|
||||
.chain()
|
||||
.focus()
|
||||
.setImage({
|
||||
src: attrs.src,
|
||||
alt: title,
|
||||
title
|
||||
})
|
||||
.updateAttributes("image", {
|
||||
attachmentId: attrs.attachmentId,
|
||||
tempFileId: attrs.tempFileId,
|
||||
width: attrs.width,
|
||||
height: attrs.height
|
||||
})
|
||||
.run();
|
||||
} else {
|
||||
props.editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContent({
|
||||
type: "video",
|
||||
attrs
|
||||
})
|
||||
.run();
|
||||
}
|
||||
visible.value = false;
|
||||
} catch (error) {
|
||||
console.error(`上传${typeLabel.value}失败:`, error);
|
||||
await showPlatformMessage("error", `上传${typeLabel.value}失败`);
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.popconfirm-content {
|
||||
--td-comp-margin-xxl: .5rem;
|
||||
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user