新增前后端AI聊天模块,包括流式对话、配置管理和UI组件优化。
This commit is contained in:
145
frontend/src/views/NovelFormView.vue
Normal file
145
frontend/src/views/NovelFormView.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { createNovel, fetchNovel, updateNovel } from '@/api/novels'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import BaseInput from '@/components/ui/BaseInput.vue'
|
||||
import BaseTextarea from '@/components/ui/BaseTextarea.vue'
|
||||
import BaseButton from '@/components/ui/BaseButton.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
|
||||
const isEdit = computed(() => route.name === 'novel-edit')
|
||||
const novelId = computed(() => Number(route.params.id))
|
||||
|
||||
const title = ref('')
|
||||
const description = ref('')
|
||||
const saving = ref(false)
|
||||
const errors = ref<{ title?: string }>({})
|
||||
|
||||
async function load() {
|
||||
if (!isEdit.value) return
|
||||
try {
|
||||
const novel = await fetchNovel(novelId.value)
|
||||
title.value = novel.title
|
||||
description.value = novel.description
|
||||
} catch {
|
||||
toast.error('作品不存在')
|
||||
router.push({ name: 'home' })
|
||||
}
|
||||
}
|
||||
|
||||
function validate(): boolean {
|
||||
errors.value = {}
|
||||
if (!title.value.trim()) {
|
||||
errors.value.title = '请输入作品名称'
|
||||
return false
|
||||
}
|
||||
if (title.value.length > 200) {
|
||||
errors.value.title = '名称不能超过200字'
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!validate()) return
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
if (isEdit.value) {
|
||||
await updateNovel(novelId.value, {
|
||||
title: title.value.trim(),
|
||||
description: description.value.trim(),
|
||||
})
|
||||
toast.success('保存成功')
|
||||
router.push({ name: 'novel-detail', params: { id: novelId.value } })
|
||||
} else {
|
||||
const novel = await createNovel({
|
||||
title: title.value.trim(),
|
||||
description: description.value.trim(),
|
||||
})
|
||||
toast.success('创建成功')
|
||||
router.push({ name: 'novel-detail', params: { id: novel.id } })
|
||||
}
|
||||
} catch {
|
||||
toast.error(isEdit.value ? '保存失败' : '创建失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-view">
|
||||
<div class="form-header">
|
||||
<BaseButton variant="ghost" @click="router.back()">
|
||||
← 返回
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<h1 class="form-title">{{ isEdit ? '编辑作品' : '新建作品' }}</h1>
|
||||
|
||||
<form @submit.prevent="handleSubmit" class="novel-form">
|
||||
<BaseInput
|
||||
v-model="title"
|
||||
label="作品名称"
|
||||
:error="errors.title"
|
||||
/>
|
||||
|
||||
<BaseTextarea
|
||||
v-model="description"
|
||||
label="作品简介"
|
||||
/>
|
||||
|
||||
<div class="form-actions">
|
||||
<BaseButton variant="ghost" type="button" @click="router.back()">
|
||||
取消
|
||||
</BaseButton>
|
||||
<BaseButton variant="primary" type="submit" :loading="saving">
|
||||
{{ isEdit ? '保存修改' : '开始创作' }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.form-view {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
animation: fadeInUp var(--duration-normal) var(--ease-out-smooth) both;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
margin-bottom: var(--space-lg);
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background: var(--paper-50);
|
||||
border: 1px solid var(--paper-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-2xl);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.6rem;
|
||||
color: var(--ink-900);
|
||||
margin-bottom: var(--space-2xl);
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space-md);
|
||||
margin-top: var(--space-xl);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user