183 lines
4.6 KiB
Vue
183 lines
4.6 KiB
Vue
<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>
|