mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-10-09 11:12:02 +00:00
feat: 重构前端项目结构并优化代码
refactor: 迁移前端资源文件至web目录 feat: 新增多种图标资源 style: 统一代码风格和格式化配置 docs: 更新README和文档说明 chore: 更新依赖和配置文件 fix: 修复部分类型定义和枚举 perf: 优化路由和组件加载逻辑
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<ElForm class="w-full mx-auto mb-10" @submit.prevent="addComment">
|
||||
<ElFormItem prop="author" class="mt-5">
|
||||
<ElInput
|
||||
v-model="newComment.author"
|
||||
placeholder="你的名称"
|
||||
class="block w-full"
|
||||
clearable
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem prop="content">
|
||||
<ElInput
|
||||
v-model="newComment.content"
|
||||
placeholder="简单说两句..."
|
||||
type="textarea"
|
||||
:rows="5"
|
||||
clearable
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<div class="flex justify-end w-full">
|
||||
<ElButton type="primary" @click="addComment">发布</ElButton>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<ul>
|
||||
<div class="pb-5 text-lg font-medium">评论 {{ comments.length }}</div>
|
||||
<CommentItem
|
||||
v-for="comment in comments.slice().reverse()"
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
:show-reply-form="showReplyForm"
|
||||
class="pb-2.5 mb-5 border-b border-g-400"
|
||||
@toggle-reply="toggleReply"
|
||||
@add-reply="addReply"
|
||||
/>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import CommentItem from './widget/CommentItem.vue';
|
||||
import { commentList, Comment } from '@/mock/temp/commentDetail';
|
||||
const comments = commentList;
|
||||
|
||||
const newComment = ref<Partial<Comment>>({
|
||||
author: '',
|
||||
content: '',
|
||||
});
|
||||
|
||||
const showReplyForm = ref<number | null>(null);
|
||||
|
||||
const addComment = () => {
|
||||
if (!newComment.value.author?.trim() || !newComment.value.content?.trim()) {
|
||||
ElMessage.warning('请填写完整的评论信息');
|
||||
return;
|
||||
}
|
||||
|
||||
comments.value.push({
|
||||
id: Date.now(),
|
||||
author: newComment.value.author.trim(),
|
||||
content: newComment.value.content.trim(),
|
||||
timestamp: new Date().toISOString(),
|
||||
replies: [],
|
||||
});
|
||||
|
||||
newComment.value.author = '';
|
||||
newComment.value.content = '';
|
||||
ElMessage.success('评论发布成功');
|
||||
};
|
||||
|
||||
const addReply = (commentId: number, replyAuthor: string, replyContent: string) => {
|
||||
if (!replyAuthor?.trim() || !replyContent?.trim()) {
|
||||
ElMessage.warning('请填写完整的回复信息');
|
||||
return;
|
||||
}
|
||||
|
||||
const comment = findComment(comments.value, commentId);
|
||||
if (comment) {
|
||||
comment.replies.push({
|
||||
id: Date.now(),
|
||||
author: replyAuthor.trim(),
|
||||
content: replyContent.trim(),
|
||||
timestamp: new Date().toISOString(),
|
||||
replies: [],
|
||||
});
|
||||
showReplyForm.value = null;
|
||||
ElMessage.success('回复发布成功');
|
||||
}
|
||||
};
|
||||
|
||||
const toggleReply = (commentId: number) => {
|
||||
showReplyForm.value = showReplyForm.value === commentId ? null : commentId;
|
||||
};
|
||||
|
||||
const findComment = (comments: Comment[], commentId: number): Comment | undefined => {
|
||||
for (const comment of comments) {
|
||||
if (comment.id === commentId) {
|
||||
return comment;
|
||||
}
|
||||
const found = findComment(comment.replies, commentId);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<li>
|
||||
<div>
|
||||
<div class="flex-c">
|
||||
<div
|
||||
class="size-5 mr-2.5 text-xs font-medium text-white rounded-full flex-cc"
|
||||
:style="{ background: randomColor() }"
|
||||
>
|
||||
{{ comment.author.substring(0, 1) }}
|
||||
</div>
|
||||
<strong class="block text-sm font-medium">{{ comment.author }}</strong>
|
||||
</div>
|
||||
<span class="block mt-2.5 text-sm text-g-700">{{ comment.content }}</span>
|
||||
<div class="flex-c mt-2.5">
|
||||
<span class="text-xs text-g-700">{{ formatDate(comment.timestamp) }}</span>
|
||||
<div
|
||||
class="ml-5 text-xs text-g-700 c-p select-none hover:text-theme"
|
||||
@click="toggleReply(comment.id)"
|
||||
>
|
||||
回复
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul v-if="comment.replies.length > 0" class="pl-2.5">
|
||||
<CommentItem
|
||||
v-for="reply in comment.replies"
|
||||
:key="reply.id"
|
||||
:comment="reply"
|
||||
:show-reply-form="showReplyForm"
|
||||
class="mt-5"
|
||||
@toggle-reply="toggleReply"
|
||||
@add-reply="addReply"
|
||||
/>
|
||||
</ul>
|
||||
|
||||
<ElForm v-if="showReplyForm === comment.id" class="mt-4" @submit.prevent="handleSubmit">
|
||||
<ElFormItem prop="author">
|
||||
<ElInput v-model="replyAuthor" placeholder="你的名称" clearable />
|
||||
</ElFormItem>
|
||||
<ElFormItem prop="content">
|
||||
<ElInput
|
||||
v-model="replyContent"
|
||||
placeholder="你的回复..."
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
clearable
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem>
|
||||
<div class="flex justify-end gap-2 w-full">
|
||||
<ElButton @click="toggleReply(comment.id)">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit">发布</ElButton>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AppConfig from '@/config';
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface Comment {
|
||||
id: number;
|
||||
author: string;
|
||||
content: string;
|
||||
timestamp: string;
|
||||
replies: Comment[];
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
comment: Comment;
|
||||
showReplyForm: number | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'toggle-reply', commentId: number): void;
|
||||
(event: 'add-reply', commentId: number, replyAuthor: string, replyContent: string): void;
|
||||
}>();
|
||||
|
||||
const replyAuthor = ref('');
|
||||
const replyContent = ref('');
|
||||
|
||||
const toggleReply = (commentId: number) => {
|
||||
emit('toggle-reply', commentId);
|
||||
};
|
||||
|
||||
const addReply = (commentId: number, author: string, content: string) => {
|
||||
emit('add-reply', commentId, author, content);
|
||||
replyAuthor.value = '';
|
||||
replyContent.value = '';
|
||||
};
|
||||
const handleSubmit = () => {
|
||||
if (!replyAuthor.value.trim() || !replyContent.value.trim()) {
|
||||
return;
|
||||
}
|
||||
emit('add-reply', props.comment.id, replyAuthor.value, replyContent.value);
|
||||
replyAuthor.value = '';
|
||||
replyContent.value = '';
|
||||
};
|
||||
|
||||
const formatDate = (timestamp: string) => {
|
||||
const date = new Date(timestamp);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
let lastColor: string | null = null;
|
||||
|
||||
const randomColor = () => {
|
||||
let newColor: string;
|
||||
|
||||
do {
|
||||
const index = Math.floor(Math.random() * AppConfig.systemMainColor.length);
|
||||
newColor = AppConfig.systemMainColor[index];
|
||||
} while (newColor === lastColor);
|
||||
|
||||
lastColor = newColor;
|
||||
return newColor;
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user