lee-zt 6 days ago
commit c4eff39ab9

@ -2,11 +2,11 @@
lj:
db:
host: localhost
password: lzt&264610
password: 1243969857
redis:
host: localhost
port: 6379
password: 123456
password: 1243969857
rabbitmq:
host: localhost
port: 5672

@ -1,4 +1,9 @@
<<<<<<< HEAD
import {defineStore} from 'pinia';
//import axios from 'axios';
=======
import {defineStore} from 'pinia';
>>>>>>> 61cc9965ff7b00a12b6a049686385afb06ef8be0
import request from '@/utils/request';
import { ElMessage } from 'element-plus';

@ -0,0 +1,78 @@
import {defineStore} from 'pinia';
//import axios from 'axios';
import request from '@/utils/request';
export const usePostListStore = defineStore('postList', {
state: () => ({
posts: [], // 帖子列表
total: 0, // 帖子总数
page: 1, // 当前页码
pageSize: 10, // 每页帖子数
lastVal: Date.now(), // 用于滚动分页的时间戳
offset: 0, // 偏移量
loading: false, // 加载状态
finished: false, // 是否加载完全部
}),
actions: {
setPosts(posts) {
this.posts = posts;
},
setTotal(total) {
this.total = total;
},
setPage(page) {
this.page = page;
},
setPageSize(pageSize) {
this.pageSize = pageSize;
},
addPost(post) {
this.posts.push(post);
this.total += 1; // 更新总数
},
removePost(postId) {
this.posts = this.posts.filter(post => post.id !== postId);
this.total -= 1; // 更新总数
},
async getList({ lastVal = this.lastVal, offset = this.offset, size = this.pageSize } = {}) {
if (this.loading || this.finished) return;
this.loading = true;
try {
const res = await request.post('/post/user', { lastVal, offset, size });
if (res.code === 200) {
const { records, lastVal: newLastVal, offset: newOffset, size: newSize } = res.data;
if (records.length > 0) {
// 字段映射
const mappedRecords = records.map(post => ({
id: post.id,
avatar: post.userAvatar || post.image || require('@/assets/default-avatar/boy_1.png'),
title: post.title,
summary: post.content ? post.content.slice(0, 40) + (post.content.length > 40 ? '...' : '') : '',
likes: post.likeCount,
comments: post.commentCount,
favorites: post.favoriteCount,
category: post.category || '全部',
createTime: post.createTime,
userName: post.userName,
}));
this.posts = [...this.posts, ...mappedRecords];
this.lastVal = newLastVal;
this.offset = newOffset;
this.pageSize = newSize;
}
if (records.length < size) {
this.finished = true; // 没有更多数据
}
}
} finally {
this.loading = false;
}
},
resetList() {
this.posts = [];
this.lastVal = Date.now();
this.offset = 0;
this.finished = false;
}
},
});

