refactor: 在文章、页面和设置管理页面中应用通用按钮和表格组件
continuous-integration/drone/push Build is passing

This commit is contained in:
seahi committed 2026-08-15 15:06:32 +08:00
1 parent 32a87ebc61
commit 5f0a5ff820
7 files changed
+66 -445

No files matched your search

+14 -93
View File
@@ -3,6 +3,8 @@
import { page } from '$app/stores';
import type { PageData } from './$types';
import { confirmState } from '$lib/stores/confirm.svelte';
import Button from '$lib/components/admin/Button.svelte';
import DataTable from '$lib/components/admin/DataTable.svelte';
let { data }: { data: PageData } = $props();
let selectedIds = $state<string[]>([]);
@@ -21,9 +23,9 @@
<div class="admin-page-header">
<h2>{$page.data.aliases?.page || '页面'}管理</h2>
<a href={resolve('/admin/pages/new')} class="btn btn-primary"
>新建{$page.data.aliases?.page || '页面'}</a
>
<Button href={resolve('/admin/pages/new')} variant="primary">
新建{$page.data.aliases?.page || '页面'}
</Button>
</div>
{#if data.pages.length > 0}
@@ -54,7 +56,7 @@
{#each selectedIds as id (id)}
<input type="hidden" name="ids" value={id} />
{/each}
<button type="submit" class="btn btn-danger btn-sm">批量删除</button>
<Button type="submit" variant="danger" size="sm">批量删除</Button>
</form>
{:else}
<span class="selected-count empty">未选择项目</span>
@@ -62,13 +64,12 @@
</div>
{/if}
<div class="table-container">
{#if data.pages.length === 0}
{#if data.pages.length === 0}
<div class="empty-state">
<p>还没有发布任何{$page.data.aliases?.page || '页面'}</p>
</div>
{:else}
<table class="data-table">
{:else}
<DataTable>
<thead>
<tr>
<th class="checkbox-col">
@@ -113,13 +114,9 @@
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"
></path><polyline points="15 3 21 3 21 9"></polyline><line
x1="10"
y1="14"
x2="21"
y2="3"
></line></svg
><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline
points="15 3 21 3 21 9"
></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg
>
</a>
</td>
@@ -129,9 +126,8 @@
</tr>
{/each}
</tbody>
</table>
{/if}
</div>
</DataTable>
{/if}
<style>
.admin-page-header {
@@ -149,65 +145,6 @@
color: var(--text-color);
}
.btn {
display: inline-block;
padding: 8px 18px;
text-decoration: none;
font-size: 0.95rem;
font-weight: 600;
transition: all 0.2s;
cursor: pointer;
border: 2px solid var(--text-color);
background-color: transparent;
color: var(--text-color);
font-family: inherit;
}
.btn-primary {
background-color: var(--text-color);
color: var(--bg-color, #ffffff);
}
.btn-primary:hover {
background-color: var(--accent-color, #419b38);
border-color: var(--accent-color, #419b38);
color: white;
}
.table-container {
background: transparent;
border: 2px solid var(--text-color);
overflow-x: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.data-table th,
.data-table td {
padding: 10px 16px;
border-bottom: 1px solid var(--border-color, rgba(0, 0, 0, 0.08));
}
.data-table th {
background-color: transparent;
color: var(--text-color);
font-weight: 700;
font-size: 0.95rem;
border-bottom: 2px solid var(--text-color);
}
.data-table tr:last-child td {
border-bottom: none;
}
.data-table tbody tr:hover {
background-color: rgba(0, 0, 0, 0.02);
}
.preview-link {
text-decoration: none;
margin-left: 6px;
@@ -272,20 +209,4 @@
text-decoration: underline;
text-underline-offset: 4px;
}
.btn-danger {
background-color: transparent;
color: #d9534f;
border-color: #d9534f;
}
.btn-danger:hover {
background-color: #d9534f;
color: white;
}
.btn-sm {
padding: 4px 12px;
font-size: 0.85rem;
}
</style>
+10 -51
View File
@@ -4,6 +4,8 @@
import type { PageData, ActionData } from './$types';
import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
import Alert from '$lib/components/admin/Alert.svelte';
import { untrack } from 'svelte';
let { data, form }: { data: PageData; form: ActionData } = $props();
@@ -18,20 +20,20 @@
<div class="admin-page-header">
<h2>编辑{$page.data.aliases?.page || '页面'}</h2>
<div class="header-actions">
<a
<Button
href={resolve('/[slug]', { slug: data.page.slug })}
target="_blank"
class="btn btn-secondary"
style="margin-right: 12px;">前台预览</a
variant="secondary"
style="margin-right: 12px;"
>
<a href={resolve('/admin/pages')} class="btn btn-secondary">&larr; 返回</a>
前台预览
</Button>
<Button href={resolve('/admin/pages')} variant="secondary">&larr; 返回</Button>
</div>
</div>
{#if form?.error}
<div class="error-alert">
{form.error}
</div>
<Alert message={form.error} />
{/if}
<form action="?/update" method="POST" class="post-form">
@@ -88,7 +90,7 @@
<div class="form-sidebar">
<div class="sidebar-box">
<h3>更新</h3>
<button type="submit" class="btn btn-primary submit-btn">保存修改</button>
<Button type="submit" variant="primary" fullWidth class="submit-btn">保存修改</Button>
</div>
<div class="sidebar-box">
@@ -128,49 +130,6 @@
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;
+5 -48
View File
@@ -4,6 +4,8 @@
import type { ActionData } from './$types';
import MarkdownRenderer from '$lib/components/MarkdownRenderer.svelte';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
import Alert from '$lib/components/admin/Alert.svelte';
import { createSlugAutoHandler } from '$lib/utils/slugify';
@@ -35,13 +37,11 @@
<div class="admin-page-header">
<h2>新建独立{$page.data.aliases?.page || '页面'}</h2>
<a href={resolve('/admin/pages')} class="btn btn-secondary">&larr; 返回</a>
<Button href={resolve('/admin/pages')} variant="secondary">&larr; 返回</Button>
</div>
{#if form?.error}
<div class="error-alert">
{form.error}
</div>
<Alert message={form.error} />
{/if}
<form method="POST" class="post-form">
@@ -99,7 +99,7 @@
<div class="form-sidebar">
<div class="sidebar-box">
<h3>发布</h3>
<button type="submit" class="btn btn-primary submit-btn">发布</button>
<Button type="submit" variant="primary" fullWidth class="submit-btn">发布</Button>
</div>
<div class="sidebar-box">
@@ -145,49 +145,6 @@
color: var(--text-color);
}
.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;
+15 -99
View File
@@ -3,6 +3,8 @@
import { page } from '$app/stores';
import type { PageData } from './$types';
import { confirmState } from '$lib/stores/confirm.svelte';
import Button from '$lib/components/admin/Button.svelte';
import DataTable from '$lib/components/admin/DataTable.svelte';
let { data }: { data: PageData } = $props();
let selectedIds = $state<string[]>([]);
@@ -21,9 +23,9 @@
<div class="admin-page-header">
<h2>{$page.data.aliases?.post || '文章'}管理</h2>
<a href={resolve('/admin/posts/new')} class="btn btn-primary"
>发布新{$page.data.aliases?.post || '文章'}</a
>
<Button href={resolve('/admin/posts/new')} variant="primary">
新建{$page.data.aliases?.post || '文章'}
</Button>
</div>
{#if data.posts.length > 0}
@@ -54,7 +56,7 @@
{#each selectedIds as id (id)}
<input type="hidden" name="ids" value={id} />
{/each}
<button type="submit" class="btn btn-danger btn-sm">批量删除</button>
<Button type="submit" variant="danger" size="sm">批量删除</Button>
</form>
{:else}
<span class="selected-count empty">未选择项目</span>
@@ -62,13 +64,12 @@
</div>
{/if}
<div class="table-container">
{#if data.posts.length === 0}
{#if data.posts.length === 0}
<div class="empty-state">
<p>还没有发布任何{$page.data.aliases?.post || '文章'}</p>
</div>
{:else}
<table class="data-table">
{:else}
<DataTable>
<thead>
<tr>
<th class="checkbox-col">
@@ -87,12 +88,7 @@
{#each data.posts as post (post.id || post)}
<tr>
<td class="checkbox-col">
<input
type="checkbox"
name="ids"
value={post.id.toString()}
bind:group={selectedIds}
/>
<input type="checkbox" name="ids" value={post.id.toString()} bind:group={selectedIds} />
</td>
<td>{post.id}</td>
<td>
@@ -114,13 +110,9 @@
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"
></path><polyline points="15 3 21 3 21 9"></polyline><line
x1="10"
y1="14"
x2="21"
y2="3"
></line></svg
><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline
points="15 3 21 3 21 9"
></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg
>
</a>
</td>
@@ -132,9 +124,8 @@
</tr>
{/each}
</tbody>
</table>
{/if}
</div>
</DataTable>
{/if}
<style>
.admin-page-header {
@@ -152,65 +143,6 @@
color: var(--text-color);
}
.btn {
display: inline-block;
padding: 8px 18px;
text-decoration: none;
font-size: 0.95rem;
font-weight: 600;
transition: all 0.2s;
cursor: pointer;
border: 2px solid var(--text-color);
background-color: transparent;
color: var(--text-color);
font-family: inherit;
}
.btn-primary {
background-color: var(--text-color);
color: var(--bg-color, #ffffff);
}
.btn-primary:hover {
background-color: var(--accent-color, #419b38);
border-color: var(--accent-color, #419b38);
color: white;
}
.table-container {
background: transparent;
border: 2px solid var(--text-color);
overflow-x: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.data-table th,
.data-table td {
padding: 10px 16px;
border-bottom: 1px solid var(--border-color, rgba(0, 0, 0, 0.08));
}
.data-table th {
background-color: transparent;
color: var(--text-color);
font-weight: 700;
font-size: 0.95rem;
border-bottom: 2px solid var(--text-color);
}
.data-table tr:last-child td {
border-bottom: none;
}
.data-table tbody tr:hover {
background-color: rgba(0, 0, 0, 0.02);
}
.preview-link {
text-decoration: none;
margin-left: 6px;
@@ -275,20 +207,4 @@
text-decoration: underline;
text-underline-offset: 4px;
}
.btn-danger {
background-color: transparent;
color: #d9534f;
border-color: #d9534f;
}
.btn-danger:hover {
background-color: #d9534f;
color: white;
}
.btn-sm {
padding: 4px 12px;
font-size: 0.85rem;
}
</style>
+8 -62
View File
@@ -10,6 +10,8 @@
import SelectDropdown from '$lib/components/admin/SelectDropdown.svelte';
import { parseHugoMarkdown } from '$lib/hugoParser';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
import Alert from '$lib/components/admin/Alert.svelte';
import { untrack } from 'svelte';
let { data, form }: { data: PageData; form: ActionData } = $props();
@@ -263,10 +265,10 @@
<div class="admin-page-header">
<h2>编辑</h2>
<div class="header-actions">
<button type="button" class="btn btn-secondary" onclick={() => fileInput?.click()}
>导入 Markdown</button
>
<a href={resolve('/admin/posts')} class="btn btn-secondary">&larr; 返回</a>
<Button type="button" variant="secondary" onclick={() => fileInput?.click()}>
导入 Markdown
</Button>
<Button href={resolve('/admin/posts')} variant="secondary">&larr; 返回</Button>
</div>
</div>
<input
@@ -278,9 +280,7 @@
/>
{#if form?.error}
<div class="error-alert">
{form.error}
</div>
<Alert message={form.error} />
{/if}
<form method="POST" action="?/update" class="post-form">
@@ -488,7 +488,7 @@
<div class="form-sidebar">
<div class="sidebar-box">
<h3>保存</h3>
<button type="submit" class="btn btn-primary submit-btn">保存</button>
<Button type="submit" variant="primary" fullWidth class="submit-btn">保存</Button>
</div>
<FeaturedImagePicker bind:cover adaptiveHeight previewable />
@@ -632,60 +632,6 @@
gap: 10px;
}
.header-actions .btn {
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-height: 42px;
margin: 0;
line-height: 1;
white-space: nowrap;
}
.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;
+10 -64
View File
@@ -10,6 +10,8 @@
import SelectDropdown from '$lib/components/admin/SelectDropdown.svelte';
import { parseHugoMarkdown } from '$lib/hugoParser';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
import Alert from '$lib/components/admin/Alert.svelte';
import { untrack } from 'svelte';
import { createSlugAutoHandler } from '$lib/utils/slugify';
@@ -280,10 +282,10 @@
<div class="admin-page-header">
<h2>新{$page.data.aliases?.post || '文章'}</h2>
<div class="header-actions">
<button type="button" class="btn btn-secondary" onclick={() => fileInput?.click()}
>导入 Markdown</button
>
<a href={resolve('/admin/posts')} class="btn btn-secondary">&larr; 返回</a>
<Button type="button" variant="secondary" onclick={() => fileInput?.click()}>
导入 Markdown
</Button>
<Button href={resolve('/admin/posts')} variant="secondary">&larr; 返回</Button>
</div>
</div>
<input
@@ -295,9 +297,7 @@
/>
{#if form?.error}
<div class="error-alert">
{form.error}
</div>
<Alert message={form.error} />
{/if}
<form method="POST" class="post-form">
@@ -505,9 +505,9 @@
<div class="form-sidebar">
<div class="sidebar-box">
<h3>发布</h3>
<button type="submit" class="btn btn-primary publish-btn"
>发布{$page.data.aliases?.post || '文章'}</button
>
<Button type="submit" variant="primary" fullWidth class="publish-btn">
发布{$page.data.aliases?.post || '文章'}
</Button>
</div>
<FeaturedImagePicker bind:cover adaptiveHeight previewable />
@@ -663,60 +663,6 @@
gap: 10px;
}
.header-actions .btn {
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
min-height: 42px;
margin: 0;
line-height: 1;
white-space: nowrap;
}
.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;
+4 -28
View File
@@ -6,6 +6,7 @@
import ImagePicker from '$lib/components/admin/FeaturedImagePicker.svelte';
import DatePicker from '$lib/components/admin/DatePicker.svelte';
import TextInput from '$lib/components/admin/TextInput.svelte';
import Button from '$lib/components/admin/Button.svelte';
import { untrack } from 'svelte';
let { data, form }: { data: PageData; form: ActionData } = $props();
@@ -98,7 +99,7 @@
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存</button>
<Button type="submit" variant="primary">保存</Button>
</div>
</form>
</div>
@@ -175,7 +176,7 @@
{/if}
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存外观偏好</button>
<Button type="submit" variant="primary">保存外观偏好</Button>
</div>
</form>
</div>
@@ -255,7 +256,7 @@
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存</button>
<Button type="submit" variant="primary">保存</Button>
</div>
</form>
</div>
@@ -374,31 +375,6 @@
justify-content: flex-end;
}
.btn {
display: inline-block;
padding: 10px 24px;
text-decoration: none;
font-size: 0.95rem;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
border: 2px solid var(--text-color);
background-color: transparent;
color: var(--text-color);
font-family: inherit;
}
.btn-primary {
background-color: var(--text-color);
color: var(--bg-color, #ffffff);
}
.btn-primary:hover {
background-color: var(--accent-color, #419b38);
border-color: var(--accent-color, #419b38);
color: white;
}
.toggle-label {
display: flex;
align-items: center;