Files
MincedPad/frontend/src/components/Composer.vue
T
2026-07-14 13:01:44 +08:00

209 lines
4.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import type { UploadRequestOptions } from 'element-plus'
import { createItem, uploadItem, type TTLOption } from '../api/client'
const emit = defineEmits<{ published: [] }>()
const body = ref('')
const ttl = ref<TTLOption>('24h')
const burnAfterRead = ref(false)
const linkOnly = ref(false)
const publishing = ref(false)
const uploading = ref(false)
const shareUrl = ref('')
const accept =
'.png,.jpg,.jpeg,.gif,.webp,.svg,.txt,.md,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.csv,.zip,.7z,.tar,.gz,.rar,.json'
const isPublic = computed(() => !linkOnly.value)
function sharePath(slug: string): string {
return `${window.location.origin}/p/${slug}`
}
async function publishText() {
const text = body.value.trim()
if (!text) {
ElMessage.warning('请输入要发布的内容')
return
}
publishing.value = true
try {
const item = await createItem({
body: text,
is_public: isPublic.value,
burn_after_read: burnAfterRead.value,
ttl: ttl.value,
})
shareUrl.value = sharePath(item.slug)
body.value = ''
ElMessage.success('发布成功')
emit('published')
} catch (err) {
ElMessage.error(err instanceof Error ? err.message : '发布失败')
} finally {
publishing.value = false
}
}
async function customUpload(options: UploadRequestOptions) {
uploading.value = true
try {
const form = new FormData()
form.append('file', options.file)
form.append('is_public', String(isPublic.value))
form.append('burn_after_read', String(burnAfterRead.value))
form.append('ttl', ttl.value)
const item = await uploadItem(form)
shareUrl.value = sharePath(item.slug)
ElMessage.success('上传成功')
options.onSuccess?.(item as unknown as never)
emit('published')
} catch (err) {
const message = err instanceof Error ? err.message : '上传失败'
ElMessage.error(message)
options.onError?.(err as never)
} finally {
uploading.value = false
}
}
async function copyShareUrl() {
if (!shareUrl.value) return
try {
await navigator.clipboard.writeText(shareUrl.value)
ElMessage.success('链接已复制')
} catch {
ElMessage.error('复制失败,请手动选择链接')
}
}
</script>
<template>
<section class="composer">
<h2>发布</h2>
<el-input
v-model="body"
type="textarea"
:rows="8"
placeholder="支持 Markdown…"
maxlength="20000"
show-word-limit
/>
<div class="controls">
<div class="control-row">
<span class="label">有效期</span>
<el-select v-model="ttl" style="width: 100%">
<el-option label="1 小时" value="1h" />
<el-option label="24 小时" value="24h" />
<el-option label="7 天" value="7d" />
<el-option label="永不" value="never" />
</el-select>
</div>
<div class="switches">
<el-switch v-model="burnAfterRead" active-text="阅后即焚" />
<el-switch v-model="linkOnly" active-text="仅链接可见" />
</div>
</div>
<div class="actions">
<el-button
type="primary"
class="full-width-xs"
:loading="publishing"
@click="publishText"
>
发布
</el-button>
</div>
<el-upload
class="uploader"
drag
:accept="accept"
:show-file-list="false"
:http-request="customUpload"
:disabled="uploading"
>
<div class="upload-inner">
<p>拖拽文件到此处或点击选择</p>
<p class="hint">手机可直接打开文件选择器</p>
</div>
</el-upload>
<div v-if="shareUrl" class="share">
<el-input v-model="shareUrl" readonly>
<template #append>
<el-button @click="copyShareUrl">复制链接</el-button>
</template>
</el-input>
</div>
</section>
</template>
<style scoped>
.composer h2 {
margin: 0 0 12px;
font-size: 1.15rem;
}
.controls {
margin-top: 12px;
display: flex;
flex-direction: column;
gap: 12px;
}
.control-row {
display: flex;
flex-direction: column;
gap: 6px;
}
.label {
font-size: 0.9rem;
color: #606266;
}
.switches {
display: flex;
flex-wrap: wrap;
gap: 16px 20px;
}
.actions {
margin: 14px 0;
}
.uploader {
width: 100%;
}
.uploader :deep(.el-upload),
.uploader :deep(.el-upload-dragger) {
width: 100%;
}
.upload-inner {
padding: 8px;
}
.upload-inner p {
margin: 0;
}
.hint {
margin-top: 6px !important;
font-size: 0.85rem;
color: #909399;
}
.share {
margin-top: 14px;
}
</style>