146 lines
3.5 KiB
Vue
146 lines
3.5 KiB
Vue
<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>
|