feat: update admin

This commit is contained in:
Rogee
2025-05-08 14:35:20 +08:00
parent eda360e398
commit 3ded1ddd60
8 changed files with 42 additions and 43 deletions

View File

@@ -19,7 +19,7 @@ const navItems = ref([
command: () => router.push('/medias')
},
{
label: '文章',
label: '曲谱',
icon: 'pi pi-file',
command: () => router.push('/posts')
},

View File

@@ -73,7 +73,7 @@ onMounted(() => {
<template #header>
<div class="border border-primary"></div>
</template>
<template #title>文章数量</template>
<template #title>曲谱数量</template>
<template #content>
<div class="text-4xl font-bold mb-2 text-primary">
{{ stats.post_published }}/{{ stats.post_draft }}

View File

@@ -192,7 +192,7 @@ onMounted(() => {
</div>
</template>
</Column>
<Column field="post_id" header="文章ID" sortable>
<Column field="post_id" header="曲谱ID" sortable>
<template #body="{ data }">
<div class="flex flex-col">
<span class="text-gray-700"> 标题: {{ data.post_title }}</span>
@@ -207,7 +207,7 @@ onMounted(() => {
<span class="text-orange-500">优惠: -¥{{ formatPrice(getDiscountAmount(data.price,
data.discount)) }}</span>
<span class="font-bold">实付: ¥{{ formatPrice(getFinalPrice(data.price, data.discount))
}}</span>
}}</span>
</div>
</template>
</Column>

View File

@@ -191,12 +191,12 @@ const savePost = async () => {
let valid = true;
if (!post.title.trim()) {
errors.title = '请填写文章标题';
errors.title = '请填写曲谱标题';
valid = false;
}
// if (!post.introduction.trim()) {
// errors.introduction = '请填写文章介绍';
// errors.introduction = '请填写曲谱介绍';
// valid = false;
// }
@@ -230,12 +230,12 @@ const savePost = async () => {
toast.add({ severity: 'error', summary: '错误', detail: resp.message, life: 3000 });
return;
}
toast.add({ severity: 'success', summary: '成功', detail: '文章已成功创建', life: 3000 });
toast.add({ severity: 'success', summary: '成功', detail: '曲谱已成功创建', life: 3000 });
// Navigate back to the post list
router.push('/posts');
} catch (error) {
toast.add({ severity: 'error', summary: '错误', detail: '创建文章失败', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '创建曲谱失败', life: 3000 });
}
};
@@ -263,7 +263,7 @@ const loadHeadImagePreviews = async () => {
<div class="w-full max-w-6xl mx-auto">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">创建文章</h1>
<h1 class="text-2xl font-bold text-gray-800">创建曲谱</h1>
<div class="flex gap-2">
<Button label="取消" icon="pi pi-times" severity="secondary" @click="cancelCreate" />
<Button label="保存" icon="pi pi-check" severity="primary" @click="savePost" />
@@ -310,7 +310,7 @@ const loadHeadImagePreviews = async () => {
<!-- Title -->
<div class="col-span-2">
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">标题</label>
<InputText id="title" v-model="post.title" class="w-full p-inputtext-lg" placeholder="输入文章标题" />
<InputText id="title" v-model="post.title" class="w-full p-inputtext-lg" placeholder="输入曲谱标题" />
<small v-if="errors.title" class="text-red-500">{{ errors.title }}</small>
</div>
@@ -343,9 +343,9 @@ const loadHeadImagePreviews = async () => {
<!-- Introduction -->
<div class="col-span-2">
<label for="introduction" class="block text-sm font-medium text-gray-700 mb-1">文章介绍</label>
<label for="introduction" class="block text-sm font-medium text-gray-700 mb-1">曲谱介绍</label>
<Textarea id="introduction" v-model="post.introduction" rows="5" class="w-full"
placeholder="输入文章介绍内容" />
placeholder="输入曲谱介绍内容" />
<small v-if="errors.introduction" class="text-red-500">{{ errors.introduction }}</small>
</div>

View File

@@ -130,7 +130,7 @@ const fetchPost = async (id) => {
post.head_images = postData.head_images || []; // Add head images
loadHeadImagePreviews(); // Load head image previews
} catch (error) {
toast.add({ severity: 'error', summary: '错误', detail: '加载文章失败', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '加载曲谱失败', life: 3000 });
router.push('/posts');
} finally {
loading.value = false;
@@ -231,12 +231,12 @@ const savePost = async () => {
let valid = true;
if (!post.title.trim()) {
errors.title = '请填写文章标题';
errors.title = '请填写曲谱标题';
valid = false;
}
// if (!post.introduction.trim()) {
// errors.introduction = '请填写文章介绍';
// errors.introduction = '请填写曲谱介绍';
// valid = false;
// }
@@ -270,10 +270,10 @@ const savePost = async () => {
return;
}
toast.add({ severity: 'success', summary: '成功', detail: '文章已成功更新', life: 3000 });
toast.add({ severity: 'success', summary: '成功', detail: '曲谱已成功更新', life: 3000 });
router.push('/posts');
} catch (error) {
toast.add({ severity: 'error', summary: '错误', detail: '更新文章失败', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '更新曲谱失败', life: 3000 });
}
};
@@ -297,7 +297,7 @@ onMounted(() => {
if (postId) {
fetchPost(postId);
} else {
toast.add({ severity: 'error', summary: '错误', detail: '未提供文章ID', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '未提供曲谱ID', life: 3000 });
router.push('/posts');
}
});
@@ -307,7 +307,7 @@ onMounted(() => {
<div class="w-full max-w-6xl mx-auto">
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-800">编辑文章</h1>
<h1 class="text-2xl font-bold text-gray-800">编辑曲谱</h1>
<div class="flex gap-2">
<Button label="取消" icon="pi pi-times" severity="secondary" @click="cancelEdit" />
<Button label="保存" icon="pi pi-check" severity="primary" @click="savePost" />
@@ -316,7 +316,7 @@ onMounted(() => {
<div v-if="loading" class="flex flex-col items-center justify-center py-12">
<ProgressSpinner style="width:50px;height:50px" />
<span class="mt-4">加载文章数据...</span>
<span class="mt-4">加载曲谱数据...</span>
</div>
<div v-else>
@@ -359,7 +359,7 @@ onMounted(() => {
<!-- Title -->
<div class="col-span-2">
<label for="title" class="block text-sm font-medium text-gray-700 mb-1">标题</label>
<InputText id="title" v-model="post.title" class="w-full p-inputtext-lg" placeholder="输入文章标题" />
<InputText id="title" v-model="post.title" class="w-full p-inputtext-lg" placeholder="输入曲谱标题" />
<small v-if="errors.title" class="text-red-500">{{ errors.title }}</small>
</div>
@@ -392,9 +392,9 @@ onMounted(() => {
<!-- Introduction -->
<div class="col-span-2">
<label for="introduction" class="block text-sm font-medium text-gray-700 mb-1">文章介绍</label>
<label for="introduction" class="block text-sm font-medium text-gray-700 mb-1">曲谱介绍</label>
<Textarea id="introduction" v-model="post.introduction" rows="5" class="w-full"
placeholder="输入文章介绍内容" />
placeholder="输入曲谱介绍内容" />
<small v-if="errors.introduction" class="p-error">{{ errors.introduction }}</small>
</div>

View File

@@ -35,7 +35,7 @@ const statusOptions = ref([
// Media types for filtering
const mediaTypeOptions = ref([
{ name: '所有类型', value: null },
{ name: '文章', value: '文章' },
{ name: '曲谱', value: '曲谱' },
{ name: '视频', value: '视频' },
{ name: '音频', value: '音频' }
]);
@@ -92,12 +92,12 @@ const confirmDelete = (post) => {
postService.deletePost(post.id)
.then(() => {
// toast success
toast.add({ severity: 'success', summary: '成功', detail: '文章已删除', life: 3000 });
toast.add({ severity: 'success', summary: '成功', detail: '曲谱已删除', life: 3000 });
fetchPosts();
})
.catch(error => {
console.error('Delete error:', error); // Debug log
toast.add({ severity: 'error', summary: '错误', detail: '删除文章失败', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '删除曲谱失败', life: 3000 });
});
}
@@ -140,7 +140,7 @@ const fetchPosts = async () => {
total.value = response.data.total;
} catch (error) {
console.error('Fetch error:', error); // Debug log
toast.add({ severity: 'error', summary: '错误', detail: '加载文章失败', life: 3000 });
toast.add({ severity: 'error', summary: '错误', detail: '加载曲谱失败', life: 3000 });
} finally {
loading.value = false;
}
@@ -173,11 +173,10 @@ onMounted(() => {
// Status badge severity mapping
const getBadgeSeverity = (status) => {
const map = {
'发布': 'success',
'草稿': 'warning',
'已下架': 'danger'
'发布': 'success',
'草稿': 'secondary',
};
return map[status] || 'info';
return map[status] || 'warn';
};
// Format price to display ¥ symbol
@@ -299,7 +298,7 @@ const handleSendConfirm = async () => {
<Dialog v-model:visible="sendDialogVisible" modal header="选择用户" :style="{ width: '80vw' }">
<div class="flex flex-col gap-4">
<div class="mb-4">
<span class="font-bold">文章</span>
<span class="font-bold">曲谱</span>
{{ selectedPost?.title }}
</div>
@@ -366,16 +365,16 @@ const handleSendConfirm = async () => {
<div class="w-full">
<div class="flex justify-between items-center mb-6 gap-4">
<h1 class="text-2xl font-semibold text-gray-800 text-nowrap">文章列表</h1>
<h1 class="text-2xl font-semibold text-gray-800 text-nowrap">曲谱列表</h1>
<Button class="text-nowrap !px-8" icon="pi pi-plus" label="创建文章" severity="primary"
<Button class="text-nowrap !px-8" icon="pi pi-plus" label="创建曲谱" severity="primary"
@click="navigateToCreatePost" />
</div>
<!-- Posts Table -->
<div class="card mt-10">
<div class="pb-10 flex">
<InputText v-model="globalFilterValue" placeholder="搜索文章..." class="flex-1" @input="onSearch" />
<InputText v-model="globalFilterValue" placeholder="搜索曲谱..." class="flex-1" @input="onSearch" />
</div>
<DataTable v-model:filters="filters" :value="posts" :paginator="true" :rows="rows" :totalRecords="total"
@@ -387,13 +386,13 @@ const handleSendConfirm = async () => {
removableSort class="p-datatable-sm" responsiveLayout="scroll">
<template #empty>
<div class="text-center p-4">未找到文章</div>
<div class="text-center p-4">未找到曲谱</div>
</template>
<template #loading>
<div class="flex flex-col items-center justify-center p-4">
<ProgressSpinner style="width:50px;height:50px" />
<span class="mt-2">加载文章数据...</span>
<span class="mt-2">加载曲谱数据...</span>
</div>
</template>
@@ -412,12 +411,12 @@ const handleSendConfirm = async () => {
<span class="text-orange-500">优惠: -{{ formatPrice(getDiscountAmount(data.price,
data.discount)) }}</span>
<span class="font-bold">实付: {{ formatPrice(getFinalPrice(data.price, data.discount))
}}</span>
}}</span>
</div>
</template>
</Column>
<Column field="bought_count" header="购买数量" sortable>
<Column field="bought_count" header="销售数量" sortable>
<template #body="{ data }">
<div class="flex flex-col">
<span class="text-gray-500">{{ data.bought_count }}</span>

View File

@@ -113,9 +113,9 @@ const formatPrice = (price) => {
</div>
</div>
<!-- 用户购买的文章列表 -->
<!-- 用户购买的曲谱列表 -->
<div class="card">
<h3 class="text-xl font-semibold mb-4">购买的文章</h3>
<h3 class="text-xl font-semibold mb-4">购买的曲谱</h3>
<DataTable :value="userArticles" stripedRows class="p-datatable-sm" responsiveLayout="scroll"
:lazy="true" :totalRecords="totalArticles" :rows="lazyParams.limit" :loading="loading"
@page="onPage" paginator :rows-per-page-options="[10, 20, 50]">

View File

@@ -72,7 +72,7 @@ onMounted(() => {
<div class="h-full flex flex-col">
<div class="flex-none bg-white border-b border-gray-200 z-50 shadow">
<div class="p-4">
<input type="search" v-model="searchInput" @keyup="handleKeyup" placeholder="搜索文章"
<input type="search" v-model="searchInput" @keyup="handleKeyup" placeholder="搜索"
class="w-full px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
</div>
@@ -91,7 +91,7 @@ onMounted(() => {
</div>
<div v-if="!hasMore && articles.length > 0" class="text-center text-gray-500 py-4">
没有更多文章
没有更多了
</div>
</div>
</div>