优化页面、修复BUG

This commit is contained in:
李琦
2026-06-25 16:57:06 +08:00
parent d5ff40e4d3
commit 20fb0e3203
31 changed files with 2032 additions and 754 deletions

View File

@@ -2,7 +2,6 @@
<div class="inquiries-container">
<h1 class="page-title">合作咨询管理</h1>
<!-- Inquiries Table -->
<div class="table-container">
<table class="admin-table">
<thead>
@@ -41,15 +40,22 @@
{{ getStatusLabel(item.status) }}
</span>
</td>
<td class="table-cell">{{ item.createdAt ? new Date(item.createdAt).toLocaleString() : '-' }}</td>
<td class="table-cell">{{ formatCreatedAt(item.createdAt) }}</td>
<td class="table-cell actions">
<button
class="btn-action btn-view"
@click="openDetail(item)"
title="查看详情"
>
查看
</button>
<button
v-if="item.status === 0"
class="btn-action btn-read"
@click="updateStatus(item.id!, 1)"
title="标记为已读"
>
👁
已读
</button>
<button
v-if="item.status !== 2"
@@ -57,18 +63,23 @@
@click="updateStatus(item.id!, 2)"
title="标记为已联系"
>
已联系
</button>
</td>
</tr>
</tbody>
</table>
<!-- Empty State -->
<div v-if="inquiries.length === 0" class="empty-state">
<p>暂无咨询数据</p>
</div>
</div>
<InquiryDetailModal
v-model:is-open="isDetailModalOpen"
:inquiry="selectedInquiry"
@update-status="handleDetailStatusUpdate"
/>
</div>
</template>
@@ -76,9 +87,12 @@
import { ref, onMounted } from 'vue'
import { API_BASE, authFetchJson, fetchInquiries, getAuthHeaders, type Inquiry } from '../../services/api'
import { useToast } from '../../composables/useToast'
import InquiryDetailModal from '../../components/admin/InquiryDetailModal.vue'
const toast = useToast()
const inquiries = ref<Inquiry[]>([])
const isDetailModalOpen = ref(false)
const selectedInquiry = ref<Inquiry | null>(null)
const fetchInquiriesList = async () => {
try {
@@ -89,6 +103,11 @@ const fetchInquiriesList = async () => {
}
}
const openDetail = (item: Inquiry) => {
selectedInquiry.value = item
isDetailModalOpen.value = true
}
const updateStatus = async (id: number, status: number) => {
try {
await authFetchJson(`${API_BASE}/admin/inquiries/${id}/status`, {
@@ -97,13 +116,32 @@ const updateStatus = async (id: number, status: number) => {
body: JSON.stringify({ status })
})
toast.success('状态更新成功')
fetchInquiriesList()
await fetchInquiriesList()
if (selectedInquiry.value?.id === id) {
selectedInquiry.value = inquiries.value.find(item => item.id === id) || selectedInquiry.value
if (selectedInquiry.value) {
selectedInquiry.value.status = status
}
}
} catch (error: any) {
console.error(error)
toast.error(error.message || '更新失败')
}
}
const handleDetailStatusUpdate = async (status: number) => {
if (!selectedInquiry.value?.id) return
await updateStatus(selectedInquiry.value.id, status)
}
const formatCreatedAt = (createdAt?: string | number) => {
if (createdAt === undefined || createdAt === null || createdAt === '') return '-'
const timestamp = typeof createdAt === 'number' ? createdAt : Number(createdAt)
if (Number.isNaN(timestamp)) return '-'
const ms = timestamp > 1e12 ? timestamp : timestamp * 1000
return new Date(ms).toLocaleString()
}
const getContactMethodLabel = (method: string) => {
const map: Record<string, string> = {
'wechat': '微信',
@@ -198,17 +236,26 @@ onMounted(() => {
.actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.btn-action {
padding: 0.5rem;
padding: 0.35rem 0.65rem;
border: none;
border-radius: 0.375rem;
cursor: pointer;
font-size: 1rem;
font-size: 0.75rem;
transition: all 0.3s;
}
.btn-view {
background: rgba(212, 179, 131, 0.12);
color: rgba(212, 179, 131, 0.95);
}
.btn-view:hover {
background: rgba(212, 179, 131, 0.22);
}
.btn-read {
background: rgba(59, 130, 246, 0.1);
color: rgba(59, 130, 246, 0.8);
@@ -230,4 +277,4 @@ onMounted(() => {
text-align: center;
color: rgba(255, 255, 255, 0.5);
}
</style>
</style>

View File

@@ -60,25 +60,54 @@
<tr v-for="post in posts" :key="post.id" class="group transition-colors duration-200">
<td class="font-mono text-xs text-white/40">#{{ post.id }}</td>
<td class="font-medium text-white group-hover:text-art-accent transition-colors">{{ post.title }}</td>
<td><span class="px-2 py-1 rounded bg-white/5 border border-white/5 text-xs text-art-muted">{{ post.categoryName || post.category?.name || '未分类' }}</span></td>
<td>
<span v-if="post.columnName" class="px-2 py-1 rounded bg-art-accent/10 border border-art-accent/20 text-xs text-art-accent">
<button
type="button"
class="px-2 py-1 rounded bg-white/5 border border-white/5 text-xs text-art-muted cursor-pointer hover:border-art-accent/40 hover:text-white transition-colors"
@click="openRelationModal(post, 'category')"
>
{{ post.categoryName || post.category?.name || '未分类' }}
</button>
</td>
<td>
<button
v-if="post.columnName"
type="button"
class="px-2 py-1 rounded bg-art-accent/10 border border-art-accent/20 text-xs text-art-accent cursor-pointer hover:border-art-accent/40 transition-colors"
@click="openRelationModal(post, 'column')"
>
{{ post.columnName }}
</span>
<span v-else class="text-art-muted text-xs">-</span>
</button>
<button
v-else
type="button"
class="px-2 py-0.5 rounded border border-dashed border-white/20 text-xs text-art-muted/60 cursor-pointer hover:border-art-accent/40 hover:text-art-muted transition-colors"
@click="openRelationModal(post, 'column')"
>
无专栏
</button>
</td>
<td>
<div v-if="post.tags && post.tags.length > 0" class="flex flex-wrap gap-1">
<span
v-for="tag in post.tags.slice(0, 3)"
:key="tag.id"
class="px-2 py-0.5 rounded bg-white/5 border border-white/5 text-xs text-art-muted"
<button
v-for="tag in post.tags.slice(0, 3)"
:key="tag.id"
type="button"
class="px-2 py-0.5 rounded bg-white/5 border border-white/5 text-xs text-art-muted cursor-pointer hover:border-art-accent/40 hover:text-white transition-colors"
@click="openRelationModal(post, 'tags')"
>
#{{ tag.name }}
</span>
<span v-if="post.tags.length > 3" class="text-art-muted text-xs">+{{ post.tags.length - 3 }}</span>
</button>
<span v-if="post.tags.length > 3" class="text-art-muted text-xs self-center">+{{ post.tags.length - 3 }}</span>
</div>
<span v-else class="text-art-muted text-xs">-</span>
<button
v-else
type="button"
class="px-2 py-0.5 rounded border border-dashed border-white/20 text-xs text-art-muted/60 cursor-pointer hover:border-art-accent/40 hover:text-art-muted transition-colors"
@click="openRelationModal(post, 'tags')"
>
无标签
</button>
</td>
<td class="text-art-muted text-xs">{{ post.date }}</td>
<td>
@@ -93,17 +122,20 @@
<td>
<button
@click="openAccessLogModal(post)"
class="admin-btn-secondary py-1 px-3 text-xs opacity-0 group-hover:opacity-100 transition-opacity duration-200"
class="admin-btn-secondary py-1 px-3 text-xs"
title="查看访问记录"
>
查看
</button>
</td>
<td class="text-right">
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<div class="flex items-center justify-end gap-2 flex-wrap">
<button @click="openRelationModal(post)" class="admin-btn-secondary py-1 px-3 text-xs">
管理
</button>
<button @click="openHistoryModal(post)" class="admin-btn-secondary py-1 px-3 text-xs">
历史
</button>
<router-link :to="`/admin/posts/${post.id}/edit`" class="admin-btn-secondary py-1 px-3 text-xs">
编辑
</router-link>
@@ -169,9 +201,17 @@
<PostRelationModal
v-model:isOpen="isRelationModalOpen"
:post="editingPost"
:focus-section="relationFocusSection"
@saved="fetchPosts"
/>
<PostHistoryModal
:is-open="isHistoryModalOpen"
:post-id="historyPostId"
@close="isHistoryModalOpen = false"
@restored="fetchPosts"
/>
<!-- Access Log Modal -->
<AccessLogModal
v-model:isOpen="isAccessLogModalOpen"
@@ -186,12 +226,18 @@ import { ref, onMounted, computed } from 'vue'
import { getAdminPosts, deletePost as deletePostApi, Post, togglePostStatus } from '../../services/api'
import { useToast } from '../../composables/useToast'
import PostRelationModal from '../../components/admin/PostRelationModal.vue'
import PostHistoryModal from '../../components/admin/PostHistoryModal.vue'
import AccessLogModal from '../../components/admin/AccessLogModal.vue'
type RelationFocusSection = 'category' | 'column' | 'tags' | 'all'
const toast = useToast()
const posts = ref<Post[]>([])
const isRelationModalOpen = ref(false)
const editingPost = ref<Post | null>(null)
const relationFocusSection = ref<RelationFocusSection>('all')
const isHistoryModalOpen = ref(false)
const historyPostId = ref<number | string>('')
const isAccessLogModalOpen = ref(false)
const selectedPostId = ref<number | null>(null)
const selectedPostTitle = ref<string>('')
@@ -226,11 +272,17 @@ const visiblePages = computed(() => {
return pages
})
const openRelationModal = (post: Post) => {
const openRelationModal = (post: Post, section: RelationFocusSection = 'all') => {
editingPost.value = post
relationFocusSection.value = section
isRelationModalOpen.value = true
}
const openHistoryModal = (post: Post) => {
historyPostId.value = post.id
isHistoryModalOpen.value = true
}
const openAccessLogModal = (post: Post) => {
selectedPostId.value = post.id
selectedPostTitle.value = post.title

View File

@@ -1,160 +1,222 @@
<template>
<div class="snippet-form-container">
<h1 class="page-title">{{ isEditing ? '编辑代码片段' : '新建代码片段' }}</h1>
<div class="w-full h-[calc(100vh-8rem)] flex flex-col animate-reveal">
<!-- Top Bar -->
<div class="flex items-center justify-between mb-4 shrink-0 gap-3">
<h1 class="text-xl md:text-2xl font-serif italic text-white truncate">
{{ isEditing ? '编辑代码片段' : '新建代码片段' }}
</h1>
<div class="flex gap-2 md:gap-3 shrink-0">
<button type="button" class="admin-btn-secondary text-sm" @click="handleCancel">
取消
</button>
<button type="button" class="admin-btn-primary text-sm" @click="handleSubmit" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : (isEditing ? '更新' : '创建') }}
</button>
</div>
</div>
<div class="form-container">
<form @submit.prevent="handleSubmit" class="snippet-form">
<!-- Title Field -->
<div class="form-group">
<label for="title">标题</label>
<input
<form @submit.prevent="handleSubmit" class="flex-1 flex flex-col lg:flex-row gap-4 lg:gap-6 overflow-hidden min-h-0">
<!-- Left: Code / Description tabs -->
<div class="flex-1 flex flex-col min-w-0 min-h-[45vh] lg:min-h-0 h-full">
<div class="admin-card flex-1 flex flex-col overflow-hidden border border-white/10 min-h-0">
<div class="flex border-b border-white/10 shrink-0">
<button
v-for="tab in editorTabs"
:key="tab.id"
type="button"
class="flex-1 px-4 py-2.5 text-xs font-mono transition-colors"
:class="editorTab === tab.id
? 'text-art-accent bg-white/5 border-b-2 border-art-accent'
: 'text-art-muted hover:text-white'"
@click="editorTab = tab.id"
>
{{ tab.label }}
</button>
</div>
<div v-show="editorTab === 'code'" class="flex-1 flex flex-col min-h-0 focus-within:border-art-accent/50">
<MdEditor
v-model="form.code"
theme="dark"
:preview="false"
placeholder="请输入代码..."
class="custom-md-editor flex-1"
/>
</div>
<div
v-show="editorTab === 'description'"
class="flex-1 flex flex-col min-h-0"
@paste="handleVideoPaste"
>
<MdEditor
ref="descEditorRef"
v-model="form.description"
theme="dark"
:preview="true"
placeholder="支持 Markdown可上传图片、粘贴或拖拽视频..."
class="custom-md-editor desc-md-editor flex-1"
@onUploadImg="handleUploadImg"
@onDrop="handleEditorDrop"
/>
</div>
</div>
<div class="error-message mt-1" v-if="errors.code && editorTab === 'code'">{{ errors.code }}</div>
</div>
<!-- Right: Meta & Settings -->
<div class="w-full lg:w-80 shrink-0 overflow-y-auto custom-scrollbar min-h-0 lg:max-h-full">
<div class="admin-card p-5 md:p-6 space-y-5">
<h3 class="text-sm font-medium text-white border-b border-white/5 pb-3 flex items-center gap-2">
<span class="text-art-accent"></span> 片段配置
</h3>
<div class="space-y-2">
<label for="title" class="block text-xs font-medium text-art-muted uppercase tracking-wider">标题</label>
<input
type="text"
id="title"
v-model="form.title"
class="admin-input"
placeholder="请输入代码片段标题"
required
/>
<div class="error-message" v-if="errors.title">
{{ errors.title }}
</div>
</div>
<!-- Type Field -->
<div class="form-group">
<label for="type">类型</label>
<CustomSelect
v-model.number="form.codeTypeId"
:options="codeTypeOptions"
placeholder="请选择代码类型"
/>
<div class="error-message" v-if="errors.codeTypeId">
{{ errors.codeTypeId }}
</div>
</div>
<!-- Code Field - Updated to MdEditor for better editing experience -->
<div class="form-group">
<label>代码内容</label>
<div class="editor-wrapper">
<MdEditor
v-model="form.code"
theme="dark"
:preview="false"
placeholder="请输入代码..."
class="custom-md-editor"
/>
<div class="error-message" v-if="errors.title">{{ errors.title }}</div>
</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>
<div class="error-message" v-if="errors.description">
{{ errors.description }}
<div class="space-y-2">
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">类型</label>
<CustomSelect
v-model.number="form.codeTypeId"
:options="codeTypeOptions"
placeholder="请选择代码类型"
/>
<div class="error-message" v-if="errors.codeTypeId">{{ errors.codeTypeId }}</div>
</div>
<div class="space-y-2">
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">
关联文章 <span class="normal-case text-art-muted/70">(可多选)</span>
</label>
<input
v-model="postSearch"
type="text"
placeholder="搜索文章标题..."
class="admin-input text-sm"
/>
<div class="post-chip-list">
<button
v-for="p in filteredPosts"
:key="p.id"
type="button"
class="post-chip"
:class="{ 'post-chip--active': form.postIds.includes(p.id) }"
@click="togglePost(p.id)"
>
{{ p.title }}
</button>
<div v-if="filteredPosts.length === 0" class="text-xs text-art-muted italic py-2">
暂无匹配文章
</div>
</div>
</div>
</div>
<!-- Submit Buttons -->
<div class="form-actions">
<button type="button" class="cancel-btn" @click="handleCancel">
取消
</button>
<button type="submit" class="submit-btn" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : (isEditing ? '更新代码片段' : '创建代码片段') }}
</button>
</div>
</form>
</div>
</div>
</form>
</div>
</template>
<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 type { ExposeParam } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
import { useToast } from '../../composables/useToast'
import { createSnippet, updateSnippet, fetchSnippet, getAdminCodeTypes } from '../../services/api'
import { useMdMediaUpload } from '../../composables/useMdMediaUpload'
import { createSnippet, updateSnippet, fetchSnippet, getAdminCodeTypes, getAdminPosts, Post } from '../../services/api'
import CustomSelect from '../../components/CustomSelect.vue'
const router = useRouter()
const route = useRoute()
const toast = useToast()
// Form state
const descEditorRef = ref<ExposeParam>()
const { handleUploadImg, handleEditorDrop, handleVideoPaste } = useMdMediaUpload(descEditorRef)
const editorTab = ref<'code' | 'description'>('code')
const editorTabs = [
{ id: 'code' as const, label: '代码' },
{ id: 'description' as const, label: '备注' },
]
const isSubmitting = ref(false)
const isEditing = computed(() => !!route.params.id)
const errors = reactive<Record<string, string>>({})
// Form data
const form = reactive({
title: '',
code: '',
codeTypeId: 0,
description: ''
description: '',
postIds: [] as number[]
})
const codeTypeOptions = ref<{ value: number; label: string }[]>([])
const adminPosts = ref<Post[]>([])
const postSearch = ref('')
const filteredPosts = computed(() => {
const q = postSearch.value.trim().toLowerCase()
if (!q) return adminPosts.value
return adminPosts.value.filter(p => p.title.toLowerCase().includes(q))
})
const togglePost = (postId: number) => {
const idx = form.postIds.indexOf(postId)
if (idx === -1) {
form.postIds.push(postId)
} else {
form.postIds.splice(idx, 1)
}
}
// Validation function
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.codeTypeId) {
errors.codeTypeId = '请选择代码类型'
isValid = false
}
// Validate code
if (!form.code.trim()) {
errors.code = '代码内容不能为空'
isValid = false
}
return isValid
}
// Submit handler
const handleSubmit = async () => {
if (!validateForm()) {
return
}
if (!validateForm()) return
isSubmitting.value = true
try {
if (isEditing.value) {
// Update existing snippet
await updateSnippet(route.params.id as string, form)
toast.success('代码片段更新成功')
} else {
// Create new snippet
await createSnippet(form)
const result = await createSnippet(form)
toast.success('代码片段创建成功')
if (result.id) {
router.push(`/admin/snippets/${result.id}/edit`)
return
}
}
// Redirect to snippets list
router.push('/admin/snippets')
} catch (error: any) {
console.error('Error submitting form:', error)
@@ -164,18 +226,20 @@ const handleSubmit = async () => {
}
}
// Cancel handler
const handleCancel = () => {
router.push('/admin/snippets')
}
// Lifecycle
onMounted(async () => {
try {
const types = await getAdminCodeTypes()
const [types, postsRes] = await Promise.all([
getAdminCodeTypes(),
getAdminPosts(1, 200)
])
codeTypeOptions.value = types.map(t => ({ value: t.id, label: `${t.name} (${t.category === 0 ? '前端' : t.category === 1 ? '后端' : '其他'})` }))
adminPosts.value = postsRes.list || []
} catch (error) {
console.error('Failed to load code types:', error)
console.error('Failed to load form data:', error)
}
if (isEditing.value) {
@@ -186,6 +250,7 @@ onMounted(async () => {
form.code = snippet.code
form.codeTypeId = snippet.codeTypeId || snippet.codeType?.id || 0
form.description = snippet.description || ''
form.postIds = snippet.posts?.map(p => p.id) || []
} catch (error: any) {
console.error('Failed to fetch snippet data:', error)
toast.error('加载代码片段数据失败: ' + (error.message || '未知错误'))
@@ -195,147 +260,66 @@ onMounted(async () => {
</script>
<style scoped>
.snippet-form-container {
width: 100%;
}
.page-title {
font-size: 1.75rem;
font-weight: 600;
color: #d4b383;
margin-bottom: 1.5rem;
font-family: 'Inter', sans-serif;
}
.form-container {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 0.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.snippet-form {
max-width: 800px;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
font-size: 0.95rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 0.5rem;
font-family: 'Inter', sans-serif;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 0.5rem;
font-size: 0.95rem;
background: rgba(255, 255, 255, 0.05);
color: white;
font-family: 'Inter', sans-serif;
resize: vertical;
}
.form-group input::placeholder,
.form-group textarea::placeholder,
.form-group select::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
outline: none;
border-color: #d4b383;
box-shadow: 0 0 0 3px rgba(212, 179, 131, 0.2);
}
.error-message {
color: #ef4444;
font-size: 0.875rem;
font-size: 0.75rem;
margin-top: 0.25rem;
font-family: 'Inter', sans-serif;
}
.form-actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
.cancel-btn {
padding: 0.75rem 1.5rem;
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 0.5rem;
cursor: pointer;
font-weight: 500;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
font-family: 'Inter', sans-serif;
}
.cancel-btn:hover {
background: rgba(212, 179, 131, 0.1);
border-color: #d4b383;
color: #d4b383;
}
.submit-btn {
padding: 0.75rem 1.5rem;
background-color: #d4b383;
color: #050505;
border: 1px solid #d4b383;
border-radius: 0.5rem;
cursor: pointer;
font-weight: 500;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
font-family: 'Inter', sans-serif;
}
.submit-btn:hover:not(:disabled) {
background-color: transparent;
color: #d4b383;
transform: translateY(-1px);
box-shadow: 0 0 15px rgba(212, 179, 131, 0.3);
}
.submit-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* MdEditor Customization (Consistent with PostForm) */
.editor-wrapper {
border: 1px solid rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.1);
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;
background-color: transparent !important;
--md-bk-color: transparent !important;
--md-color: white !important;
--md-border-color: transparent !important;
height: 500px;
height: 100% !important;
min-height: 280px;
}
:deep(.desc-md-editor) {
height: 100% !important;
min-height: 280px;
}
.post-chip-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
min-height: 2.5rem;
padding: 0.75rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 0.5rem;
max-height: 160px;
overflow-y: auto;
}
.post-chip {
padding: 0.35rem 0.75rem;
font-size: 0.75rem;
border-radius: 9999px;
border: 1px solid rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
.post-chip:hover {
border-color: rgba(255, 255, 255, 0.3);
color: white;
}
.post-chip--active {
background: rgba(212, 179, 131, 0.15);
border-color: #d4b383;
color: #d4b383;
}
:deep(.md-editor-toolbar-wrapper) {
@@ -351,20 +335,4 @@ onMounted(async () => {
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>

View File

@@ -63,7 +63,7 @@ const fetchSnippets = async () => {
}
}
const deleteSnippet = async (id: string) => {
const deleteSnippet = async (id: number) => {
if (confirm('确定要删除这个代码片段吗?')) {
try {
await deleteSnippetApi(id)