1
This commit is contained in:
@@ -1,95 +1,97 @@
|
||||
<template>
|
||||
<div class="post-form-container">
|
||||
<h1 class="page-title">{{ isEditing ? '编辑文章' : '新建文章' }}</h1>
|
||||
|
||||
|
||||
<div class="form-container">
|
||||
<form @submit.prevent="handleSubmit" class="post-form">
|
||||
<!-- Title Field -->
|
||||
<div class="form-group">
|
||||
<label for="title">文章标题</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
v-model="form.title"
|
||||
placeholder="请输入文章标题"
|
||||
required
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
v-model="form.title"
|
||||
placeholder="请输入文章标题"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" v-if="errors.title">
|
||||
{{ errors.title }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Category Field -->
|
||||
<div class="form-group">
|
||||
<label for="category">分类</label>
|
||||
<input
|
||||
type="text"
|
||||
id="category"
|
||||
v-model="form.category"
|
||||
placeholder="请输入文章分类"
|
||||
required
|
||||
<input
|
||||
type="text"
|
||||
id="category"
|
||||
v-model="form.category"
|
||||
placeholder="请输入文章分类"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" v-if="errors.category">
|
||||
{{ errors.category }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Date Field -->
|
||||
<div class="form-group">
|
||||
<label for="date">发布日期</label>
|
||||
<input
|
||||
type="date"
|
||||
id="date"
|
||||
v-model="form.date"
|
||||
required
|
||||
<input
|
||||
type="date"
|
||||
id="date"
|
||||
v-model="form.date"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" v-if="errors.date">
|
||||
{{ errors.date }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Excerpt Field -->
|
||||
<div class="form-group">
|
||||
<label for="excerpt">文章摘要</label>
|
||||
<textarea
|
||||
id="excerpt"
|
||||
v-model="form.excerpt"
|
||||
placeholder="请输入文章摘要"
|
||||
rows="3"
|
||||
<textarea
|
||||
id="excerpt"
|
||||
v-model="form.excerpt"
|
||||
placeholder="请输入文章摘要"
|
||||
rows="3"
|
||||
></textarea>
|
||||
<div class="error-message" v-if="errors.excerpt">
|
||||
{{ errors.excerpt }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Field -->
|
||||
|
||||
<!-- Content Field - 使用 Markdown 编辑器 -->
|
||||
<div class="form-group">
|
||||
<label for="content">文章内容</label>
|
||||
<textarea
|
||||
id="content"
|
||||
v-model="form.content"
|
||||
placeholder="请输入文章内容"
|
||||
rows="10"
|
||||
required
|
||||
></textarea>
|
||||
<label>文章内容</label>
|
||||
<div class="editor-wrapper">
|
||||
<MdEditor
|
||||
v-model="form.content"
|
||||
theme="dark"
|
||||
:preview="false"
|
||||
placeholder="请输入 Markdown 内容..."
|
||||
class="custom-md-editor"
|
||||
/>
|
||||
</div>
|
||||
<div class="error-message" v-if="errors.content">
|
||||
{{ errors.content }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Is Published Field -->
|
||||
<div class="form-group">
|
||||
<label for="isPublished">发布状态</label>
|
||||
<CustomSelect
|
||||
v-model="form.isPublished"
|
||||
:options="publishStatusOptions"
|
||||
placeholder="请选择发布状态"
|
||||
v-model="form.isPublished"
|
||||
:options="publishStatusOptions"
|
||||
placeholder="请选择发布状态"
|
||||
/>
|
||||
<div class="error-message" v-if="errors.isPublished">
|
||||
{{ errors.isPublished }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Submit Buttons -->
|
||||
<div class="form-actions">
|
||||
<button type="button" class="cancel-btn" @click="handleCancel">
|
||||
@@ -107,6 +109,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
// 引入 md-editor-v3
|
||||
import { MdEditor } from 'md-editor-v3'
|
||||
import 'md-editor-v3/lib/style.css'
|
||||
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { createPost, updatePost, fetchPost } from '../../services/api'
|
||||
import CustomSelect from '../../components/CustomSelect.vue'
|
||||
@@ -140,33 +146,33 @@ const publishStatusOptions = [
|
||||
const validateForm = (): boolean => {
|
||||
// Reset errors
|
||||
Object.keys(errors).forEach(key => delete errors[key])
|
||||
|
||||
|
||||
let isValid = true
|
||||
|
||||
|
||||
// Validate title
|
||||
if (!form.title.trim()) {
|
||||
errors.title = '文章标题不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
// Validate category
|
||||
if (!form.category.trim()) {
|
||||
errors.category = '文章分类不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
// Validate date
|
||||
if (!form.date) {
|
||||
errors.date = '发布日期不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
// Validate content
|
||||
if (!form.content.trim()) {
|
||||
errors.content = '文章内容不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
@@ -175,9 +181,9 @@ const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
isSubmitting.value = true
|
||||
|
||||
|
||||
try {
|
||||
if (isEditing.value) {
|
||||
// Update existing post
|
||||
@@ -188,7 +194,7 @@ const handleSubmit = async () => {
|
||||
await createPost(form)
|
||||
toast.success('文章创建成功')
|
||||
}
|
||||
|
||||
|
||||
// Redirect to posts list
|
||||
router.push('/admin/posts')
|
||||
} catch (error: any) {
|
||||
@@ -211,7 +217,7 @@ onMounted(async () => {
|
||||
try {
|
||||
const postId = route.params.id as string
|
||||
const post = await fetchPost(postId)
|
||||
|
||||
|
||||
// Populate form with post data
|
||||
form.title = post.title
|
||||
form.category = post.category
|
||||
@@ -350,19 +356,54 @@ onMounted(async () => {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* MdEditor Customization */
|
||||
.editor-wrapper {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.editor-wrapper:focus-within {
|
||||
border-color: #d4b383;
|
||||
box-shadow: 0 0 0 3px rgba(212, 179, 131, 0.2);
|
||||
}
|
||||
|
||||
:deep(.custom-md-editor) {
|
||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
||||
--md-bk-color: transparent !important;
|
||||
--md-color: white !important;
|
||||
--md-border-color: transparent !important;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
:deep(.md-editor-toolbar-wrapper) {
|
||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
:deep(.md-editor-content) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.md-editor-footer) {
|
||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.form-container {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.cancel-btn,
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,68 +1,70 @@
|
||||
<template>
|
||||
<div class="snippet-form-container">
|
||||
<h1 class="page-title">{{ isEditing ? '编辑代码片段' : '新建代码片段' }}</h1>
|
||||
|
||||
|
||||
<div class="form-container">
|
||||
<form @submit.prevent="handleSubmit" class="snippet-form">
|
||||
<!-- Title Field -->
|
||||
<div class="form-group">
|
||||
<label for="title">标题</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
v-model="form.title"
|
||||
placeholder="请输入代码片段标题"
|
||||
required
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
v-model="form.title"
|
||||
placeholder="请输入代码片段标题"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" v-if="errors.title">
|
||||
{{ errors.title }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Type Field -->
|
||||
<div class="form-group">
|
||||
<label for="type">类型</label>
|
||||
<input
|
||||
type="text"
|
||||
id="type"
|
||||
v-model="form.type"
|
||||
placeholder="请输入代码类型(如:js、css、html等)"
|
||||
required
|
||||
<input
|
||||
type="text"
|
||||
id="type"
|
||||
v-model="form.type"
|
||||
placeholder="请输入代码类型(如:js、css、html等)"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" v-if="errors.type">
|
||||
{{ errors.type }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code Field -->
|
||||
|
||||
<!-- Code Field - Updated to MdEditor for better editing experience -->
|
||||
<div class="form-group">
|
||||
<label for="code">代码内容</label>
|
||||
<textarea
|
||||
id="code"
|
||||
v-model="form.code"
|
||||
placeholder="请输入代码内容"
|
||||
rows="10"
|
||||
required
|
||||
></textarea>
|
||||
<label>代码内容</label>
|
||||
<div class="editor-wrapper">
|
||||
<MdEditor
|
||||
v-model="form.code"
|
||||
theme="dark"
|
||||
:preview="false"
|
||||
placeholder="请输入代码..."
|
||||
class="custom-md-editor"
|
||||
/>
|
||||
</div>
|
||||
<div class="error-message" v-if="errors.code">
|
||||
{{ errors.code }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Description Field -->
|
||||
<div class="form-group">
|
||||
<label for="description">描述</label>
|
||||
<textarea
|
||||
id="description"
|
||||
v-model="form.description"
|
||||
placeholder="请输入代码片段描述"
|
||||
rows="3"
|
||||
<textarea
|
||||
id="description"
|
||||
v-model="form.description"
|
||||
placeholder="请输入代码片段描述"
|
||||
rows="3"
|
||||
></textarea>
|
||||
<div class="error-message" v-if="errors.description">
|
||||
{{ errors.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Submit Buttons -->
|
||||
<div class="form-actions">
|
||||
<button type="button" class="cancel-btn" @click="handleCancel">
|
||||
@@ -80,6 +82,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
// Import MdEditor
|
||||
import { MdEditor } from 'md-editor-v3'
|
||||
import 'md-editor-v3/lib/style.css'
|
||||
|
||||
import { useToast } from '../../composables/useToast'
|
||||
import { createSnippet, updateSnippet, fetchSnippet } from '../../services/api'
|
||||
|
||||
@@ -104,27 +110,27 @@ const form = reactive({
|
||||
const validateForm = (): boolean => {
|
||||
// Reset errors
|
||||
Object.keys(errors).forEach(key => delete errors[key])
|
||||
|
||||
|
||||
let isValid = true
|
||||
|
||||
|
||||
// Validate title
|
||||
if (!form.title.trim()) {
|
||||
errors.title = '代码片段标题不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
// Validate type
|
||||
if (!form.type.trim()) {
|
||||
errors.type = '代码类型不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
// Validate code
|
||||
if (!form.code.trim()) {
|
||||
errors.code = '代码内容不能为空'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
@@ -133,9 +139,9 @@ const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
isSubmitting.value = true
|
||||
|
||||
|
||||
try {
|
||||
if (isEditing.value) {
|
||||
// Update existing snippet
|
||||
@@ -146,7 +152,7 @@ const handleSubmit = async () => {
|
||||
await createSnippet(form)
|
||||
toast.success('代码片段创建成功')
|
||||
}
|
||||
|
||||
|
||||
// Redirect to snippets list
|
||||
router.push('/admin/snippets')
|
||||
} catch (error: any) {
|
||||
@@ -304,19 +310,54 @@ onMounted(async () => {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* MdEditor Customization (Consistent with PostForm) */
|
||||
.editor-wrapper {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.editor-wrapper:focus-within {
|
||||
border-color: #d4b383;
|
||||
box-shadow: 0 0 0 3px rgba(212, 179, 131, 0.2);
|
||||
}
|
||||
|
||||
:deep(.custom-md-editor) {
|
||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
||||
--md-bk-color: transparent !important;
|
||||
--md-color: white !important;
|
||||
--md-border-color: transparent !important;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
:deep(.md-editor-toolbar-wrapper) {
|
||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
:deep(.md-editor-content) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
:deep(.md-editor-footer) {
|
||||
background-color: rgba(0, 0, 0, 0.2) !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.form-container {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.cancel-btn,
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user