feat: add template upload UI flow
This commit is contained in:
@@ -3,9 +3,6 @@ import axios from "axios";
|
||||
export const apiClient = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000/api",
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
apiClient.interceptors.response.use(
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { apiClient } from "./base";
|
||||
import type { BlockConfig } from "@/stores/templateStore";
|
||||
|
||||
export type UploadTemplateResponse = {
|
||||
template_id: number;
|
||||
name: string;
|
||||
block_count: number;
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type TemplateDetailResponse = {
|
||||
template: {
|
||||
id: number;
|
||||
@@ -30,6 +37,19 @@ export async function getTemplateDetail(templateId: number) {
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function uploadTemplate(file: File, name: string, type: string) {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("name", name);
|
||||
formData.append("type", type);
|
||||
|
||||
const response = await apiClient.post<{ data: UploadTemplateResponse; message: string }>(
|
||||
"/templates/upload",
|
||||
formData,
|
||||
);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function saveTemplateBlockConfig(
|
||||
templateId: number,
|
||||
blockId: string,
|
||||
|
||||
@@ -1,11 +1,63 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { FileTextOutlined, PlusOutlined } from "@ant-design/icons-vue";
|
||||
import { message } from "ant-design-vue";
|
||||
|
||||
import { uploadTemplate } from "@/api/templates";
|
||||
import { useAppStore } from "@/stores/appStore";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
const statusClass = computed(() => (appStore.saveStatus === "已保存" ? "saved" : "dirty"));
|
||||
const uploadOpen = ref(false);
|
||||
const uploading = ref(false);
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null);
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const form = ref({
|
||||
name: "",
|
||||
type: "report",
|
||||
});
|
||||
|
||||
function openUploadModal() {
|
||||
uploadOpen.value = true;
|
||||
}
|
||||
|
||||
function chooseFile() {
|
||||
fileInputRef.value?.click();
|
||||
}
|
||||
|
||||
function handleFileChange(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0] ?? null;
|
||||
selectedFile.value = file;
|
||||
if (file && !form.value.name) {
|
||||
form.value.name = file.name.replace(/\.docx$/i, "");
|
||||
}
|
||||
}
|
||||
|
||||
async function submitUpload() {
|
||||
if (!selectedFile.value) {
|
||||
message.warning("请先选择 .docx 文件");
|
||||
return;
|
||||
}
|
||||
if (!form.value.name.trim()) {
|
||||
message.warning("请输入模板名称");
|
||||
return;
|
||||
}
|
||||
|
||||
uploading.value = true;
|
||||
try {
|
||||
const result = await uploadTemplate(selectedFile.value, form.value.name.trim(), form.value.type);
|
||||
message.success(`模板上传成功,解析 ${result.block_count} 个区域`);
|
||||
uploadOpen.value = false;
|
||||
await router.push(`/templates/${result.template_id}/edit`);
|
||||
} catch {
|
||||
message.error("模板上传失败,请确认后端服务和数据库已启动");
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -31,7 +83,7 @@ const statusClass = computed(() => (appStore.saveStatus === "已保存" ? "saved
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<span class="save-status" :class="statusClass">{{ appStore.saveStatus }}</span>
|
||||
<a-button type="primary">
|
||||
<a-button type="primary" @click="openUploadModal">
|
||||
<template #icon><PlusOutlined /></template>
|
||||
上传模板
|
||||
</a-button>
|
||||
@@ -46,6 +98,44 @@ const statusClass = computed(() => (appStore.saveStatus === "已保存" ? "saved
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<a-modal
|
||||
v-model:open="uploadOpen"
|
||||
title="上传模板"
|
||||
:confirm-loading="uploading"
|
||||
ok-text="上传"
|
||||
cancel-text="取消"
|
||||
@ok="submitUpload"
|
||||
>
|
||||
<div class="upload-form">
|
||||
<label>
|
||||
模板名称
|
||||
<a-input v-model:value="form.name" placeholder="请输入模板名称" />
|
||||
</label>
|
||||
<label>
|
||||
模板类型
|
||||
<a-select v-model:value="form.type">
|
||||
<a-select-option value="report">报告类</a-select-option>
|
||||
<a-select-option value="official">公文类</a-select-option>
|
||||
<a-select-option value="summary">总结类</a-select-option>
|
||||
<a-select-option value="contract">合同类</a-select-option>
|
||||
</a-select>
|
||||
</label>
|
||||
<div class="file-picker">
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
class="hidden-input"
|
||||
type="file"
|
||||
accept=".docx"
|
||||
@change="handleFileChange"
|
||||
/>
|
||||
<button type="button" @click="chooseFile">
|
||||
<FileTextOutlined />
|
||||
{{ selectedFile ? selectedFile.name : "选择 .docx 文件" }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -211,4 +301,40 @@ h1 {
|
||||
margin: 0;
|
||||
color: var(--c-text-muted);
|
||||
}
|
||||
|
||||
.upload-form {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.upload-form label {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
color: var(--c-text-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.hidden-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file-picker button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 88px;
|
||||
gap: 8px;
|
||||
border: 1px dashed var(--c-border);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--c-text-secondary);
|
||||
background: var(--c-surface);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-picker button:hover {
|
||||
border-color: var(--c-primary);
|
||||
color: var(--c-primary);
|
||||
background: var(--c-primary-soft);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user