327 lines
6.5 KiB
Svelte
327 lines
6.5 KiB
Svelte
<script lang="ts">
|
|
import { resolve } from '$app/paths';
|
|
import { page } from '$app/stores';
|
|
import type { PageData, ActionData } from './$types';
|
|
import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte';
|
|
import { untrack } from 'svelte';
|
|
|
|
let { data, form }: { data: PageData; form: ActionData } = $props();
|
|
const initialData = $state.snapshot(untrack(() => data));
|
|
|
|
let title = $state(initialData.page.title);
|
|
let slug = $state(initialData.page.slug);
|
|
let content = $state(initialData.page.content);
|
|
let mode = $state<'edit' | 'preview'>('edit');
|
|
</script>
|
|
|
|
<div class="admin-page-header">
|
|
<h2>编辑{$page.data.aliases?.page || '页面'}</h2>
|
|
<div class="header-actions">
|
|
<a
|
|
href={resolve('/[slug]', { slug: data.page.slug })}
|
|
target="_blank"
|
|
class="btn btn-secondary"
|
|
style="margin-right: 12px;">前台预览</a
|
|
>
|
|
<a href={resolve('/admin/pages')} class="btn btn-secondary">← 返回</a>
|
|
</div>
|
|
</div>
|
|
|
|
{#if form?.error}
|
|
<div class="error-alert">
|
|
{form.error}
|
|
</div>
|
|
{/if}
|
|
|
|
<form action="?/update" method="POST" class="post-form">
|
|
<div class="form-grid">
|
|
<div class="form-main">
|
|
<div class="form-group">
|
|
<label for="title">标题 *</label>
|
|
<input
|
|
type="text"
|
|
id="title"
|
|
name="title"
|
|
bind:value={title}
|
|
placeholder="输入标题..."
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<div class="editor-header">
|
|
<label for="content">正文 *</label>
|
|
<div class="editor-tabs">
|
|
<button
|
|
type="button"
|
|
class="tab-btn {mode === 'edit' ? 'active' : ''}"
|
|
onclick={() => (mode = 'edit')}>编辑</button
|
|
>
|
|
<button
|
|
type="button"
|
|
class="tab-btn {mode === 'preview' ? 'active' : ''}"
|
|
onclick={() => (mode = 'preview')}>预览</button
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="editor-body">
|
|
{#if mode === 'edit'}
|
|
<textarea
|
|
id="content"
|
|
name="content"
|
|
bind:value={content}
|
|
rows="16"
|
|
style="min-height: 400px;"
|
|
placeholder="支持 Markdown 格式..."
|
|
required></textarea>
|
|
{:else}
|
|
<div class="preview-area">
|
|
<MarkdownRenderer {content} />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-sidebar">
|
|
<div class="sidebar-box">
|
|
<h3>更新</h3>
|
|
<button type="submit" class="btn btn-primary submit-btn">保存修改</button>
|
|
</div>
|
|
|
|
<div class="sidebar-box">
|
|
<h3>别名 *</h3>
|
|
<input
|
|
type="text"
|
|
id="slug"
|
|
name="slug"
|
|
class="sidebar-input"
|
|
bind:value={slug}
|
|
placeholder="hello-world"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<style>
|
|
.admin-page-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25px;
|
|
border-bottom: 2px solid var(--text-color);
|
|
padding-bottom: 12px;
|
|
}
|
|
|
|
.admin-page-header h2 {
|
|
margin: 0;
|
|
font-size: 2rem;
|
|
color: var(--text-color);
|
|
}
|
|
|
|
.header-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.btn {
|
|
display: inline-block;
|
|
padding: 10px 20px;
|
|
text-decoration: none;
|
|
font-size: 0.95rem;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
border: 1px solid var(--text-color);
|
|
background-color: transparent;
|
|
color: var(--text-color);
|
|
font-family: inherit;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: var(--text-color);
|
|
color: var(--bg-color, #ffffff);
|
|
width: 100%;
|
|
text-align: center;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background-color: var(--accent-color, #419b38);
|
|
border-color: var(--accent-color, #419b38);
|
|
color: white;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
border-color: var(--accent-color, #419b38);
|
|
color: var(--accent-color, #419b38);
|
|
}
|
|
|
|
.error-alert {
|
|
padding: 12px 16px;
|
|
background-color: transparent;
|
|
border-left: 4px solid #d9534f;
|
|
color: #d9534f;
|
|
margin-bottom: 30px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.editor-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-end;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.editor-tabs {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.tab-btn {
|
|
background: none;
|
|
border: none;
|
|
padding: 4px 12px;
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
color: rgba(0, 0, 0, 0.4);
|
|
cursor: pointer;
|
|
border-radius: 4px;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.tab-btn:hover {
|
|
background: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.tab-btn.active {
|
|
color: var(--accent-color, #419b38);
|
|
background: rgba(65, 155, 56, 0.1);
|
|
}
|
|
|
|
.preview-area {
|
|
padding: 20px;
|
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
background: rgba(0, 0, 0, 0.015);
|
|
min-height: 400px;
|
|
max-height: 600px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.form-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 280px;
|
|
gap: 40px;
|
|
align-items: start;
|
|
}
|
|
|
|
.form-main {
|
|
width: 100%;
|
|
}
|
|
|
|
.form-sidebar {
|
|
position: sticky;
|
|
top: 80px;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 30px;
|
|
z-index: 10;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.form-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.form-sidebar {
|
|
position: static;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
gap: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
font-weight: 700;
|
|
margin-bottom: 6px;
|
|
color: var(--text-color);
|
|
font-size: 0.9rem;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.form-group input[type='text'],
|
|
.form-group textarea {
|
|
width: 100%;
|
|
padding: 12px 14px;
|
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
border-radius: 0;
|
|
background: rgba(0, 0, 0, 0.015);
|
|
font-family: inherit;
|
|
font-size: 1.05rem;
|
|
color: var(--text-color);
|
|
box-sizing: border-box;
|
|
transition:
|
|
border-color 0.3s ease,
|
|
box-shadow 0.3s ease;
|
|
}
|
|
|
|
.form-group textarea {
|
|
resize: vertical;
|
|
line-height: 1.8;
|
|
min-height: 120px;
|
|
}
|
|
|
|
.form-group input[type='text']:focus,
|
|
.form-group textarea:focus {
|
|
outline: none;
|
|
border-color: var(--accent-color, #419b38);
|
|
box-shadow: 0 0 0 3px rgba(65, 155, 56, 0.08);
|
|
}
|
|
|
|
.sidebar-input:focus {
|
|
outline: none;
|
|
border-bottom-color: var(--accent-color, #419b38);
|
|
}
|
|
|
|
.sidebar-input {
|
|
width: 100%;
|
|
padding: 10px 0;
|
|
border: none;
|
|
border-bottom: 2px solid rgba(0, 0, 0, 0.1);
|
|
background: transparent;
|
|
font-family: inherit;
|
|
font-size: 1rem;
|
|
color: var(--text-color);
|
|
box-sizing: border-box;
|
|
transition: border-bottom-color 0.3s ease;
|
|
}
|
|
|
|
.sidebar-box {
|
|
background: transparent;
|
|
border: none;
|
|
border-radius: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.sidebar-box h3 {
|
|
margin: 0 0 20px 0;
|
|
font-size: 0.9rem;
|
|
font-weight: 700;
|
|
letter-spacing: 1px;
|
|
text-transform: uppercase;
|
|
color: var(--text-color);
|
|
border-bottom: none;
|
|
}
|
|
</style>
|