fix:评论完善加载逻辑
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -80,6 +80,7 @@ declare module 'vue' {
|
|||||||
Xxxx: typeof import('./src/components/xxxx/index.vue')['default']
|
Xxxx: typeof import('./src/components/xxxx/index.vue')['default']
|
||||||
}
|
}
|
||||||
export interface GlobalDirectives {
|
export interface GlobalDirectives {
|
||||||
|
vInfiniteScroll: typeof import('element-plus/es')['ElInfiniteScroll']
|
||||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,12 @@ $color-white: #fff;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.comment-loading-status{
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 子评论卡片样式
|
// 3. 子评论卡片样式
|
||||||
.sub-container {
|
.sub-container {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<div class="comment-app">
|
<div class="comment-app">
|
||||||
<!-- 评论列表 -->
|
<!-- 评论列表 -->
|
||||||
<div class="comment-list">
|
<div class="comment-list">
|
||||||
|
<el-scrollbar @end-reached="loadMore">
|
||||||
<!-- 骨架屏 -->
|
<!-- 骨架屏 -->
|
||||||
<div v-if="loading">
|
<div v-if="loading">
|
||||||
<div v-for="n in 2" :key="'skeleton-' + n" class="comment-group">
|
<div v-for="n in 2" :key="'skeleton-' + n" class="comment-group">
|
||||||
@@ -149,28 +150,31 @@
|
|||||||
<div class="expand-line"></div>
|
<div class="expand-line"></div>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="item.children.length < item.childrenCount"
|
v-if="!item.showAllReplies"
|
||||||
link
|
link
|
||||||
@click="loadReplies(item)"
|
@click="item.showAllReplies = true"
|
||||||
>
|
>
|
||||||
<template v-if="item.children.length === 0">
|
|
||||||
展开 {{ item.childrenCount }} 条回复
|
展开 {{ item.childrenCount }} 条回复
|
||||||
</template>
|
<el-icon><ArrowDown /></el-icon>
|
||||||
<template v-else>
|
|
||||||
更多
|
|
||||||
{{ item.childrenCount - item.children.length }} 条回复
|
|
||||||
</template>
|
|
||||||
<el-icon v-if="!loading"><ArrowDown /></el-icon>
|
|
||||||
<el-icon v-else class="is-loading"><Loading /></el-icon>
|
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="item.children.length > 0"
|
v-else-if="item.children.length < item.childrenCount"
|
||||||
|
link
|
||||||
|
:loading="item.loading"
|
||||||
|
@click="loadReplies(item)"
|
||||||
|
>
|
||||||
|
更多
|
||||||
|
{{ item.childrenCount - item.children.length }} 条回复
|
||||||
|
<el-icon v-if="!item.loading"><ArrowDown /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
v-if="item.showAllReplies"
|
||||||
link
|
link
|
||||||
@click="collapseReplies(item)"
|
@click="collapseReplies(item)"
|
||||||
>
|
>
|
||||||
收起
|
收起 <el-icon><ArrowUp /></el-icon>
|
||||||
<el-icon><ArrowUp /></el-icon>
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -178,6 +182,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<div v-if="loadingMore" class="comment-loading-status">
|
||||||
|
<el-icon class="is-loading" :size="24"><Loading /></el-icon>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 评论模块 -->
|
<!-- 评论模块 -->
|
||||||
@@ -206,7 +215,7 @@
|
|||||||
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
|
<!-- <el-button link title="附件"><el-icon><Paperclip /></el-icon></el-button> -->
|
||||||
|
|
||||||
<!-- 表情功能 -->
|
<!-- 表情功能 -->
|
||||||
<emoji-picker @select="(e) => onSelectEmoji(e, 'main')" />
|
<emoji-picker @select="(e) => onSelectEmoji(e)" />
|
||||||
</div>
|
</div>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button
|
<el-button
|
||||||
@@ -322,14 +331,6 @@ const openReply = (target, group) => {
|
|||||||
if (!mainInput.value.includes(mentionStr)) {
|
if (!mainInput.value.includes(mentionStr)) {
|
||||||
mainInput.value = mentionStr + mainInput.value;
|
mainInput.value = mentionStr + mainInput.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 聚焦到输入框
|
|
||||||
nextTick(() => {
|
|
||||||
const textarea = document.querySelector(".fixed-publisher textarea");
|
|
||||||
textarea?.focus();
|
|
||||||
// 飞书细节:光标移到最后
|
|
||||||
textarea.selectionStart = textarea.value.length;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 删除回复-删除评论
|
// 删除回复-删除评论
|
||||||
@@ -350,11 +351,8 @@ const deleteMainComment = (target) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// emoji输入框选择
|
// emoji输入框选择
|
||||||
const onSelectEmoji = (emoji, type) => {
|
const onSelectEmoji = (emoji) => {
|
||||||
console.log("emoji", emoji, type);
|
|
||||||
if (type === "main") {
|
|
||||||
mainInput.value += emoji;
|
mainInput.value += emoji;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 取消评论
|
// 取消评论
|
||||||
@@ -374,7 +372,7 @@ const userList = [
|
|||||||
|
|
||||||
// TODO:@ 获取选中的用户信息
|
// TODO:@ 获取选中的用户信息
|
||||||
const onUserMentioned = (user) => {
|
const onUserMentioned = (user) => {
|
||||||
console.log("Mentioned:", user.nickname);
|
console.log("Mentioned:", user);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -470,15 +468,40 @@ const updateUIAfterSend = (type, params) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// TODO:展开
|
// TODO:展开
|
||||||
const loadingReply = ref(false);
|
const MOCK_REPLIES_POOL = Array.from({ length: 20 }, (_, i) => ({
|
||||||
|
id: 200 + i,
|
||||||
|
content: `这是模拟的第 ${i + 1} 条回复内容,用于测试分页加载。`,
|
||||||
|
createTime: Date.now() - i * 100000,
|
||||||
|
employee: {
|
||||||
|
id: 10 + i,
|
||||||
|
name: `同事${i + 1}`,
|
||||||
|
avatar: "",
|
||||||
|
},
|
||||||
|
reply: null,
|
||||||
|
}));
|
||||||
const loadReplies = async (item) => {
|
const loadReplies = async (item) => {
|
||||||
if(!loadingReply.value) return;
|
if (item.loading) return;
|
||||||
loadingReply.value = true;
|
item.loading = true;
|
||||||
try {
|
try {
|
||||||
// 后端获取最终的数据信息
|
// 后端获取最终的数据信息
|
||||||
// const res = await xxxxxx()
|
// const res = await xxxxxx()
|
||||||
|
// 模拟延迟
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||||
const res = [];
|
const res = [];
|
||||||
const combined = [...(item.localReplies || []), ...res, ...item.children];
|
|
||||||
|
// 模拟的数据
|
||||||
|
const currentLength = item.children.length;
|
||||||
|
const pageSize = 3;
|
||||||
|
const nextBatch = MOCK_REPLIES_POOL.slice(
|
||||||
|
currentLength,
|
||||||
|
currentLength + pageSize
|
||||||
|
);
|
||||||
|
const combined = [
|
||||||
|
...(item.localReplies || []),
|
||||||
|
...res,
|
||||||
|
...item.children,
|
||||||
|
...nextBatch,
|
||||||
|
];
|
||||||
item.children = combined.filter(
|
item.children = combined.filter(
|
||||||
(v, i, a) => a.findIndex((t) => t.id === v.id) === i
|
(v, i, a) => a.findIndex((t) => t.id === v.id) === i
|
||||||
);
|
);
|
||||||
@@ -487,14 +510,17 @@ const loadReplies = async (item) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
console.log("error", error);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
item.loading = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 收起
|
// 收起
|
||||||
const collapseReplies = (item) => {
|
const collapseReplies = (item) => {
|
||||||
item.showAllReplies = false;
|
item.showAllReplies = false;
|
||||||
// nextTick(() => scrollIntoView(item.id));
|
nextTick(() => {
|
||||||
|
const el = document.getElementById(`comment-${item.id}`);
|
||||||
|
el?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -523,6 +549,56 @@ const parseMention = (text, atUsers = userList) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME:加载更多
|
||||||
|
const page = ref(1);
|
||||||
|
const loadingMore = ref(false); // 分页加载状态
|
||||||
|
const noMore = ref(false); // 是否全部加载完毕
|
||||||
|
const totalCount = ref(20); // 模拟后端返回的总评论数
|
||||||
|
const disabled = computed(() => loadingMore.value || noMore.value);
|
||||||
|
const loadMore = async () => {
|
||||||
|
if (disabled.value) return;
|
||||||
|
|
||||||
|
loadingMore.value = true;
|
||||||
|
|
||||||
|
// 模拟接口请求
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 模拟构造下一页 Mock 数据
|
||||||
|
const nextBatch = [
|
||||||
|
{
|
||||||
|
id: Date.now(),
|
||||||
|
content: `这是第 ${page.value + 1} 页的评论内容`,
|
||||||
|
createTime: new Date().toISOString(),
|
||||||
|
employee: { id: 88, name: "滚动测试员", avatar: "" },
|
||||||
|
childrenCount: 2,
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: Date.now() + 1,
|
||||||
|
content: "多拉出一条数据",
|
||||||
|
createTime: new Date().toISOString(),
|
||||||
|
employee: { id: 89, name: "张三", avatar: "" },
|
||||||
|
childrenCount: 0,
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 合并数据
|
||||||
|
commentData.value.push(...nextBatch);
|
||||||
|
page.value++;
|
||||||
|
|
||||||
|
// 判断是否加载完(模拟)
|
||||||
|
if (commentData.value.length >= totalCount.value) {
|
||||||
|
noMore.value = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("加载失败", error);
|
||||||
|
} finally {
|
||||||
|
loadingMore.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<el-form-item label="域名">
|
<el-form-item label="域名">
|
||||||
<el-input v-model="form.domain" placeholder="example.com">
|
<el-input v-model="form.domain" placeholder="example.com">
|
||||||
<template #prefix
|
<template #prefix
|
||||||
><el-icon><component is="Global" /></el-icon
|
><el-icon><component :is="'Global'" /></el-icon
|
||||||
></template>
|
></template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -81,9 +81,9 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="org-ganization-footer">
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="dialogVisible = false" round>取消</el-button>
|
||||||
<el-button type="primary" class="btn-confirm" @click="handleSubmit"
|
<el-button type="primary" class="btn-confirm" round @click="handleSubmit"
|
||||||
>确认创建</el-button
|
>确认创建</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,6 +164,15 @@ const handleSubmit = () => {};
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.org-ganization-footer{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.el-button{
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -143,44 +143,7 @@ const addDynamicRoutes = async () => {
|
|||||||
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
|
// 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
|
||||||
let allRoutes:any[] = [];
|
let allRoutes:any[] = [];
|
||||||
if (userStore.isBackendUser) {
|
if (userStore.isBackendUser) {
|
||||||
// const backendResponse = await getRouteMenus();
|
const backendResponse = await getRouteMenus();
|
||||||
const backendResponse = [
|
|
||||||
{
|
|
||||||
"name": "字典管理",
|
|
||||||
"code": "dict",
|
|
||||||
"icon": "OfficeBuilding",
|
|
||||||
"metadata": null,
|
|
||||||
"children": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "组织管理",
|
|
||||||
"code": "origanization",
|
|
||||||
"icon": "OfficeBuilding",
|
|
||||||
"metadata": null,
|
|
||||||
"children": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "人员管理",
|
|
||||||
"code": "personnel",
|
|
||||||
"icon": "OfficeBuilding",
|
|
||||||
"metadata": null,
|
|
||||||
"children": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "权限管理",
|
|
||||||
"code": "permission",
|
|
||||||
"icon": "OfficeBuilding",
|
|
||||||
"metadata": null,
|
|
||||||
"children": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "流程管理",
|
|
||||||
"code": "flow",
|
|
||||||
"icon": "OfficeBuilding",
|
|
||||||
"metadata": null,
|
|
||||||
"children": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
allRoutes = [
|
allRoutes = [
|
||||||
{
|
{
|
||||||
code: "stage",
|
code: "stage",
|
||||||
|
|||||||
Reference in New Issue
Block a user