数据结构优化
This commit is contained in:
@@ -96,7 +96,16 @@
|
||||
|
||||
<!-- Category Field -->
|
||||
<div class="space-y-2">
|
||||
<label for="category" class="block text-xs font-medium text-art-muted uppercase tracking-wider">分类</label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="category" class="block text-xs font-medium text-art-muted uppercase tracking-wider">分类</label>
|
||||
<button
|
||||
type="button"
|
||||
@click="showCreateCategory = true"
|
||||
class="text-xs text-art-accent hover:text-white transition-colors"
|
||||
>
|
||||
+ 新建分类
|
||||
</button>
|
||||
</div>
|
||||
<CustomSelect
|
||||
v-model="form.categoryId"
|
||||
:options="categories"
|
||||
@@ -105,21 +114,79 @@
|
||||
<div class="text-art-error text-xs mt-1" v-if="errors.categoryId">
|
||||
{{ errors.categoryId }}
|
||||
</div>
|
||||
|
||||
<!-- Quick Create Category -->
|
||||
<div v-if="showCreateCategory" class="admin-card p-3 space-y-2 mt-2">
|
||||
<input
|
||||
v-model="newCategory.name"
|
||||
placeholder="分类名称"
|
||||
class="admin-input text-sm"
|
||||
@keyup.enter="createCategory"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" @click="createCategory" class="admin-btn-primary text-xs px-3 py-1">创建</button>
|
||||
<button type="button" @click="showCreateCategory = false" class="admin-btn-secondary text-xs px-3 py-1">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Column Field -->
|
||||
<div class="space-y-2">
|
||||
<label for="column" class="block text-xs font-medium text-art-muted uppercase tracking-wider">专栏 <span class="text-art-muted text-xs">(可选)</span></label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="column" class="block text-xs font-medium text-art-muted uppercase tracking-wider">专栏 <span class="text-art-muted text-xs">(可选)</span></label>
|
||||
<button
|
||||
type="button"
|
||||
@click="showCreateColumn = true"
|
||||
class="text-xs text-art-accent hover:text-white transition-colors"
|
||||
>
|
||||
+ 新建专栏
|
||||
</button>
|
||||
</div>
|
||||
<CustomSelect
|
||||
v-model="form.columnId"
|
||||
:options="columns"
|
||||
placeholder="选择专栏(可选)"
|
||||
/>
|
||||
|
||||
<!-- Quick Create Column -->
|
||||
<div v-if="showCreateColumn" class="admin-card p-3 space-y-2 mt-2">
|
||||
<input
|
||||
v-model="newColumn.name"
|
||||
placeholder="专栏名称"
|
||||
class="admin-input text-sm"
|
||||
/>
|
||||
<div>
|
||||
<label class="block text-xs text-art-muted mb-2">封面图片</label>
|
||||
<ImageUpload
|
||||
v-model="newColumn.cover"
|
||||
:categoryId="1"
|
||||
/>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="newColumn.description"
|
||||
placeholder="描述"
|
||||
rows="2"
|
||||
class="admin-input text-sm"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" @click="createColumn" class="admin-btn-primary text-xs px-3 py-1">创建</button>
|
||||
<button type="button" @click="showCreateColumn = false" class="admin-btn-secondary text-xs px-3 py-1">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags Field -->
|
||||
<div class="space-y-2">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">标签</label>
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">标签</label>
|
||||
<button
|
||||
type="button"
|
||||
@click="showCreateTag = true"
|
||||
class="text-xs text-art-accent hover:text-white transition-colors"
|
||||
>
|
||||
+ 新建标签
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
@@ -135,6 +202,20 @@
|
||||
暂无可用标签
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Create Tag -->
|
||||
<div v-if="showCreateTag" class="admin-card p-3 space-y-2 mt-2">
|
||||
<input
|
||||
v-model="newTag.name"
|
||||
placeholder="标签名称"
|
||||
class="admin-input text-sm"
|
||||
@keyup.enter="createTag"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<button type="button" @click="createTag" class="admin-btn-primary text-xs px-3 py-1">创建</button>
|
||||
<button type="button" @click="showCreateTag = false" class="admin-btn-secondary text-xs px-3 py-1">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date Field -->
|
||||
@@ -187,9 +268,21 @@ import { MdEditor } from 'md-editor-v3'
|
||||
import 'md-editor-v3/lib/style.css'
|
||||
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { createPost, updatePost, fetchPost, fetchCategories, fetchColumns, fetchTags, Tag } from '../../services/api'
|
||||
import {
|
||||
createPost,
|
||||
updatePost,
|
||||
fetchPost,
|
||||
fetchCategories,
|
||||
fetchColumns,
|
||||
fetchTags,
|
||||
createCategory as createCategoryApi,
|
||||
createColumn as createColumnApi,
|
||||
createTag as createTagApi,
|
||||
Tag
|
||||
} from '../../services/api'
|
||||
import CustomSelect from '../../components/CustomSelect.vue'
|
||||
import AccessLogModal from '../../components/admin/AccessLogModal.vue'
|
||||
import ImageUpload from '../../components/admin/ImageUpload.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@@ -217,6 +310,15 @@ const categories = ref<{value: number, label: string}[]>([])
|
||||
const columns = ref<{value: number, label: string}[]>([])
|
||||
const availableTags = ref<Tag[]>([])
|
||||
|
||||
// Create form states
|
||||
const showCreateCategory = ref(false)
|
||||
const showCreateColumn = ref(false)
|
||||
const showCreateTag = ref(false)
|
||||
|
||||
const newCategory = ref({ name: '', slug: '', description: '' })
|
||||
const newColumn = ref({ name: '', cover: '', description: '', isActive: 1, sortOrder: 0 })
|
||||
const newTag = ref({ name: '', slug: '' })
|
||||
|
||||
// Form data
|
||||
const form = reactive({
|
||||
title: '',
|
||||
@@ -283,6 +385,86 @@ const toggleTag = (tagId: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Create category
|
||||
const createCategory = async () => {
|
||||
if (!newCategory.value.name) {
|
||||
toast.showToast('请输入分类名称', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await createCategoryApi({
|
||||
name: newCategory.value.name,
|
||||
slug: newCategory.value.slug || newCategory.value.name,
|
||||
description: newCategory.value.description || '',
|
||||
sortOrder: 0
|
||||
})
|
||||
toast.showToast('分类创建成功', 'success')
|
||||
const createdName = newCategory.value.name
|
||||
newCategory.value = { name: '', slug: '', description: '' }
|
||||
showCreateCategory.value = false
|
||||
await loadData()
|
||||
// 自动选择新创建的分类
|
||||
const newCat = categories.value.find(c => c.label === createdName)
|
||||
if (newCat) {
|
||||
form.categoryId = newCat.value
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.showToast(err.message || '创建失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// Create column
|
||||
const createColumn = async () => {
|
||||
if (!newColumn.value.name) {
|
||||
toast.showToast('请输入专栏名称', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await createColumnApi(newColumn.value)
|
||||
toast.showToast('专栏创建成功', 'success')
|
||||
const createdName = newColumn.value.name
|
||||
newColumn.value = { name: '', cover: '', description: '', isActive: 1, sortOrder: 0 }
|
||||
showCreateColumn.value = false
|
||||
await loadData()
|
||||
// 自动选择新创建的专栏
|
||||
const newCol = columns.value.find(c => c.label === createdName)
|
||||
if (newCol) {
|
||||
form.columnId = newCol.value
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.showToast(err.message || '创建失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// Create tag
|
||||
const createTag = async () => {
|
||||
if (!newTag.value.name) {
|
||||
toast.showToast('请输入标签名称', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await createTagApi({
|
||||
name: newTag.value.name,
|
||||
slug: newTag.value.slug || newTag.value.name
|
||||
})
|
||||
toast.showToast('标签创建成功', 'success')
|
||||
const createdName = newTag.value.name
|
||||
newTag.value = { name: '', slug: '' }
|
||||
showCreateTag.value = false
|
||||
await loadData()
|
||||
// 自动选择新创建的标签
|
||||
const newTagItem = availableTags.value.find(t => t.name === createdName)
|
||||
if (newTagItem && !form.tagIds.includes(newTagItem.id)) {
|
||||
form.tagIds.push(newTagItem.id)
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.showToast(err.message || '创建失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// Submit handler
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
|
||||
Reference in New Issue
Block a user