完善未完成功能:修复关注列表/评论发布/编辑器草稿等页面

This commit is contained in:
2026-08-06 00:10:19 +08:00
parent b3c6fc67c9
commit 315bc474d6
28 changed files with 2979 additions and 4276 deletions
+267 -262
View File
@@ -3,7 +3,6 @@
<unicloud-db ref='udb' v-slot:default="{ pagination, hasMore, loading, error, options }"
orderby="last_view_time desc" @error="onqueryerror" :page-size="10" @onPullDownRefresh="onPullDownRefresh"
@load="listLoad" :collection="colList">
<!-- 基于 uni-list 的页面布局 field="user_id.nickname"-->
<!-- #ifdef APP-NVUE -->
<list class="uni-list" :border="false" :style="{ height: listHeight }">
<!-- #endif -->
@@ -16,15 +15,13 @@
<!-- #endif -->
<template v-for="item in listData">
<uni-card :title="item.author_id.nickname" :sub-title="item.update_time"
:thumbnail="'https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/unicloudlogo.png'">
<uni-card :title="item.author_id.nickname" :sub-title="item.update_time">
<template v-slot:title>
<view class="p_f">
<cloud-image v-if="item.author_id.avatar_file" height="80rpx" width="80rpx"
borderRadius="50%" :src="item.author_id.avatar_file.url"></cloud-image>
<image class="avatar" :src="emptyAvatar" v-else></image>
<view>
<view class="title">
{{ item.author_id.nickname }}
@@ -38,21 +35,20 @@
回复
</view>
</view>
</template>
<text class="uni-body" @click="toArticle(item)">
{{ item.author_id.nickname }}: {{ item.content }}
</text>
<uni-card :is-shadow="false" :isFull="true" @click="toArticle(item)">
<text class="uni-body">
{{ item.parent_author_id.nickname }}:
{{ item.parent_author_id.nickname }}:
</text>
<text class="uni-body">{{ item.parent_author_content }}</text>
</uni-card>
</uni-card>
</template>
<!-- 加载状态:上拉加载更多,加载中,没有更多数据了,加载错误 -->
<!-- 加载状态 -->
<!-- #ifdef APP-PLUS -->
<uni-list-item>
<template v-slot:body>
@@ -71,7 +67,6 @@
</list>
<!-- #endif -->
<view class="comment-submit-box" v-if="submit" @click="closeInput">
<!-- 下边的click.stop.prevent用于让上边的click不传下去,以防点到下边的空白处触发closeInput方法 -->
<view class="comment-add" @click.stop.prevent="stopPrevent" :style="'bottom:' + KeyboardHeight + 'px'">
<view class="comment-submit">
<view class="btn-click cancel" @click="closeInput">取消</view>
@@ -86,7 +81,7 @@
</view>
</view>
<textarea class="textarea" v-model="commentReq.content" :placeholder="placeholder" :adjust-position="false" :show-confirm-bar="false"
@blur="blur" @focus="focusOn" :focus="focus" maxlength="800"></textarea>
@blur="blur" @focus="focusOnInput" :focus="focus" maxlength="800"></textarea>
</view>
</view>
</unicloud-db>
@@ -94,26 +89,19 @@
</template>
<script>
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time";
import refreshBox from "@/uni_modules/uni-cms-article/components/refresh-box/refreshBox.nvue";
import { ref, computed, reactive, onReady, watch } from 'vue'
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar"
import translatePublishTime from "@/uni_modules/uni-cms-article/common/publish-time"
import refreshBox from "@/uni_modules/uni-cms-article/components/refresh-box/refreshBox.nvue"
import notCover from "@/uni_modules/uni-cms-article/components/list-template/not-cover.vue"
import rightSmallCover from "@/uni_modules/uni-cms-article/components/list-template/right-small-cover.vue"
import threeCover from "@/uni_modules/uni-cms-article/components/list-template/three-cover.vue"
import { parseImageUrl } from "@/uni_modules/uni-cms-article/common/parse-image-url"
import { store } from '@/pages3/uni_modules/uni-id-pages/common/store.js'
import notCover from "@/uni_modules/uni-cms-article/components/list-template/not-cover.vue";
import rightSmallCover from "@/uni_modules/uni-cms-article/components/list-template/right-small-cover.vue";
import threeCover from "@/uni_modules/uni-cms-article/components/list-template/three-cover.vue";
import {
parseImageUrl
} from "@/uni_modules/uni-cms-article/common/parse-image-url";
const db = uniCloud.database();
const articleDBName = 'uni-cms-articles'
const db = uniCloud.database()
const userDBName = 'uni-id-users'
const uniIdCo = uniCloud.importObject("uni-id-co")
import {
store,
mutations
} from '@/pages3/uni_modules/uni-id-pages/common/store.js';
const articleDBName = 'uni-cms-articles'
export default {
components: {
@@ -123,35 +111,40 @@
rightSmallCover,
threeCover
},
computed: {
userInfo() {
return store.userInfo
},
queryUserId() {
return this.userInfo && this.userInfo._id ? this.userInfo._id : '__empty__'
},
realNameStatus() {
if (!this.userInfo.realNameAuth) {
return 0
}
return this.userInfo.realNameAuth.authStatus
},
// 根据当前语言返回不同的搜索框占位符
inputPlaceholder(e) {
if (uni.getStorageSync('CURRENT_LANG') == "en") {
return 'Please enter the search content' // 英文
} else {
return '请输入搜索内容' // 中文
}
},
// 连表查询,返回两个集合的查询结果
colList() {
setup() {
const udbRef = ref(null)
const emptyAvatar = ref("https://qnycdn.mymoyu.top/static/avatar.jpg")
const showRefresh = ref(false)
const listHeight = ref(0)
const mpButtonLeftPlaceholderSize = ref(0)
const mpButtonPlaceholderSize = ref(87)
const navBarHeight = ref(44)
const listData = ref([])
const loadType = ref(null)
const placeholder = ref("请输入评论")
const commentReq = reactive({
pId: null,
article_id: null,
content: null,
root_id: null
})
const pUser = ref(null)
const showTag = ref(false)
const focus = ref(false)
const submit = ref(false)
const KeyboardHeight = ref(0)
const userInfo = computed(() => store.userInfo)
const queryUserId = computed(() => {
return userInfo.value && userInfo.value._id ? userInfo.value._id : '__empty__'
})
const colList = computed(() => {
return [
db.collection("comment")
.where({
parent_author_id: this.queryUserId,
})
.where({ parent_author_id: queryUserId.value })
.getTemp(),
db.collection("uni-id-users")
.field("_id,avatar_file,nickname")
@@ -159,275 +152,288 @@
db.collection("uni-cms-articles")
.field("_id,thumbnail,title,publish_date,user_id, excerpt, article_status")
.getTemp()
];
}
},
data() {
return {
emptyAvatar: "https://qnycdn.mymoyu.top/static/avatar.jpg",
tags_v: [],
tags: [
]
})
],
showRefresh: false, // 是否显示刷新按钮
listHeight: 0, // 列表高度
mpButtonLeftPlaceholderSize: 0, // 小程序左侧icon占位大小
mpButtonPlaceholderSize: 87, // 小程序导航栏按钮占位大小
navBarHeight: 44, // 导航栏高度
refreshStatus: 0, // 刷新状态 0: 未刷新 1: 刷新中 2: 刷新完成
listData: [], // 列表数据
loadType: null,
historyList: [],
placeholder: "请输入评论",
commentReq: {
pId: null, // 评论父id
content: null, // 评论内容
root_id: null
},
pUser: null, // 标签-回复人
showTag: false, // 标签展示与否
focus: false, // 输入框自动聚焦
submit: false, // 弹出评论
KeyboardHeight: 0 // 键盘高度
}
},
methods: {
stopPrevent() {},
getNewItem(item) {
function getNewItem(item) {
if (!item.excerpt) {
item.excerpt = "";
item.excerpt = ""
} else if (item.excerpt.length > 50) {
item.excerpt = item.excerpt.slice(0, 50) + " ... ...";
item.excerpt = item.excerpt.slice(0, 50) + " ... ..."
}
return item;
},
async listLoad(data) {
console.log(data);
return item
}
const listData = [];
async function listLoad(data) {
const tempListData = []
data.map(item => {
listData.push({
tempListData.push({
article_id: item.article_id[0],
author_id: item.author_id[0],
content: item.content,
parent_author_id: item.parent_author_id[0],
parent_author_content: item.parent_author_content,
root_id:item.root_id,
root_id: item.root_id,
_id: item._id,
update_time: new Date().getTime(item.update_time)
update_time: item.update_time
})
})
this.listData = this.loadType === 'loadMore' ? this.listData.concat(listData) : listData
this.loadType = null
},
// 初始化导航栏大小
initNavBarSize() {
// 获取小程序导航栏按钮信息
listData.value = loadType.value === 'loadMore' ? listData.value.concat(tempListData) : tempListData
loadType.value = null
}
function initNavBarSize() {
// #ifdef MP-TOUTIAO
let menuButtonInfo = tt.getCustomButtonBoundingClientRect()
menuButtonInfo.width = menuButtonInfo.capsule.width // 小程序按钮区域中使用的按钮宽度
this.mpButtonLeftPlaceholderSize = menuButtonInfo.leftIcon.width + 10
menuButtonInfo.width = menuButtonInfo.capsule.width
mpButtonLeftPlaceholderSize.value = menuButtonInfo.leftIcon.width + 10
// #endif
// #ifndef MP-TOUTIAO
let menuButtonInfo = uni.getMenuButtonBoundingClientRect()
// #endif
// 计算小程序导航栏按钮占位大小
this.mpButtonPlaceholderSize = menuButtonInfo.width + 10
// 获取系统信息,判断是否为 iOS 系统,设置导航栏高度
this.navBarHeight = uni.getSystemInfoSync().system.toLowerCase().includes('ios') ? 44 : 48
},
// 格式化时间戳
publishTime(timestamp) {
mpButtonPlaceholderSize.value = menuButtonInfo.width + 10
navBarHeight.value = uni.getSystemInfoSync().system.toLowerCase().includes('ios') ? 44 : 48
}
function publishTime(timestamp) {
return translatePublishTime(timestamp)
},
// 点击搜索框
searchClick(e) {
uni.hideKeyboard();
uni.navigateTo({
url: '/uni_modules/uni-cms-article/pages/search/search'
});
},
// 重试
retry() {
this.refresh()
},
// 刷新
refresh() {
this.loadType = 'refresh'
console.log(this.$refs.udb);
this.$refs.udb.loadData({
clear: true
}, () => {
console.log(1);
}
function refresh() {
loadType.value = 'refresh'
udbRef.value.loadData({ clear: true }, () => {
uni.stopPullDownRefresh()
// #ifdef APP-NVUE
this.showRefresh = false
showRefresh.value = false
// #endif
})
},
// 加载更多
loadMore() {
this.loadType = 'loadMore'
this.$refs.udb.loadMore()
},
// 查询出错
onqueryerror(e) {
console.error(e);
},
}
// #ifdef H5
// 下拉刷新
onPullDownRefresh() {
this.refresh()
},
// #endif
change(currentPage, type) {
this.page.currentPage = currentPage;
console.log("点击了" + type + ",当前页:" + currentPage);
this.data_clear();
},
async getHistoryList() {
return uniCloud.importObject("cms-articles-log").getHistoryList({
user_id: this.userInfo._id
}).data.data;
},
async getHistory() {
console.log(this.userInfo._id);
return uniCloud.importObject("cms-articles-log").getHistory(this.userInfo._id);
},
closeInput() {
this.focus = false;
this.submit = false;
},
tagClose() {
this.showTag = false;
this.pUser = null;
this.commentReq.pId = null;
},
selectAdd() {
if (this.showTag === true) {
this.replyAdd()
function loadMore() {
loadType.value = 'loadMore'
udbRef.value.loadMore()
}
function onqueryerror(e) {
console.error(e)
}
function closeInput() {
focus.value = false
submit.value = false
}
function tagClose() {
showTag.value = false
pUser.value = null
commentReq.pId = null
}
function selectAdd() {
if (showTag.value === true) {
replyAdd()
} else {
this.add();
add()
}
},
async replyAdd() {
if (this.commentReq.content == null || this.commentReq.content.length < 0) {
}
async function replyAdd() {
if (!commentReq.content || !commentReq.content.trim()) {
uni.showToast({
title: '评论内容不能为空',
icon: 'none',
duration: 2000
});
})
return
}
try {
let content = this.commentReq.content;
// 获取当前用户信息(需确保已登录)
const uniIdToken = uni.getStorageSync('uni_id_token');
const uniIdToken = uni.getStorageSync('uni_id_token')
if (!uniIdToken) {
uni.showToast({
title: '请先登录',
icon: 'none'
});
return;
})
return
}
// return;
// 构建符合后端接口规范的评论对象
const comment = {
article_id: this.id,
root_id: this.commentReq.root_id,
content: this.commentReq.content,
parent_id: this.commentReq.pId
};
console.log(comment)
// 调用云对象方法
const commentObj = uniCloud.importObject("comments");
const res = await commentObj.replyComment(comment);
// 处理响应结果
article_id: commentReq.article_id,
root_id: commentReq.root_id,
content: commentReq.content,
parent_id: commentReq.pId
}
const commentObj = uniCloud.importObject("comments")
const res = await commentObj.replyComment(comment)
if (res.code === 200) {
uni.showToast({
title: '评论成功'
});
})
} else {
uni.showToast({
title: res.message || '评论失败,请稍后重试',
icon: 'none'
})
}
} catch (e) {
console.error('评论失败:', e);
// 错误分类处理(根据后端错误码映射)
console.error('评论失败:', e)
const errorHandler = {
'PERMISSION_DENIED': '登录状态失效,请重新登录',
'INVALID_PARAMS': '评论内容不能为空',
'CONTENT_BLOCKED': '包含违禁词汇: ' + e.message.split(':')[1],
'PARENT_COMMENT_NOT_FOUND': '回复的评论不存在'
};
}
uni.showToast({
title: errorHandler[e.errorType] || '评论失败,请稍后重试',
icon: 'none'
});
})
}
this.closeInput();
},
reply(comment) {
console.log(comment);
this.pUser = comment.author_id.nickname;
this.commentReq.pId = comment._id;
this.commentReq.root_id = comment.root_id === 0? comment._id : comment.root_id;
console.log(comment.author_id.nickname);
commentReq.content = ''
closeInput()
}
async function add() {
if (!commentReq.content || !commentReq.content.trim()) {
uni.showToast({
title: '评论内容不能为空',
icon: 'none',
duration: 2000
})
return
}
try {
const uniIdToken = uni.getStorageSync('uni_id_token')
if (!uniIdToken) {
uni.showToast({
title: '请先登录',
icon: 'none'
})
return
}
const comment = {
article_id: commentReq.article_id,
content: commentReq.content,
parent_id: 0,
root_id: 0
}
const commentObj = uniCloud.importObject("comments")
const res = await commentObj.addComment(comment)
if (res.code === 200) {
uni.showToast({
title: '评论成功'
})
} else {
uni.showToast({
title: res.message || '评论失败,请稍后重试',
icon: 'none'
})
}
} catch (e) {
console.error('评论失败:', e)
uni.showToast({
title: '评论失败,请稍后重试',
icon: 'none'
})
}
commentReq.content = ''
closeInput()
}
function reply(comment) {
pUser.value = comment.author_id.nickname
commentReq.pId = comment._id
commentReq.article_id = comment.article_id._id
commentReq.root_id = comment.root_id === 0 ? comment._id : comment.root_id
if (comment.author_id.nickname) {
this.commentReq.content = '@' + comment.author_id.nickname + ' ';
commentReq.content = '@' + comment.author_id.nickname + ' '
} else {
this.commentReq.content = '';
commentReq.content = ''
}
this.showTag = true;
this.commentInput();
},
// 输入评论
commentInput() {
// TODO 调起键盘方法
this.submit = true;
showTag.value = true
commentInput()
}
function commentInput() {
submit.value = true
setTimeout(() => {
this.focus = true;
focus.value = true
}, 50)
},
// 输入框失去焦点
blur() {
this.focus = false;
},
// 输入框聚焦
focusOn() {
},
toArticle(item) {
}
function blur() {
focus.value = false
}
function focusOnInput() {
// 输入框聚焦
}
function toArticle(item) {
uni.navigateTo({
url:'/uni_modules/uni-cms-article/pages/detail/detail?id=' + item.article_id._id
url: '/uni_modules/uni-cms-article/pages/detail/detail?id=' + item.article_id._id
})
}
},
watch: {
'userInfo._id'(val, oldVal) {
if (val && val !== oldVal && this.$refs.udb) {
this.refresh()
}
}
},
async onReady() {
// #ifdef MP
this.initNavBarSize() // 初始化导航栏大小
// #endif
/* 可用窗口高度 - 搜索框高 - 状态栏高 */
this.listHeight = uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - this
.navBarHeight + 'px'; // 计算列表高度
},
onLoad() {
},
function stopPrevent() {}
onReady(() => {
// #ifdef MP
initNavBarSize()
// #endif
// #ifdef MP
uni.onKeyboardHeightChange(res => {
KeyboardHeight.value = res.height
})
// #endif
listHeight.value = uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - navBarHeight.value + 'px'
})
watch(() => userInfo.value._id, (val, oldVal) => {
if (val && val !== oldVal && udbRef.value) {
refresh()
}
})
return {
udbRef,
emptyAvatar,
showRefresh,
listHeight,
listData,
loadType,
placeholder,
commentReq,
pUser,
showTag,
focus,
submit,
KeyboardHeight,
userInfo,
queryUserId,
colList,
getNewItem,
listLoad,
publishTime,
refresh,
loadMore,
onqueryerror,
closeInput,
tagClose,
selectAdd,
replyAdd,
reply,
blur,
toArticle,
stopPrevent
}
}
}
</script>
@@ -540,9 +546,8 @@
.p_f {
display: flex;
}
.comment-add {
position: fixed;
background-color: #FFFFFF;
@@ -552,7 +557,7 @@
transition: .3s;
-webkit-transition: .3s;
}
.comment-submit-box {
position: fixed;
display: flex;