@ -0,0 +1,255 @@
<template>
<div class="post-container">
<h1 class="page-title">发布新帖子</h1>
<div class="editor-wrapper">
<el-form
ref="postForm"
:model="form"
:rules="rules"
label-width="80px"
@submit.prevent="submitForm"
>
<!-- 标题输入 -->
<el-form-item label="标题" prop="title">
<el-input
v-model="form.title"
placeholder="请输入标题3-50个字符"
maxlength="50"
show-word-limit
/>
</el-form-item>
<!-- 分类选择 -->
<el-form-item label="分类" prop="categoryId">
<el-select
v-model="form.categoryId"
placeholder="请选择分类"
class="category-selector"
>
<el-option
v-for="category in categories"
:key="category.id"
:label="category.name"
:value="category.id"
/>
</el-select>
</el-form-item>
<!-- 图片上传 -->
<el-form-item label="图片" prop="image">
<el-upload
action="/api/upload"
list-type="picture-card"
:file-list="fileList"
:on-success="handleUploadSuccess"
:on-remove="handleRemove"
:limit="3"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<!-- 内容编辑器 -->
<el-form-item label="内容" prop="content">
<el-input
v-model="form.content"
type="textarea"
:rows="8"
placeholder="请输入帖子内容支持Markdown语法"
resize="none"
/>
<div class="markdown-tips">
<span>Markdown语法支持</span>
<el-link type="info" href="/help/markdown" target="_blank">查看帮助</el-link>
</div>
</el-form-item>
<!-- 提交按钮 -->
<div class="submit-area">
<el-button
type="primary"
size="large"
:loading="submitting"
@click="submitForm"
>
{{ submitting ? '发布中...' : '立即发布' }}
</el-button>
<el-button plain @click="saveDraft">稿</el-button>
</div>
</el-form>
</div>
<!-- 操作反馈 -->
<el-alert
v-if="submitResult.visible"
:title="submitResult.title"
:type="submitResult.type"
:closable="false"
class="result-alert"
/>
</div>
</template>
<script>
export default {
data() {
return {
submitting: false,
categories: [], // API
fileList: [],
form: {
title: '',
content: '',
categoryId: null,
image: '',
status: 0 // 0
},
rules: {
title: [
{ required: true, message: '标题不能为空', trigger: 'blur' },
{ min: 3, max: 50, message: '长度在3到50个字符', trigger: 'blur' }
],
content: [
{ required: true, message: '内容不能为空', trigger: 'blur' },
{ min: 10, message: '内容至少10个字符', trigger: 'blur' }
],
categoryId: [
{ required: true, message: '请选择分类', trigger: 'change' }
]
},
submitResult: {
visible: false,
type: 'success',
title: ''
}
}
},
async created() {
//
try {
const response = await this.$axios.get('/categories')
this.categories = response.data.data
} catch (error) {
console.error('加载分类失败:', error)
}
},
methods: {
//
handleUploadSuccess(response) {
this.form.image = this.form.image
? `${this.form.image},${response.data.url}`
: response.data.url
},
handleRemove(file) {
const urls = this.form.image.split(',')
this.form.image = urls.filter(url => url !== file.url).join(',')
},
//
async submitForm() {
try {
this.submitting = true
await this.$refs.postForm.validate()
const response = await this.$axios.post('/post', this.form)
if (response.data.code === 0) {
this.showResult('success', '帖子发布成功')
setTimeout(() => {
this.$router.push(`/post/${response.data.data}`)
}, 1500)
} else {
this.showResult('error', response.data.msg || '发布失败')
}
} catch (error) {
this.showResult('error', error.response?.data?.msg || '网络错误')
} finally {
this.submitting = false
}
},
// 稿
async saveDraft() {
try {
await this.$axios.post('/drafts', {
...this.form,
status: 1 // 1稿
})
this.showResult('success', '草稿保存成功')
} catch (error) {
this.showResult('error', '保存失败')
}
},
//
showResult(type, message) {
this.submitResult = {
visible: true,
type,
title: message
}
setTimeout(() => {
this.submitResult.visible = false
}, 3000)
}
}
}
</script>
<style scoped>
.post-container {
max-width: 800px;
margin: 20px auto;
padding: 30px;
background-color: #f9f9f9;
border-radius: 8px;
}
.page-title {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-size: 1.8em;
}
.editor-wrapper {
background: white;
padding: 25px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
}
.category-selector {
width: 100%;
}
.submit-area {
text-align: center;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.markdown-tips {
margin-top: 10px;
font-size: 0.9em;
color: #666;
}
.result-alert {
margin-top: 20px;
}
/* 保持与通知页面一致的输入框样式 */
.el-textarea__inner,
.el-input__inner {
border-radius: 4px;
border: 1px solid #e4e4e4;
transition: border-color 0.2s;
}
.el-input__inner:focus {
border-color: #409eff;
}
</style>

@ -55,93 +55,99 @@
</div>
</div>
</div>
<!-- 添加加载和错误状态显示 -->
<div v-if="loading" class="loading">...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<div v-else>
<!-- 你的页面内容 -->
<div v-for="post in userPosts" :key="post.id">
{{ post.title }}
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
<script setup lang="js" name="UserPage">
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/stores/user.js';
import axios from 'axios';
import { usePostListStore } from '@/stores/userpost'; //
const router = useRouter();
const userStore = useUserStore();
const postListStore = usePostListStore(); // postListStore
//
const loading = ref(true);
//
const userPosts = ref([
{
id: 1,
title: "雷霆连续两场20分钟逆转什么水平",
summary: "今天保罗带领雷霆完成逆转末节狂胜15分",
likes: 1200,
comments: 56,
favorites: 230
},
{
id: 2,
title: "一个普通人需要多大努力才能考上985",
summary: "分享我的备考经历每天学习12小时坚持300天...",
likes: 2200,
comments: 108,
favorites: 450
}
]);
//
const isComponentMounted = ref(false);
//
const userInfo = computed(() => userStore.userInfo);
//
const userPosts = computed(() => postListStore.posts);
const userStats = computed(() => ({
postCount: userPosts.value.length,
postCount: postListStore.total,
followers: 0,
following: 0,
likes: userPosts.value.reduce((sum, post) => sum + (post.likeCount || 0), 0)
likes: userPosts.value.reduce((sum, post) => sum + (post.likes || 0), 0)
}));
//
const handleScroll = async () => {
if (!isComponentMounted.value) return;
const scrollContainer = document.documentElement;
if (
scrollContainer.scrollTop + window.innerHeight >= scrollContainer.scrollHeight - 10 &&
!postListStore.loading &&
!postListStore.finished
) {
try {
// store
await postListStore.getList({
lastVal: postListStore.lastVal,
offset: postListStore.offset,
size: postListStore.pageSize
});
} catch (error) {
console.error("加载帖子失败:", error);
}
}
};
onMounted(async () => {
isComponentMounted.value = true;
try {
//
const response = await axios.post('/post/user',
{
offset: 0, //
size: 10 //
},
{
headers: {
Authorization: `Bearer ${userStore.userInfo.accessToken}`
}
}
);
//
if (response.data.code === 0) {
userPosts.value = response.data.data.records.map(post => ({
id: post.id,
title: post.title,
summary: post.summary, // 50
likes: post.likeCount,
comments: post.commentCount,
favorites: post.favoriteCount,
image: post.image || '/default-post-image.png'
}));
}
// 使 store
await postListStore.getList({
lastVal: postListStore.lastVal,
offset: postListStore.offset,
size: postListStore.pageSize
});
} catch (error) {
console.error('帖子加载失败:', error);
alert('帖子加载失败,请检查网络连接');
} finally {
loading.value = false;
console.error("初始化加载失败:", error);
}
window.addEventListener('scroll', handleScroll);
});
//
onUnmounted(() => {
isComponentMounted.value = false;
window.removeEventListener('scroll', handleScroll);
//
// postListStore.resetList();
});
//
const goToPostDetail = (postId) => {
router.push({ name: 'PostDetail', params: { id: postId } });
router.push({
name: 'PostDetail',
params: { id: postId },
state: { from: 'user' }
});
};
</script>
<style scoped>
.user-page-container {
display: flex;
justify-content: center;

Loading…
Cancel
Save