开源版重新整理提交
123
README.md
@@ -13,7 +13,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 平台简介
|
## 平台简介
|
||||||
* 本仓库为前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [ts](https://www.tslang.cn) 版本。
|
* 本仓库为红薯用户端:前端技术栈 [Vue3](https://v3.cn.vuejs.org) + [Element Plus](https://element-plus.org/zh-CN) + [ts](https://www.tslang.cn) 版本。
|
||||||
* 配套后端代码仓库地址 [HongShu](https://gitee.com/Maverick_Ma/hongshu.git)
|
* 配套后端代码仓库地址 [HongShu](https://gitee.com/Maverick_Ma/hongshu.git)
|
||||||
|
|
||||||
## 前端运行
|
## 前端运行
|
||||||
@@ -25,129 +25,12 @@ git clone https://gitee.com/Maverick_Ma/hongshu-web.git
|
|||||||
cd hongshu-web
|
cd hongshu-web
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
yarn --registry=https://registry.npmmirror.com
|
npm install
|
||||||
|
|
||||||
# 启动服务
|
# 启动服务
|
||||||
yarn dev
|
run run dev
|
||||||
|
|
||||||
# 构建测试环境 yarn build:stage
|
# 构建测试环境 yarn build:stage
|
||||||
# 构建生产环境 yarn build:prod
|
# 构建生产环境 yarn build:prod
|
||||||
# 前端访问地址 http://localhost:80
|
# 前端访问地址 http://localhost:80
|
||||||
```
|
```
|
||||||
|
|
||||||
## 用户端内置功能
|
|
||||||
1. 笔记:瀑布流按分类展示笔记,懒加载笔记图片
|
|
||||||
2. 搜索:使用 ElasticSearch 做关键词搜索高亮查询
|
|
||||||
3. 动态:展示个人和好友动态
|
|
||||||
4. 消息:使用 WebSocket 做私信聊天和消息通知,用户发送消息实时通知、消息页面实时展示当前未读消息数量提醒
|
|
||||||
5. 发布:发布和修改笔记功能,使用七牛云oss对象存储图片
|
|
||||||
6. 用户信息:展示当前用户发布、点赞和收藏的笔记
|
|
||||||
7. 双 Token 登录机制、无感刷新,使用 Redis 做对象缓存
|
|
||||||
|
|
||||||
## 管理端内置功能
|
|
||||||
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
|
||||||
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
|
||||||
3. 岗位管理:配置系统用户所属担任职务。
|
|
||||||
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
|
||||||
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
|
||||||
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
|
||||||
7. 参数管理:对系统动态配置常用参数。
|
|
||||||
8. 通知公告:系统通知公告信息发布维护。
|
|
||||||
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
|
||||||
10. 登录日志:系统登录日志记录查询包含登录异常。
|
|
||||||
11. 在线用户:当前系统中活跃用户状态监控。
|
|
||||||
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
|
|
||||||
13. 系统接口:根据业务代码自动生成相关的api接口文档。
|
|
||||||
14. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
|
|
||||||
15. 缓存监控:对系统的缓存信息查询,命令统计等。
|
|
||||||
16. 在线构建器:拖动表单元素生成相应的HTML代码。
|
|
||||||
17. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
|
|
||||||
|
|
||||||
## 2.0版本实现功能
|
|
||||||
1. 添加移动端
|
|
||||||
2. 重构实现 SpringCloud 微服务架构
|
|
||||||
3. 加入商城购物功能
|
|
||||||
4. 加入推荐算法,优化内容推荐和用户推荐功能
|
|
||||||
5. 使用 MQ+Redis 优化点赞、收藏、浏览功能
|
|
||||||
6. 支持七牛云、阿里、腾讯、Minio等多种oss对象存储方式
|
|
||||||
7. 笔记内容支持视频和live图
|
|
||||||
|
|
||||||
#### ⚠️如有【项目问题】或【部署需求】可联系微信:coder_xiaomage
|
|
||||||
|
|
||||||
## 演示站(2.0版)
|
|
||||||
- web端 ➡️ [点我体验](http://47.95.205.22)
|
|
||||||
- admin端 ➡️ [点我体验](http://47.95.205.22/admin/)
|
|
||||||
- app端 ➡️ [点我体验](http://47.95.205.22/app/)
|
|
||||||
- 文档及资料会暂时放到我的个人博客:[点我进入](https://mayongjian.cn)
|
|
||||||
* 由于服务器资源有限,首次加载可能缓慢一些。
|
|
||||||
* 同时为优化服务器也感谢小伙伴们打赏支持❤️。
|
|
||||||
<img src="src/assets/images/reward.png" style="width: 50px heihgt: 50px"/>
|
|
||||||
|
|
||||||
## 视频演示
|
|
||||||
➡️ [点击查看](https://www.bilibili.com/video/BV1QP8dekEGq/?spm_id_from=333.999.list.card_archive.click&vd_source=ec9224821314432ac6e12dc7d500d74b)
|
|
||||||
|
|
||||||
|
|
||||||
## 演示图
|
|
||||||
### - web端:
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/web/web-login.png"/></td>
|
|
||||||
<td><img src="src/assets/images/web/web-dashboard.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/web/web-search.png"/></td>
|
|
||||||
<td><img src="src/assets/images/web/web-trends.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/web/web-message.png"/></td>
|
|
||||||
<td><img src="src/assets/images/web/web-follow.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/web/web-publish.png"/></td>
|
|
||||||
<td><img src="src/assets/images/web/web-user.png"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### - admin端:
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/admin/admin-login.png"/></td>
|
|
||||||
<td><img src="src/assets/images/admin/admin-data.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/admin/admin-category.png"/></td>
|
|
||||||
<td><img src="src/assets/images/admin/admin-member.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/admin/admin-note.png"/></td>
|
|
||||||
<td><img src="src/assets/images/admin/admin-album.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/admin/admin-comment.png"/></td>
|
|
||||||
<td><img src="src/assets/images/admin/admin-log.png"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### - app端:
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/app/app-login.png"/></td>
|
|
||||||
<td><img src="src/assets/images/app/app-index.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/app/app-trend.png"/></td>
|
|
||||||
<td><img src="src/assets/images/app/app-message.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/app/app-user.png"/></td>
|
|
||||||
<td><img src="src/assets/images/app/app-follow.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/app/app-hot.png"/></td>
|
|
||||||
<td><img src="src/assets/images/app/app-main.png"/></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="src/assets/images/app/app-search.png"/></td>
|
|
||||||
<td><img src="src/assets/images/app/app-push.png"/></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
@@ -11,8 +11,10 @@
|
|||||||
"lint:prettier": "prettier --write \"**/*.{js,ts,json,css,less,scss,vue,html,md}\""
|
"lint:prettier": "prettier --write \"**/*.{js,ts,json,css,less,scss,vue,html,md}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@lhlyu/vue-virtual-waterfall": "^1.0.2",
|
"@lhlyu/vue-virtual-waterfall": "^1.0.2",
|
||||||
"@opentiny/vue": "3",
|
"@opentiny/vue": "3",
|
||||||
|
"@vueuse/core": "^12.3.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"async-validator": "^4.2.5",
|
"async-validator": "^4.2.5",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.5.1",
|
||||||
|
0
src/assets/images/3.png
Executable file → Normal file
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 2.0 MiB |
Before Width: | Height: | Size: 781 KiB |
Before Width: | Height: | Size: 440 KiB |
Before Width: | Height: | Size: 416 KiB |
Before Width: | Height: | Size: 635 KiB |
Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 853 KiB |
Before Width: | Height: | Size: 862 KiB |
Before Width: | Height: | Size: 3.6 MiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 939 KiB |
Before Width: | Height: | Size: 416 KiB |
Before Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 986 KiB |
Before Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 731 KiB |
Before Width: | Height: | Size: 5.7 MiB |
Before Width: | Height: | Size: 467 KiB |
Before Width: | Height: | Size: 3.6 MiB |
Before Width: | Height: | Size: 326 KiB |
Before Width: | Height: | Size: 304 KiB |
Before Width: | Height: | Size: 5.0 MiB |
Before Width: | Height: | Size: 1016 KiB |
Before Width: | Height: | Size: 3.7 MiB |
@@ -102,7 +102,7 @@ import { Waterfall } from "vue-waterfall-plugin-next";
|
|||||||
import "vue-waterfall-plugin-next/dist/style.css";
|
import "vue-waterfall-plugin-next/dist/style.css";
|
||||||
import { ref, onMounted, watch } from "vue";
|
import { ref, onMounted, watch } from "vue";
|
||||||
import { getTrendByUser } from "@/api/user";
|
import { getTrendByUser } from "@/api/user";
|
||||||
import Main from "@/pages/main/main.vue";
|
import Main from "@/views/main/main.vue";
|
||||||
import { options } from "@/constant/constant";
|
import { options } from "@/constant/constant";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { useUserStore } from "@/store/userStore";
|
import { useUserStore } from "@/store/userStore";
|
||||||
|
@@ -1,462 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="feeds-page">
|
|
||||||
<div class="channel-container">
|
|
||||||
<div class="scroll-container channel-scroll-container">
|
|
||||||
<div class="content-container">
|
|
||||||
<div :class="categoryClass == '0' ? 'channel active' : 'channel'" @click="getNoteList">推荐</div>
|
|
||||||
<div
|
|
||||||
:class="categoryClass == item.id ? 'channel active' : 'channel'"
|
|
||||||
v-for="item in categoryList"
|
|
||||||
:key="item.id"
|
|
||||||
@click="getNoteListByCategory(item.id)"
|
|
||||||
>
|
|
||||||
{{ item.title }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="loading-container"></div>
|
|
||||||
<div class="feeds-container" v-infinite-scroll="loadMoreData" :infinite-scroll-distance="50">
|
|
||||||
<div class="feeds-loading-top animate__animated animate__zoomIn animate__delay-0.5s" v-show="topLoading">
|
|
||||||
<Loading style="width: 1.2em; height: 1.2em"></Loading>
|
|
||||||
</div>
|
|
||||||
<Waterfall
|
|
||||||
:list="noteList"
|
|
||||||
:width="options.width"
|
|
||||||
:gutter="options.gutter"
|
|
||||||
:hasAroundGutter="options.hasAroundGutter"
|
|
||||||
:animation-effect="options.animationEffect"
|
|
||||||
:animation-duration="options.animationDuration"
|
|
||||||
:animation-delay="options.animationDelay"
|
|
||||||
:breakpoints="options.breakpoints"
|
|
||||||
style="min-width: 740px"
|
|
||||||
>
|
|
||||||
<template #item="{ item }">
|
|
||||||
<el-skeleton style="width: 240px" :loading="!item.isLoading" animated>
|
|
||||||
<template #template>
|
|
||||||
<el-image
|
|
||||||
:src="item.noteCover"
|
|
||||||
:style="{
|
|
||||||
width: '240px',
|
|
||||||
maxHeight: '300px',
|
|
||||||
height: item.noteCoverHeight + 'px',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}"
|
|
||||||
@load="handleLoad(item)"
|
|
||||||
></el-image>
|
|
||||||
<div style="padding: 14px">
|
|
||||||
<el-skeleton-item variant="h3" style="width: 100%" />
|
|
||||||
<div style="display: flex; align-items: center; margin-top: 2px; height: 16px">
|
|
||||||
<el-skeleton style="--el-skeleton-circle-size: 20px">
|
|
||||||
<template #template>
|
|
||||||
<el-skeleton-item variant="circle" />
|
|
||||||
</template>
|
|
||||||
</el-skeleton>
|
|
||||||
<el-skeleton-item variant="text" style="margin-left: 10px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default>
|
|
||||||
<div class="card" style="max-width: 240px">
|
|
||||||
<el-image
|
|
||||||
:src="item.noteCover"
|
|
||||||
:style="{
|
|
||||||
width: '240px',
|
|
||||||
maxHeight: '300px',
|
|
||||||
height: item.noteCoverHeight + 'px',
|
|
||||||
borderRadius: '8px',
|
|
||||||
}"
|
|
||||||
fit="cover"
|
|
||||||
@click="toMain(item.id)"
|
|
||||||
></el-image>
|
|
||||||
<div class="footer">
|
|
||||||
<a class="title">
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
</a>
|
|
||||||
<div class="author-wrapper">
|
|
||||||
<a class="author">
|
|
||||||
<img class="author-avatar" :src="item.avatar" />
|
|
||||||
<span class="name">{{ item.username }}</span>
|
|
||||||
</a>
|
|
||||||
<span class="like-wrapper like-active">
|
|
||||||
<i
|
|
||||||
class="iconfont icon-follow-fill"
|
|
||||||
:style="{ width: '1em', height: '1em', color: item.isLike ? 'red' : 'black' }"
|
|
||||||
v-if="item.isLike"
|
|
||||||
>
|
|
||||||
</i>
|
|
||||||
<i class="iconfont icon-follow" style="width: 1em; height: 1em" v-else></i>
|
|
||||||
<span class="count">{{ item.likeCount }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-skeleton>
|
|
||||||
</template>
|
|
||||||
</Waterfall>
|
|
||||||
|
|
||||||
<div class="feeds-loading" v-show="!isEnd">
|
|
||||||
<Loading style="width: 1.2em; height: 1.2em"></Loading>
|
|
||||||
</div>
|
|
||||||
<div class="feeds-end" v-show="isEnd">······ 已经到底了 ·····</div>
|
|
||||||
</div>
|
|
||||||
<FloatingBtn @click-refresh="refresh"></FloatingBtn>
|
|
||||||
<Main
|
|
||||||
v-show="mainShow"
|
|
||||||
:nid="nid"
|
|
||||||
:nowTime="new Date()"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-main="close"
|
|
||||||
></Main>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Waterfall } from "vue-waterfall-plugin-next";
|
|
||||||
import "vue-waterfall-plugin-next/dist/style.css";
|
|
||||||
import { ref, watch } from "vue";
|
|
||||||
import { getRecommendNote, getNoteByDTO, addRecord } from "@/api/search";
|
|
||||||
import { getCategoryTreeData } from "@/api/category";
|
|
||||||
import type { NoteDTO, NoteSearch } from "@/type/note";
|
|
||||||
import type { Category } from "@/type/category";
|
|
||||||
import Main from "@/pages/main/main.vue";
|
|
||||||
import FloatingBtn from "@/components/FloatingBtn.vue";
|
|
||||||
import { options } from "@/constant/constant";
|
|
||||||
import { useSearchStore } from "@/store/searchStore";
|
|
||||||
import Loading from "@/components/Loading.vue";
|
|
||||||
import { refreshTab } from "@/utils/util";
|
|
||||||
const searchStore = useSearchStore();
|
|
||||||
const topLoading = ref(false);
|
|
||||||
const noteList = ref<Array<NoteSearch>>([]);
|
|
||||||
const categoryList = ref<Array<Category>>([]);
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 20;
|
|
||||||
const noteTotal = ref(0);
|
|
||||||
const categoryClass = ref("0");
|
|
||||||
const mainShow = ref(false);
|
|
||||||
const nid = ref("");
|
|
||||||
const isEnd = ref(false);
|
|
||||||
const noteDTO = ref<NoteDTO>({
|
|
||||||
keyword: "",
|
|
||||||
type: 0,
|
|
||||||
cid: "",
|
|
||||||
cpid: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [searchStore.seed],
|
|
||||||
() => {
|
|
||||||
noteDTO.value.keyword = searchStore.keyWord;
|
|
||||||
noteDTO.value.cpid = "";
|
|
||||||
categoryClass.value = "0";
|
|
||||||
getNoteListByKeyword();
|
|
||||||
addRecord(searchStore.keyWord);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const toMain = (noteId: string) => {
|
|
||||||
// router.push({ name: "main", state: { nid: nid } });
|
|
||||||
nid.value = noteId;
|
|
||||||
mainShow.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
mainShow.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLoad = (item: any) => {
|
|
||||||
item.isLoading = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refresh = () => {
|
|
||||||
// 使用回调函数优化代码
|
|
||||||
refreshTab(() => {
|
|
||||||
topLoading.value = true;
|
|
||||||
console.log(111);
|
|
||||||
setTimeout(() => {
|
|
||||||
currentPage.value = 1;
|
|
||||||
noteList.value = [];
|
|
||||||
getNoteList();
|
|
||||||
topLoading.value = false;
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMoreData = () => {
|
|
||||||
if (noteList.value.length >= noteTotal.value) {
|
|
||||||
isEnd.value = true;
|
|
||||||
return; // 如果已经加载完所有数据,则不再请求
|
|
||||||
}
|
|
||||||
currentPage.value += 1;
|
|
||||||
if (noteDTO.value.cpid === "" && noteDTO.value.keyword == "") {
|
|
||||||
getRecommendNote(currentPage.value, pageSize).then((res: any) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setData = (res: any) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
noteTotal.value = total;
|
|
||||||
if (records.length === 0) {
|
|
||||||
isEnd.value = true;
|
|
||||||
} else {
|
|
||||||
noteList.value.push(...records);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteList = async () => {
|
|
||||||
categoryClass.value = "0";
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getRecommendNote(currentPage.value, pageSize).then((res: any) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteListByCategory = (id: string) => {
|
|
||||||
categoryClass.value = id;
|
|
||||||
noteDTO.value.cpid = id;
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteListByKeyword = () => {
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCategoryData = () => {
|
|
||||||
getCategoryTreeData().then((res: any) => {
|
|
||||||
categoryList.value = res.data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
getCategoryData();
|
|
||||||
getNoteList();
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.feeds-page {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 24px;
|
|
||||||
padding-top: 72px;
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
.channel-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
|
|
||||||
.channel-scroll-container {
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
background-color: transparent;
|
|
||||||
width: calc(100vw - 24px);
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
height: 40px;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: 72px;
|
|
||||||
|
|
||||||
.content-container::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
display: flex;
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-weight: 600;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 999px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel {
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.2); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feeds-container {
|
|
||||||
position: relative;
|
|
||||||
transition: width 0.5s;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.feeds-loading {
|
|
||||||
margin: 3vh;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feeds-loading-top {
|
|
||||||
text-align: center;
|
|
||||||
line-height: 6vh;
|
|
||||||
height: 6vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noteImg {
|
|
||||||
width: 240px;
|
|
||||||
max-height: 300px;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
word-break: break-all;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 140%;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 20px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
font-size: 12px;
|
|
||||||
transition: color 1s;
|
|
||||||
|
|
||||||
.author {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: inherit;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-right: 12px;
|
|
||||||
|
|
||||||
.author-avatar {
|
|
||||||
margin-right: 6px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 20px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
flex-shrink: 0;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.like-wrapper {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.count {
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-btn-sets {
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 40px;
|
|
||||||
grid-gap: 8px;
|
|
||||||
gap: 8px;
|
|
||||||
right: 24px;
|
|
||||||
bottom: 24px;
|
|
||||||
|
|
||||||
.back-top {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 100px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
// transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reload {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
box-shadow:
|
|
||||||
0 2px 8px 0 rgba(0, 0, 0, 0.1),
|
|
||||||
0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 100px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
//transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.feeds-end {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 50px;
|
|
||||||
color: #999;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-top: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,346 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container" v-infinite-scroll="loadMoreData" :infinite-scroll-distance="50">
|
|
||||||
<div v-if="isLogin">
|
|
||||||
<ul class="trend-container">
|
|
||||||
<li class="trend-item" v-for="(item, index) in trendData" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.uid)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span>{{ item.time }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-content" @click="toMain(item.nid)">
|
|
||||||
{{ item.content }}
|
|
||||||
</div>
|
|
||||||
<div class="interaction-imgs" @click="toMain(item.nid)">
|
|
||||||
<!-- 限制最多显示三张图片 -->
|
|
||||||
<div
|
|
||||||
class="details-box"
|
|
||||||
v-for="(url, index) in item.imgUrls.slice(0, 3)"
|
|
||||||
:key="index"
|
|
||||||
style="position: relative"
|
|
||||||
>
|
|
||||||
<el-image
|
|
||||||
v-if="!item.isLoading"
|
|
||||||
:src="url"
|
|
||||||
@load="handleLoad(item)"
|
|
||||||
style="height: 230px; width: 100%"
|
|
||||||
></el-image>
|
|
||||||
<el-image
|
|
||||||
v-else
|
|
||||||
:src="url"
|
|
||||||
class="note-img animate__animated animate__fadeIn animate__delay-0.5s"
|
|
||||||
fit="cover"
|
|
||||||
style="height: 230px; width: 100%"
|
|
||||||
></el-image>
|
|
||||||
|
|
||||||
<!-- 在第三张图片上显示覆盖标识 -->
|
|
||||||
<div v-if="index === 2 && item.imgUrls.length > 3" class="overlay">
|
|
||||||
<span class="more-text">+{{ item.imgUrls.length - 3 }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-footer">
|
|
||||||
<div class="icon-item">
|
|
||||||
<i
|
|
||||||
class="iconfont icon-follow-fill"
|
|
||||||
:style="{ width: '1em', height: '1em', color: item.isLike ? 'red' : 'black' }"
|
|
||||||
@click="like(item.nid, item.uid, index, -1)"
|
|
||||||
v-if="item.isLike"
|
|
||||||
>
|
|
||||||
</i>
|
|
||||||
<i
|
|
||||||
class="iconfont icon-follow"
|
|
||||||
style="width: 1em; height: 1em"
|
|
||||||
@click="like(item.nid, item.uid, index, 1)"
|
|
||||||
v-else
|
|
||||||
></i
|
|
||||||
><span class="count">{{ item.likeCount }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="icon-item">
|
|
||||||
<ChatRound style="width: 0.9em; height: 0.9em" /><span class="count">{{ item.commentCount }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="icon-item">
|
|
||||||
<More style="width: 1em; height: 1em" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<FloatingBtn @click-refresh="refresh"></FloatingBtn>
|
|
||||||
<Main
|
|
||||||
v-show="mainShow"
|
|
||||||
:nid="nid"
|
|
||||||
:nowTime="new Date()"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-main="close"
|
|
||||||
></Main>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<el-empty description="用户未登录" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ChatRound, More } from "@element-plus/icons-vue";
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { getFollowTrend } from "@/api/follower";
|
|
||||||
import { formateTime, refreshTab } from "@/utils/util";
|
|
||||||
import FloatingBtn from "@/components/FloatingBtn.vue";
|
|
||||||
import Main from "@/pages/main/main.vue";
|
|
||||||
import type { LikeOrCollectionDTO } from "@/type/likeOrCollection";
|
|
||||||
import { likeOrCollectionByDTO } from "@/api/likeOrCollection";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = ref(5);
|
|
||||||
const trendData = ref<Array<any>>([]);
|
|
||||||
const trendTotal = ref(0);
|
|
||||||
const topLoading = ref(false);
|
|
||||||
const mainShow = ref(false);
|
|
||||||
const nid = ref("");
|
|
||||||
const likeOrCollectionDTO = ref<LikeOrCollectionDTO>({
|
|
||||||
likeOrCollectionId: "",
|
|
||||||
publishUid: "",
|
|
||||||
type: 0,
|
|
||||||
});
|
|
||||||
const isLogin = ref(false);
|
|
||||||
|
|
||||||
const handleLoad = (item: any) => {
|
|
||||||
item.isLoading = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
//router.push({ name: "user", state: { uid: uid } });
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFollowTrends = () => {
|
|
||||||
getFollowTrend(currentPage.value, pageSize.value).then((res) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
console.log(records, total);
|
|
||||||
records.forEach((item: any) => {
|
|
||||||
item.time = formateTime(item.time);
|
|
||||||
trendData.value.push(item);
|
|
||||||
});
|
|
||||||
trendTotal.value = total;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMoreData = () => {
|
|
||||||
currentPage.value += 1;
|
|
||||||
getFollowTrends();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toMain = (noteId: string) => {
|
|
||||||
nid.value = noteId;
|
|
||||||
mainShow.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = (nid: string, val: any) => {
|
|
||||||
const index = trendData.value.findIndex((item) => item.nid === nid);
|
|
||||||
console.log("---val", val, index);
|
|
||||||
const _data = trendData.value[index];
|
|
||||||
if (_data.isLike != val.isLike) {
|
|
||||||
_data.isLike = val.isLike;
|
|
||||||
_data.likeCount += val.isLike ? 1 : -1;
|
|
||||||
}
|
|
||||||
if (val.isComment) {
|
|
||||||
_data.commentCount += 1;
|
|
||||||
}
|
|
||||||
mainShow.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const refresh = () => {
|
|
||||||
refreshTab(() => {
|
|
||||||
topLoading.value = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
currentPage.value = 1;
|
|
||||||
trendData.value = [];
|
|
||||||
getFollowTrends();
|
|
||||||
topLoading.value = false;
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const like = (nid: string, uid: string, index: number, val: number) => {
|
|
||||||
likeOrCollectionDTO.value.likeOrCollectionId = nid;
|
|
||||||
likeOrCollectionDTO.value.publishUid = uid;
|
|
||||||
likeOrCollectionDTO.value.type = 1;
|
|
||||||
likeOrCollectionByDTO(likeOrCollectionDTO.value).then(() => {
|
|
||||||
if (val < 0 && trendData.value[index].likeCount == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
trendData.value[index].isLike = val == 1;
|
|
||||||
trendData.value[index].likeCount += val;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
isLogin.value = userStore.isLogin();
|
|
||||||
getFollowTrends();
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.details-box {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.more-text {
|
|
||||||
color: white;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 24px;
|
|
||||||
padding-top: 72px;
|
|
||||||
width: 67%;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.feeds-loading {
|
|
||||||
margin: 3vh;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trend-container {
|
|
||||||
.trend-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 24px;
|
|
||||||
max-width: 100vw;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-imgs {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.details-box {
|
|
||||||
width: 25%;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin: 8px 12px 0 0;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.note-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 230px;
|
|
||||||
display: flex;
|
|
||||||
border-radius: 10px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-footer {
|
|
||||||
margin: 8px 12px 0 0;
|
|
||||||
padding: 0 12px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: left;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(51, 51, 51, 0.929);
|
|
||||||
|
|
||||||
.count {
|
|
||||||
margin-left: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.2); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>红薯</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,928 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container" id="container">
|
|
||||||
<div class="top">
|
|
||||||
<header class="mask-paper">
|
|
||||||
<a style="display: flex">
|
|
||||||
<img src="@/assets/logo.png" style="width: 80px" />
|
|
||||||
</a>
|
|
||||||
<div class="tool-box"></div>
|
|
||||||
<div class="input-box" id="sujContainer">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
v-model="keyword"
|
|
||||||
class="search-input"
|
|
||||||
placeholder="搜索小红书"
|
|
||||||
@input="changeInput"
|
|
||||||
@focus="focusInput"
|
|
||||||
@keyup.enter="searchPage"
|
|
||||||
ref="SearchInput"
|
|
||||||
/>
|
|
||||||
<div class="input-button">
|
|
||||||
<div class="close-icon" v-show="showClose" @click="clearInput">
|
|
||||||
<Close style="width: 1.2em; height: 1.2em; margin-right: 20px; margin-top: 5px" />
|
|
||||||
</div>
|
|
||||||
<div class="search-icon" @click="searchPage">
|
|
||||||
<a href="#">
|
|
||||||
<Search style="width: 1.2em; height: 1.2em; margin-right: 20px; margin-top: 5px" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<SearchContainer v-show="showSearch" :recordList="recordList"></SearchContainer>
|
|
||||||
<SujContainer v-show="showHistory" :closeHistoryRecord="showHistory"></SujContainer>
|
|
||||||
</div>
|
|
||||||
<div class="right"></div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
<div class="main">
|
|
||||||
<div class="side-bar">
|
|
||||||
<ul class="channel-list">
|
|
||||||
<li :class="activeLink == 0 ? 'active-channel' : ''" @click="toLink(0)">
|
|
||||||
<a class="link-wrapper">
|
|
||||||
<House style="width: 1em; height: 1em; margin-right: 8px" /><span class="channel">发现</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li :class="activeLink == 1 ? 'active-channel' : ''" @click="toLink(1)">
|
|
||||||
<Star style="width: 1em; height: 1em; margin-right: 8px" /><span class="channel"> 动态</span>
|
|
||||||
</li>
|
|
||||||
<li :class="activeLink == 2 ? 'active-channel' : ''" @click="toLink(2)">
|
|
||||||
<Bell style="width: 1em; height: 1em; margin-right: 8px" />
|
|
||||||
|
|
||||||
<el-badge is-dot class="item" v-if="messageCount > 0 && userInfo != null">
|
|
||||||
<span class="channel"> 消息</span></el-badge
|
|
||||||
>
|
|
||||||
<span class="channel" v-else>消息</span>
|
|
||||||
</li>
|
|
||||||
<li :class="activeLink == 3 ? 'active-channel' : ''" @click="toLink(3)">
|
|
||||||
<CirclePlus style="width: 1em; height: 1em; margin-right: 8px" /><span class="channel"> 发布</span>
|
|
||||||
</li>
|
|
||||||
<div v-if="userInfo == null">
|
|
||||||
<el-button type="danger" round @click="login" class="custom-button">登录</el-button>
|
|
||||||
</div>
|
|
||||||
<li v-else :class="activeLink == 4 ? 'active-channel' : ''" @click="toLink(4)">
|
|
||||||
<el-avatar :src="userInfo.avatar" :size="22" />
|
|
||||||
<span class="channel">我</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div v-if="userInfo == null">
|
|
||||||
<div data-v-6432121e="" data-v-7d49aed8="" class="floating-box visible">
|
|
||||||
<div data-v-6432121e="" class="title">马上登录即可</div>
|
|
||||||
<div data-v-6432121e="" class="line-container">
|
|
||||||
<svg data-v-23d27ada="" data-v-6432121e="" class="reds-icon icon" width="16" height="16">
|
|
||||||
<use data-v-23d27ada="" xlink:href="#thumbUp"></use>
|
|
||||||
</svg>
|
|
||||||
<span data-v-6432121e="" class="desc">刷到更懂你的优质内容</span>
|
|
||||||
</div>
|
|
||||||
<div data-v-6432121e="" class="line-container">
|
|
||||||
<svg data-v-23d27ada="" data-v-6432121e="" class="reds-icon icon" width="16" height="16">
|
|
||||||
<use data-v-23d27ada="" xlink:href="#convention_b"></use>
|
|
||||||
</svg>
|
|
||||||
<span data-v-6432121e="" class="desc">搜索最新种草、拔草信息</span>
|
|
||||||
</div>
|
|
||||||
<div data-v-6432121e="" class="line-container">
|
|
||||||
<svg data-v-23d27ada="" data-v-6432121e="" class="reds-icon icon" width="16" height="16">
|
|
||||||
<use data-v-23d27ada="" xlink:href="#collect"></use>
|
|
||||||
</svg>
|
|
||||||
<span data-v-6432121e="" class="desc">查看收藏、点赞的笔记</span>
|
|
||||||
</div>
|
|
||||||
<div data-v-6432121e="" class="line-container">
|
|
||||||
<svg data-v-23d27ada="" data-v-6432121e="" class="reds-icon icon" width="16" height="16">
|
|
||||||
<use data-v-23d27ada="" xlink:href="#chat"></use>
|
|
||||||
</svg>
|
|
||||||
<span data-v-6432121e="" class="desc">与他人更好地互动、交流</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 嵌入 SVG 图标定义 -->
|
|
||||||
<svg style="display: none">
|
|
||||||
<symbol id="thumbUp" viewBox="0 0 24 24">
|
|
||||||
<!-- SVG 路径数据 -->
|
|
||||||
<path
|
|
||||||
d="M1 21h4V9H1v12zM23 10h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L16.17 3 9.59 9.59C9.21 9.95 9 10.45 9 11v8c0 .55.45 1 1 1h6c.38 0 .72-.21.89-.55l3.66-7.33c.08-.14.13-.3.13-.46V11c0-.55-.45-1-1-1zm-2 7h-4v-6h4v6z"
|
|
||||||
/>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="convention_b" viewBox="0 0 24 24">
|
|
||||||
<!-- SVG 路径数据 -->
|
|
||||||
<path
|
|
||||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z"
|
|
||||||
/>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="collect" viewBox="0 0 24 24">
|
|
||||||
<!-- SVG 路径数据 -->
|
|
||||||
<path
|
|
||||||
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
|
|
||||||
/>
|
|
||||||
</symbol>
|
|
||||||
<symbol id="chat" viewBox="0 0 24 24">
|
|
||||||
<!-- SVG 路径数据 -->
|
|
||||||
<path d="M20 2H4c-1.1 0-2 .9-2 2v14l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 9H6V9h12v2zm0-3H6V6h12v2z" />
|
|
||||||
</symbol>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<div class="information-container" id="informationContainer">
|
|
||||||
<div class="information-pad" v-show="padShow">
|
|
||||||
<div class="container">
|
|
||||||
<div class="group-wrapper">
|
|
||||||
<a
|
|
||||||
class="menu-item hover-effect links"
|
|
||||||
target="_blank"
|
|
||||||
href="https://agree.xiaohongshu.com/h5/terms/ZXXY20220331001/-1"
|
|
||||||
>
|
|
||||||
<span>关于小红书</span>
|
|
||||||
<div class="icon">
|
|
||||||
<ArrowRight style="width: 1em; height: 1em; margin-right: 8px" />
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="menu-item hover-effect links"
|
|
||||||
target="_blank"
|
|
||||||
href="https://agree.xiaohongshu.com/h5/terms/ZXXY20220509001/-1"
|
|
||||||
>
|
|
||||||
<span>隐私,协议</span>
|
|
||||||
<div class="icon">
|
|
||||||
<ArrowRight style="width: 1em; height: 1em; margin-right: 8px" />
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div class="menu-item hover-effect">
|
|
||||||
<a href="#" @click="toUpshow = true">
|
|
||||||
<span> 帮助与客服 </span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div class="group-wrapper">
|
|
||||||
<div class="group-header">设置</div>
|
|
||||||
<div class="menu-item hover-effect">
|
|
||||||
<span>深色模式</span>
|
|
||||||
<div class="multistage-toggle component">
|
|
||||||
<button class="toggle-item active">
|
|
||||||
<div class="icon-wrapper">
|
|
||||||
<Sunny style="width: 1em; height: 1em" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
<button class="toggle-item">
|
|
||||||
<div class="icon-wrapper">
|
|
||||||
<Moon style="width: 1em; height: 1em" />
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="userInfo != null">
|
|
||||||
<div class="menu-item hover-effect" @click="logout">
|
|
||||||
<a href="#"><span>退出登录</span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="information-wrapper" @click="loadPad">
|
|
||||||
<!-- <More style="width: 1em; height: 1em; margin-right: 8px" /> -->
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M19.75 12a.75.75 0 0 0-.75-.75H5a.75.75 0 0 0 0 1.5h14a.75.75 0 0 0 .75-.75m0-5a.75.75 0 0 0-.75-.75H5a.75.75 0 0 0 0 1.5h14a.75.75 0 0 0 .75-.75m0 10a.75.75 0 0 0-.75-.75H5a.75.75 0 0 0 0 1.5h14a.75.75 0 0 0 .75-.75"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span class="channel">更多</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="main-content with-side-bar">
|
|
||||||
<router-view />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Login v-show="loginShow" @click-child="close"></Login>
|
|
||||||
<ToUP v-show="toUpshow" @click-to-up="toUpshow = false"></ToUP>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Search, Sunny, Moon, Close, House, Star, Bell, ArrowRight, CirclePlus } from "@element-plus/icons-vue";
|
|
||||||
import { useRouter, useRoute } from "vue-router";
|
|
||||||
import Login from "@/pages/login.vue";
|
|
||||||
import { ref, watch, onMounted, computed, watchEffect } from "vue";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
import { useSearchStore } from "@/store/searchStore";
|
|
||||||
import SujContainer from "@/components/SujContainer.vue";
|
|
||||||
import SearchContainer from "@/components/SearchContainer.vue";
|
|
||||||
import { addRecord, getRecordByKeyWord } from "@/api/search";
|
|
||||||
import { getRandomString } from "@/utils/util";
|
|
||||||
import { getChatUserList, getCountMessage } from "@/api/im";
|
|
||||||
import { useImStore } from "@/store/imStore";
|
|
||||||
import { loginOut } from "@/api/user";
|
|
||||||
import { wsKey } from "@/constant/constant";
|
|
||||||
import ToUP from "@/pages/to-up/index.vue";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const route = useRoute();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const searchStore = useSearchStore();
|
|
||||||
const imStore = useImStore();
|
|
||||||
const loginShow = ref(true);
|
|
||||||
const userInfo = ref<any>({});
|
|
||||||
const showHistory = ref(false);
|
|
||||||
const showSearch = ref(false);
|
|
||||||
const keyword = ref("");
|
|
||||||
const showClose = ref(false);
|
|
||||||
const SearchInput = ref();
|
|
||||||
const recordList = ref<Array<string>>([]);
|
|
||||||
const activeLink = ref(1);
|
|
||||||
const padShow = ref(false);
|
|
||||||
const ws = ref();
|
|
||||||
const toUpshow = ref(false);
|
|
||||||
const isShowDot = ref(false);
|
|
||||||
|
|
||||||
const routerList = ["/dashboard", "/followTrend", "/notice", "/push", "/user", "/search"];
|
|
||||||
|
|
||||||
// 监听外部点击
|
|
||||||
onMounted(() => {
|
|
||||||
document.getElementById("container")!.addEventListener("click", function (e) {
|
|
||||||
let event = e || window.event;
|
|
||||||
let target = event.target || (event.srcElement as any);
|
|
||||||
isInDiv("sujContainer", target).then((data) => {
|
|
||||||
if (!data) {
|
|
||||||
showHistory.value = false;
|
|
||||||
showSearch.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
isInDiv("informationContainer", target).then((data) => {
|
|
||||||
if (!data) {
|
|
||||||
padShow.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const isInDiv = (dom: string, target: any) => {
|
|
||||||
return new Promise((res) => {
|
|
||||||
const data = document.getElementById(dom)!.contains(target);
|
|
||||||
res(data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchPage = () => {
|
|
||||||
// 1.storage中添加搜索记录
|
|
||||||
searchStore.setKeyword(keyword.value);
|
|
||||||
if (keyword.value.length > 0) {
|
|
||||||
addRecord(keyword.value);
|
|
||||||
searchStore.pushRecord(keyword.value);
|
|
||||||
searchStore.setSeed(getRandomString(12));
|
|
||||||
}
|
|
||||||
showSearch.value = false;
|
|
||||||
|
|
||||||
router.push({ name: "search", query: { keyword: keyword.value } });
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [searchStore.seed, route.query.date],
|
|
||||||
(newVal, oldVal) => {
|
|
||||||
if (newVal[0] != oldVal[0]) {
|
|
||||||
keyword.value = searchStore.keyWord;
|
|
||||||
showHistory.value = false;
|
|
||||||
showSearch.value = false;
|
|
||||||
}
|
|
||||||
if (newVal[1] != oldVal[1]) {
|
|
||||||
initData();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [imStore.countMessage],
|
|
||||||
(val) => {
|
|
||||||
const allCount = val[0].chatCount + val[0].likeOrCollectionCount + val[0].commentCount + val[0].followCount;
|
|
||||||
if (allCount === 0) {
|
|
||||||
isShowDot.value = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const messageCount = computed({
|
|
||||||
get: () => {
|
|
||||||
return (
|
|
||||||
imStore.countMessage.chatCount +
|
|
||||||
imStore.countMessage.likeOrCollectionCount +
|
|
||||||
imStore.countMessage.commentCount +
|
|
||||||
imStore.countMessage.followCount
|
|
||||||
);
|
|
||||||
},
|
|
||||||
set: (val) => {
|
|
||||||
imStore.setCountMessage(val);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
const url = window.location.href;
|
|
||||||
const _keyword = "keyword";
|
|
||||||
if (url.indexOf("?") != -1 && url.indexOf(_keyword) != -1) {
|
|
||||||
const val = url.substring(url.lastIndexOf(_keyword) + _keyword.length + 1, url.length);
|
|
||||||
keyword.value = decodeURI(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const changeInput = (e: any) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
keyword.value = value;
|
|
||||||
showClose.value = keyword.value == "" ? false : true;
|
|
||||||
showSearch.value = keyword.value.length == 0 ? false : true;
|
|
||||||
showHistory.value = keyword.value.length > 0 ? false : true;
|
|
||||||
if (keyword.value.length > 0) {
|
|
||||||
getRecordByKeyWord(keyword.value).then((res) => {
|
|
||||||
recordList.value = res.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const focusInput = () => {
|
|
||||||
showClose.value = keyword.value == "" ? false : true;
|
|
||||||
showSearch.value = keyword.value.length == 0 ? false : true;
|
|
||||||
showHistory.value = keyword.value.length > 0 ? false : true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearInput = () => {
|
|
||||||
keyword.value = "";
|
|
||||||
showClose.value = false;
|
|
||||||
showHistory.value = true;
|
|
||||||
showSearch.value = false;
|
|
||||||
SearchInput.value.focus();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toLink = (num: number) => {
|
|
||||||
activeLink.value = num;
|
|
||||||
const url = routerList[num];
|
|
||||||
if (url === "/user") {
|
|
||||||
router.push({ name: "user", query: { uid: userInfo.value.id } });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
router.push({ path: url });
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = (val: boolean) => {
|
|
||||||
loginShow.value = val;
|
|
||||||
userInfo.value = userStore.getUserInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadPad = () => {
|
|
||||||
padShow.value = !padShow.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const maxRetries = 5; // 最大重试次数
|
|
||||||
let retryCount = 0; // 当前重试次数
|
|
||||||
|
|
||||||
const connectWs = (uid: string) => {
|
|
||||||
ws.value = new WebSocket(wsKey + uid);
|
|
||||||
ws.value.onopen = () => {
|
|
||||||
console.log("连接成功");
|
|
||||||
retryCount = 0; // 重置重试计数
|
|
||||||
};
|
|
||||||
ws.value.onclose = () => {
|
|
||||||
console.log("连接断开");
|
|
||||||
if (userInfo.value != null && userInfo.value != undefined) {
|
|
||||||
if (retryCount < maxRetries) {
|
|
||||||
retryCount++;
|
|
||||||
const retryDelay = Math.min(1000 * retryCount, 5000); // 延迟时间(1秒,2秒,3秒,最多5秒)
|
|
||||||
console.log(`尝试重新连接,第 ${retryCount} 次重试,将在 ${retryDelay} 毫秒后重试`);
|
|
||||||
setTimeout(() => connectWs(userInfo.value.id), retryDelay);
|
|
||||||
} else {
|
|
||||||
console.log("已达到最大重试次数,停止重连");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ws.value.onmessage = (e: any) => {
|
|
||||||
const message = JSON.parse(e.data);
|
|
||||||
console.log("收到消息", message);
|
|
||||||
|
|
||||||
if (message.msgType === 0) {
|
|
||||||
const content = message.content;
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
_countMessage.likeOrCollectionCount = content.likeOrCollectionCount;
|
|
||||||
_countMessage.commentCount = content.commentCount;
|
|
||||||
_countMessage.followCount = content.followCount;
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
}
|
|
||||||
if (message.msgType === 1) {
|
|
||||||
imStore.setMessage(message);
|
|
||||||
}
|
|
||||||
if (message.msgType === 5) {
|
|
||||||
const userList = message.content;
|
|
||||||
imStore.setUserList(userList);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const getChatUserListMethod = () => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
getChatUserList().then((res: any) => {
|
|
||||||
const data = res.data;
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
data.forEach((item: any) => {
|
|
||||||
_countMessage.chatCount += item.count;
|
|
||||||
});
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
imStore.setUserList(data);
|
|
||||||
resolve(_countMessage.chatCount);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCountMessageMethod = () => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
getCountMessage().then((res: any) => {
|
|
||||||
const data = res.data;
|
|
||||||
imStore.setCountMessage(data);
|
|
||||||
resolve(data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const login = () => {
|
|
||||||
loginShow.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
loginOut(userInfo.value.id).then(() => {
|
|
||||||
userStore.loginOut();
|
|
||||||
userInfo.value = null;
|
|
||||||
loginShow.value = true;
|
|
||||||
activeLink.value = 0;
|
|
||||||
ws.value.onclose();
|
|
||||||
router.push({ path: "/" });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getWsMessage = async () => {
|
|
||||||
if (userInfo.value === null || userInfo.value === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
loginShow.value = false;
|
|
||||||
connectWs(userInfo.value.id);
|
|
||||||
const p = await getChatUserListMethod();
|
|
||||||
const q = (await getCountMessageMethod()) as any;
|
|
||||||
|
|
||||||
// TODO: 需要修复显示数量bug
|
|
||||||
const _countMessage = {} as any;
|
|
||||||
_countMessage.chatCount = p;
|
|
||||||
_countMessage.likeOrCollectionCount = q.likeOrCollectionCount;
|
|
||||||
_countMessage.commentCount = q.commentCount;
|
|
||||||
_countMessage.followCount = q.followCount;
|
|
||||||
|
|
||||||
messageCount.value = _countMessage;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPath = () => {
|
|
||||||
const url = window.location.href;
|
|
||||||
let path = "";
|
|
||||||
if (url.indexOf("?") != -1) {
|
|
||||||
const index = url.indexOf("?");
|
|
||||||
path = url.substring(url.lastIndexOf("/"), index);
|
|
||||||
} else {
|
|
||||||
path = url.substring(url.lastIndexOf("/"), url.length);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
userInfo.value = userStore.getUserInfo();
|
|
||||||
const path = getPath();
|
|
||||||
activeLink.value = path === "/search" ? 0 : routerList.findIndex((item) => item === path);
|
|
||||||
getWsMessage();
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 添加选中标签的效果 */
|
|
||||||
.channel-list li:hover {
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 激活的选中标签样式 */
|
|
||||||
.active-channel {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 浮动框容器 */
|
|
||||||
.floating-box {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 20px; /* 调整内边距 */
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
|
||||||
max-width: 300px; /* 调整宽度 */
|
|
||||||
margin: 0 auto; /* 使浮动框居中 */
|
|
||||||
margin-left: -2px; /* 向左移动 2px */
|
|
||||||
cursor: pointer;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 标题样式 */
|
|
||||||
.title {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 行容器样式 */
|
|
||||||
.line-container {
|
|
||||||
margin-bottom: 6px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 图标样式 */
|
|
||||||
.reds-icon {
|
|
||||||
fill: #ff2442; /* 图标颜色 */
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 描述文本样式 */
|
|
||||||
.desc {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-button {
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-left: -2px;
|
|
||||||
height: 48px;
|
|
||||||
background: #ff2442;
|
|
||||||
color: #fff;
|
|
||||||
opacity: 1;
|
|
||||||
border-radius: 999px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1728px;
|
|
||||||
background-color: #fff;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.top {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100vw;
|
|
||||||
height: 72px;
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10;
|
|
||||||
align-items: center;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
header {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1728px;
|
|
||||||
height: 72px;
|
|
||||||
padding: 0 16px 0 24px;
|
|
||||||
z-index: 10;
|
|
||||||
|
|
||||||
.tool-box {
|
|
||||||
width: 24px;
|
|
||||||
height: 70px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box {
|
|
||||||
height: 40px;
|
|
||||||
position: fixed;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%);
|
|
||||||
|
|
||||||
@media screen and (max-width: 695px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 960px) and (max-width: 1191px) {
|
|
||||||
width: calc(-36px + 50vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1192px) and (max-width: 1423px) {
|
|
||||||
width: calc(-33.6px + 40vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1424px) and (max-width: 1727px) {
|
|
||||||
width: calc(-42.66667px + 33.33333vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1728px) {
|
|
||||||
width: 533.33333px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
padding: 0 84px 0 16px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 120%;
|
|
||||||
color: #333;
|
|
||||||
caret-color: #ff2442;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-button {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 100%;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.close-icon .search-icon {
|
|
||||||
width: 40px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.15); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.side-bar {
|
|
||||||
@media screen and (max-width: 695px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 696px) and (max-width: 959px) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 960px) and (max-width: 1191px) {
|
|
||||||
width: calc(-18px + 25vw);
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1192px) and (max-width: 1423px) {
|
|
||||||
width: calc(-16.8px + 20vw);
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1424px) and (max-width: 1727px) {
|
|
||||||
width: calc(-21.33333px + 16.66667vw);
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1728px) {
|
|
||||||
width: 266.66667px;
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
height: calc(100vh - 72px);
|
|
||||||
overflow-y: scroll;
|
|
||||||
background-color: #fff;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding-top: 16px;
|
|
||||||
margin-top: 72px;
|
|
||||||
position: fixed;
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
.channel-list {
|
|
||||||
min-height: auto;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
|
|
||||||
.active-channel {
|
|
||||||
background-color: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 999px;
|
|
||||||
color: #333;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding-left: 16px;
|
|
||||||
min-height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
|
|
||||||
.link-wrapper {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 48px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-count {
|
|
||||||
margin-left: 7rem;
|
|
||||||
background-color: red;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-left: 12px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.information-container {
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
color: #333;
|
|
||||||
font-size: 16px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
margin-left: -6px;
|
|
||||||
|
|
||||||
.information-pad {
|
|
||||||
z-index: 16;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
width: 90%;
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow:
|
|
||||||
0 4px 32px 0 rgba(0, 0, 0, 0.08),
|
|
||||||
0 1px 4px 0 rgba(0, 0, 0, 0.04);
|
|
||||||
border-radius: 12px;
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 0px 12px;
|
|
||||||
list-style: none;
|
|
||||||
height: 0;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-width: 1px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group-wrapper {
|
|
||||||
padding: 4px;
|
|
||||||
|
|
||||||
.group-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
height: 32px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
height: 40px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
color: rgba(51, 51, 51, 0.3);
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multistage-toggle {
|
|
||||||
position: relative;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
display: flex();
|
|
||||||
padding: 2px;
|
|
||||||
border-radius: 999px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.active {
|
|
||||||
background: #fff;
|
|
||||||
box-shadow:
|
|
||||||
0 2px 8px 0 rgba(0, 0, 0, 0.04),
|
|
||||||
0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-item {
|
|
||||||
border-radius: 999px;
|
|
||||||
background: transparent;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
|
|
||||||
.icon-wrapper {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 添加选中标签的效果 */
|
|
||||||
.menu-item:hover {
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.information-wrapper {
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
height: 48px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
font-weight: 600;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 999px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 添加选中标签的效果 */
|
|
||||||
.information-wrapper:hover {
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
@media screen and (min-width: 960px) and (max-width: 1191px) {
|
|
||||||
padding-left: calc(-6px + 25vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1192px) and (max-width: 1423px) {
|
|
||||||
padding-left: calc(-4.8px + 20vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1424px) and (max-width: 1727px) {
|
|
||||||
padding-left: calc(-5.33333px + 16.66667vw);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1728px) {
|
|
||||||
padding-left: 282.66667px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,754 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="note-detail-mask" style="transition: background-color 0.4s ease 0s;hsla(0,0%,100%,0.98)">
|
|
||||||
<div class="note-container">
|
|
||||||
<div class="media-container">
|
|
||||||
<el-carousel height="90vh" :autoplay="false">
|
|
||||||
<el-carousel-item v-for="(item, index) in noteInfo.imgList" :key="index">
|
|
||||||
<el-image
|
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
:src="item"
|
|
||||||
fit="contain"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
/>
|
|
||||||
</el-carousel-item>
|
|
||||||
</el-carousel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="interaction-container">
|
|
||||||
<div class="author-container">
|
|
||||||
<div class="author-me">
|
|
||||||
<div class="info" @click="toUser(noteInfo.uid)">
|
|
||||||
<img class="avatar-item" style="width: 40px; height: 40px" :src="noteInfo.avatar" />
|
|
||||||
<span class="name">{{ noteInfo.username }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="follow-btn" v-if="currentUid !== noteInfo.uid">
|
|
||||||
<el-button type="info" size="large" round v-if="noteInfo.isFollow" @click="follow(noteInfo.uid, 1)"
|
|
||||||
>已关注</el-button
|
|
||||||
>
|
|
||||||
<el-button type="danger" size="large" round v-else @click="follow(noteInfo.uid, 0)">关注</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="follow-btn" v-else>
|
|
||||||
<el-dropdown>
|
|
||||||
<el-button type="danger" size="large" round>
|
|
||||||
编辑
|
|
||||||
<el-icon class="el-icon--right">
|
|
||||||
<arrow-down />
|
|
||||||
</el-icon>
|
|
||||||
</el-button>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item v-if="noteInfo.pinned === '0'" @click="pinned(noteInfo.id, '1')"
|
|
||||||
>置顶</el-dropdown-item
|
|
||||||
>
|
|
||||||
<el-dropdown-item v-else @click="pinned(noteInfo.id, '0')">取消置顶</el-dropdown-item>
|
|
||||||
<el-dropdown-item @click="deleteNote(noteInfo.id)">删除</el-dropdown-item>
|
|
||||||
<el-dropdown-item @click="toEdit(noteInfo.id)">编辑</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-scroller" ref="noteScroller" @scroll="loadMoreData">
|
|
||||||
<div class="note-content">
|
|
||||||
<div class="title">{{ noteInfo.title }}</div>
|
|
||||||
<div class="desc">
|
|
||||||
<span>{{ noteInfo.content }} <br /></span>
|
|
||||||
<a class="tag tag-search" v-for="(item, index) in noteInfo.tagList" :key="index">#{{ item.title }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="bottom-container">
|
|
||||||
<span class="date">{{ noteInfo.time }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="divider interaction-divider"></div>
|
|
||||||
|
|
||||||
<!-- 评论 -->
|
|
||||||
<div class="comments-el">
|
|
||||||
<Comment
|
|
||||||
:nid="props.nid"
|
|
||||||
:currentPage="currentPage"
|
|
||||||
:replyComment="replyComment"
|
|
||||||
:seed="seed"
|
|
||||||
@click-comment="clickComment"
|
|
||||||
>
|
|
||||||
</Comment>
|
|
||||||
</div>
|
|
||||||
<!-- -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="interactions-footer">
|
|
||||||
<div class="buttons">
|
|
||||||
<div class="left">
|
|
||||||
<span class="like-wrapper"
|
|
||||||
><span class="like-lottie" v-if="noteInfo.isCollection" @click="likeOrCollection(3, -1)">
|
|
||||||
<StarFilled style="width: 0.9em; height: 0.9em; color: #333" />
|
|
||||||
</span>
|
|
||||||
<span class="like-lottie" v-else @click="likeOrCollection(3, 1)">
|
|
||||||
<Star style="width: 0.8em; height: 0.8em; color: #333" />
|
|
||||||
</span>
|
|
||||||
<span class="count">{{ noteInfo.collectionCount }}</span></span
|
|
||||||
>
|
|
||||||
<span class="collect-wrapper">
|
|
||||||
<span class="like-lottie" v-if="noteInfo.isLike" @click="likeOrCollection(1, -1)">
|
|
||||||
<i
|
|
||||||
class="iconfont icon-follow-fill"
|
|
||||||
:style="{ width: '1em', height: '1em', color: noteInfo.isLike ? 'red' : 'black' }"
|
|
||||||
v-if="noteInfo.isLike"
|
|
||||||
>
|
|
||||||
</i>
|
|
||||||
<i class="iconfont icon-follow" style="width: 1em; height: 1em" v-else></i>
|
|
||||||
</span>
|
|
||||||
<span class="like-lottie" v-else @click="likeOrCollection(1, 1)">
|
|
||||||
<i class="iconfont icon-follow" style="width: 0.8em; height: 0.8em; color: #333"></i>
|
|
||||||
</span>
|
|
||||||
<span class="count">{{ noteInfo.likeCount }}</span>
|
|
||||||
</span>
|
|
||||||
<span class="chat-wrapper">
|
|
||||||
<span class="like-lottie">
|
|
||||||
<ChatRound style="width: 0.8em; height: 0.8em; color: #333" />
|
|
||||||
</span>
|
|
||||||
<span class="count">
|
|
||||||
{{ noteInfo.commentCount }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="share-wrapper"></div>
|
|
||||||
</div>
|
|
||||||
<div :class="showSaveBtn ? 'comment-wrapper active comment-comp ' : 'comment-wrapper comment-comp '">
|
|
||||||
<div class="input-wrapper">
|
|
||||||
<input
|
|
||||||
class="comment-input"
|
|
||||||
v-model="commentValue"
|
|
||||||
type="text"
|
|
||||||
:placeholder="commentPlaceVal"
|
|
||||||
@input="commenInput"
|
|
||||||
@keyup.enter="saveComment"
|
|
||||||
/>
|
|
||||||
<div class="input-buttons" @click="clearCommeent" v-show="showSaveBtn">
|
|
||||||
<Close style="width: 1.2em; height: 1.2em" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="submit" @click="saveComment">发送</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="close-cricle" @click="close">
|
|
||||||
<div class="close close-mask-white">
|
|
||||||
<Close style="width: 1.2em; height: 1.2em; color: rgba(51, 51, 51, 0.8)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="back-desk"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Close, Star, ChatRound, StarFilled, ArrowDown } from "@element-plus/icons-vue";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { ref, watch } from "vue";
|
|
||||||
import { getNoteById, pinnedNote, deleteNoteByIds } from "@/api/note";
|
|
||||||
import { likeOrCollectionByDTO } from "@/api/likeOrCollection";
|
|
||||||
import type { NoteInfo } from "@/type/note";
|
|
||||||
import type { LikeOrCollectionDTO } from "@/type/likeOrCollection";
|
|
||||||
import { formateTime, getRandomString } from "@/utils/util";
|
|
||||||
import { followById } from "@/api/follower";
|
|
||||||
import Comment from "@/components/Comment.vue";
|
|
||||||
import type { CommentDTO } from "@/type/comment";
|
|
||||||
import { saveCommentByDTO, syncCommentByIds } from "@/api/comment";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// 这是路由传参
|
|
||||||
// nid.value = history.state.nid;
|
|
||||||
|
|
||||||
const emit = defineEmits(["clickMain"]);
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
nid: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
nowTime: {
|
|
||||||
type: Date,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentUid = ref("");
|
|
||||||
const noteInfo = ref<NoteInfo>({
|
|
||||||
id: "",
|
|
||||||
title: "",
|
|
||||||
content: "",
|
|
||||||
noteCover: "",
|
|
||||||
uid: "",
|
|
||||||
username: "",
|
|
||||||
avatar: "",
|
|
||||||
imgList: [],
|
|
||||||
type: -1,
|
|
||||||
likeCount: 0,
|
|
||||||
collectionCount: 0,
|
|
||||||
commentCount: 0,
|
|
||||||
tagList: [],
|
|
||||||
time: "",
|
|
||||||
isFollow: false,
|
|
||||||
isLike: false,
|
|
||||||
isCollection: false,
|
|
||||||
pinned: "0",
|
|
||||||
});
|
|
||||||
const commentValue = ref("");
|
|
||||||
const commentPlaceVal = ref("请输入内容");
|
|
||||||
const commentObject = ref<any>({});
|
|
||||||
const replyComment = ref<any>({});
|
|
||||||
const showSaveBtn = ref(false);
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const seed = ref("");
|
|
||||||
const commentIds = ref<Array<string>>([]);
|
|
||||||
const noteScroller = ref(null);
|
|
||||||
const isLogin = ref(false);
|
|
||||||
const likeOrComment = ref({
|
|
||||||
isLike: false,
|
|
||||||
isComment: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [props.nowTime],
|
|
||||||
() => {
|
|
||||||
currentPage.value = 1;
|
|
||||||
if (props.nid !== null && props.nid !== "") {
|
|
||||||
getNoteById(props.nid).then((res: any) => {
|
|
||||||
console.log("---note", res.data);
|
|
||||||
noteInfo.value = res.data;
|
|
||||||
noteInfo.value.imgList = JSON.parse(res.data.urls);
|
|
||||||
noteInfo.value.time = formateTime(res.data.time);
|
|
||||||
likeOrComment.value.isLike = noteInfo.value.isLike;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const noLoginNotice = () => {
|
|
||||||
if (!isLogin.value) {
|
|
||||||
ElMessage.warning("用户未登录");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
const _login = noLoginNotice();
|
|
||||||
if (!_login) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
if (isLogin.value) {
|
|
||||||
syncCommentByIds(commentIds.value).then(() => {
|
|
||||||
commentIds.value = [];
|
|
||||||
emit("clickMain", props.nid, likeOrComment.value);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
emit("clickMain");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const follow = (fid: string, type: number) => {
|
|
||||||
const _login = noLoginNotice();
|
|
||||||
if (!_login) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
followById(fid).then(() => {
|
|
||||||
noteInfo.value.isFollow = type == 0;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const likeOrCollection = (type: number, val: number) => {
|
|
||||||
const _login = noLoginNotice();
|
|
||||||
if (!_login) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const likeOrCollectionDTO = {} as LikeOrCollectionDTO;
|
|
||||||
likeOrCollectionDTO.likeOrCollectionId = noteInfo.value.id;
|
|
||||||
likeOrCollectionDTO.publishUid = noteInfo.value.uid;
|
|
||||||
likeOrCollectionDTO.type = type == 1 ? 1 : 3;
|
|
||||||
likeOrCollectionByDTO(likeOrCollectionDTO).then(() => {
|
|
||||||
if (type == 1) {
|
|
||||||
noteInfo.value.isLike = val == 1;
|
|
||||||
noteInfo.value.likeCount += val;
|
|
||||||
likeOrComment.value.isLike = val == 1;
|
|
||||||
} else {
|
|
||||||
noteInfo.value.isCollection = val == 1;
|
|
||||||
noteInfo.value.collectionCount += val;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const pinned = (noteId: string, type: string) => {
|
|
||||||
pinnedNote(noteId)
|
|
||||||
.then((res: any) => {
|
|
||||||
if (res.data) {
|
|
||||||
noteInfo.value.pinned = type;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage.warning("最多只能置顶3个笔记");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteNote = (noteId: string) => {
|
|
||||||
const data = [] as Array<string>;
|
|
||||||
data.push(noteId);
|
|
||||||
deleteNoteByIds(data).then(() => {
|
|
||||||
ElMessage.success("删除成功");
|
|
||||||
emit("clickMain");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const toEdit = (noteId: string) => {
|
|
||||||
router.push({ path: "/push", query: { date: Date.now(), noteId: noteId } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const clickComment = (comment: any) => {
|
|
||||||
commentObject.value = comment;
|
|
||||||
commentPlaceVal.value = "回复" + comment.username;
|
|
||||||
};
|
|
||||||
|
|
||||||
const commenInput = (e: any) => {
|
|
||||||
const { value } = e.target;
|
|
||||||
commentValue.value = value;
|
|
||||||
showSaveBtn.value = commentValue.value.length > 0 || commentObject.value.pid !== undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveComment = () => {
|
|
||||||
const _login = noLoginNotice();
|
|
||||||
if (!_login) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const comment = {} as CommentDTO;
|
|
||||||
comment.nid = props.nid;
|
|
||||||
comment.noteUid = noteInfo.value.uid;
|
|
||||||
if (commentObject.value.pid === undefined) {
|
|
||||||
comment.pid = "0";
|
|
||||||
comment.replyId = "0";
|
|
||||||
comment.replyUid = noteInfo.value.uid;
|
|
||||||
comment.level = 1;
|
|
||||||
} else if (commentObject.value.pid == "0") {
|
|
||||||
comment.pid = commentObject.value.id;
|
|
||||||
comment.replyId = commentObject.value.id;
|
|
||||||
comment.replyUid = commentObject.value.uid;
|
|
||||||
comment.level = 2;
|
|
||||||
} else {
|
|
||||||
comment.pid = commentObject.value.pid;
|
|
||||||
comment.replyId = commentObject.value.id;
|
|
||||||
comment.replyUid = commentObject.value.uid;
|
|
||||||
comment.level = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
comment.content = commentValue.value;
|
|
||||||
saveCommentByDTO(comment).then((res: any) => {
|
|
||||||
replyComment.value = res.data;
|
|
||||||
replyComment.value.replyUsername = commentObject.value.username;
|
|
||||||
commentValue.value = "";
|
|
||||||
commentObject.value = {};
|
|
||||||
commentPlaceVal.value = "请输入内容";
|
|
||||||
showSaveBtn.value = false;
|
|
||||||
seed.value = getRandomString(12);
|
|
||||||
commentIds.value.push(res.data.id);
|
|
||||||
likeOrComment.value.isComment = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearCommeent = () => {
|
|
||||||
commentValue.value = "";
|
|
||||||
commentObject.value = {};
|
|
||||||
commentPlaceVal.value = "请输入内容";
|
|
||||||
showSaveBtn.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMoreData = () => {
|
|
||||||
console.log("main加载更多");
|
|
||||||
const container = noteScroller.value as any;
|
|
||||||
if (container.scrollTop + container.clientHeight >= container.scrollHeight) {
|
|
||||||
currentPage.value += 1;
|
|
||||||
console.log("到底了");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
isLogin.value = userStore.isLogin();
|
|
||||||
if (isLogin.value) {
|
|
||||||
currentUid.value = userStore.getUserInfo().id;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
:deep(.el-dropdown-menu__item:not(.is-disabled):focus) {
|
|
||||||
background-color: #f8f8f8;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-detail-mask {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
display: flex;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
z-index: 20;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.back-desk {
|
|
||||||
position: fixed;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
opacity: 0.5;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
z-index: 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-cricle {
|
|
||||||
left: 1.3vw;
|
|
||||||
top: 1.3vw;
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
z-index: 100;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.close-mask-white {
|
|
||||||
box-shadow:
|
|
||||||
0 2px 8px 0 rgba(0, 0, 0, 0.04),
|
|
||||||
0 1px 2px 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.close {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 40px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.2); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-container {
|
|
||||||
width: 65%;
|
|
||||||
height: 86%;
|
|
||||||
transition:
|
|
||||||
transform 0.4s ease 0s,
|
|
||||||
width 0.4s ease 0s;
|
|
||||||
transform: translate(280px, 60px) scale(1);
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
box-shadow:
|
|
||||||
0 8px 64px 0 rgba(0, 0, 0, 0.04),
|
|
||||||
0 1px 4px 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 20px;
|
|
||||||
background: #f8f8f8;
|
|
||||||
transform-origin: left top;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.media-container {
|
|
||||||
width: 65%;
|
|
||||||
height: auto;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
flex-shrink: 0;
|
|
||||||
flex-grow: 0;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
overflow: hidden;
|
|
||||||
border-radius: 20px 0 0 20px;
|
|
||||||
transform: translateZ(0);
|
|
||||||
height: 100%;
|
|
||||||
object-fit: contain;
|
|
||||||
min-width: 440px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-container {
|
|
||||||
width: 35%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 0 20px 20px 0;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
border-left: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.author-me {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
padding: 24px;
|
|
||||||
border-bottom: 1px solid transparent;
|
|
||||||
|
|
||||||
.info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
padding-left: 12px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-scroller::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-scroller {
|
|
||||||
transition: scroll 0.4s;
|
|
||||||
overflow-y: scroll;
|
|
||||||
flex-grow: 1;
|
|
||||||
height: 80vh;
|
|
||||||
|
|
||||||
.note-content {
|
|
||||||
padding: 0 24px 24px;
|
|
||||||
color: var(--color-primary-label);
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 140%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
margin: 0;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 150%;
|
|
||||||
color: #333;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
|
|
||||||
.tag-search {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
margin-right: 2px;
|
|
||||||
color: #13386c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-top: 12px;
|
|
||||||
|
|
||||||
.date {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 120%;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-divider {
|
|
||||||
margin: 0 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 4px 8px;
|
|
||||||
list-style: none;
|
|
||||||
height: 0;
|
|
||||||
border: solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-width: 1px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comments-el {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactions-footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0px;
|
|
||||||
background: #fff;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding: 12px 24px 24px;
|
|
||||||
height: 130px;
|
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
flex-basis: 130px;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.count {
|
|
||||||
margin-left: 6px;
|
|
||||||
margin-right: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.like-wrapper {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
justify-content: left;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-right: 5px;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.like-lottie {
|
|
||||||
transform: scale(1.7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.collect-wrapper {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-right: 5px;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.like-lottie {
|
|
||||||
transform: scale(1.7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.15); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-wrapper {
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.like-lottie {
|
|
||||||
transform: scale(1.7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-wrapper {
|
|
||||||
&.active {
|
|
||||||
.input-wrapper {
|
|
||||||
flex-shrink: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-wrapper {
|
|
||||||
display: flex;
|
|
||||||
font-size: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.input-wrapper {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: flex 0.3s;
|
|
||||||
|
|
||||||
.comment-input:placeholder-shown {
|
|
||||||
background-image: none;
|
|
||||||
padding: 12px 92px 12px 36px;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEUAAAA0NDQyMjIzMzM2NjY2NjYyMjI0NDQ1NTU1NTUzMzM1NTU1NTUzMzM1NTUzMzM1NTU1NTVl84gVAAAAEnRSTlMAmUyGEzlgc2AmfRx9aToKQzCSoXt+AAAAhElEQVRIx+3Uuw6DMAyF4XOcBOdCafv+L9vQkQFyJBak/JOHT7K8GLM7epuHusRhHwP/mejJ77i32CpZh33aD+lDFDzgZFE8+tgUv5BB9NxEb9NPL3i46JvoUUhXPBKZFQ/rTPHI3ZXt8xr12KX055LoAVtXz9kKHprxNMMxXqRvmAn9ACQ7A/tTXYAxAAAAAElFTkSuQmCC);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 16px 16px;
|
|
||||||
background-position: 16px 12px;
|
|
||||||
color: rgba(51, 51, 51, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-input {
|
|
||||||
padding: 12px 92px 12px 16px;
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 16px;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
caret-color: rgba(51, 51, 51, 0.3);
|
|
||||||
border-radius: 22px;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-buttons {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 92px;
|
|
||||||
color: rgba(51, 51, 51, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit {
|
|
||||||
margin-left: 8px;
|
|
||||||
width: 60px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background: #3d8af5;
|
|
||||||
border-radius: 44px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-comp {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,382 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul class="message-container" v-infinite-scroll="loadMore">
|
|
||||||
<li class="message-item" v-for="(item, index) in dataList" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<!-- https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png -->
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.uid)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span v-if="item.pid === '0'">评论了您的笔记</span>
|
|
||||||
<span v-if="item.replyUid === currentUid && item.pid !== '0'">回复了您的评论</span>
|
|
||||||
<span v-if="item.replyUid !== currentUid && item.pid !== '0'">回复了{{ item.replyUsername }}的评论</span>
|
|
||||||
<span>{{ item.time }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-content">
|
|
||||||
<span>{{ item.content }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="quote-info" v-show="item.replyContent !== null">
|
|
||||||
{{ item.replyContent }}
|
|
||||||
</div>
|
|
||||||
<!-- <div class="action">
|
|
||||||
<div class="action-reply">
|
|
||||||
<ChatRound style="width: 1.2em; height: 1.2em" />
|
|
||||||
<div class="action-text">回复</div>
|
|
||||||
</div>
|
|
||||||
<div class="action-like">
|
|
||||||
<i class="iconfont icon-follow" style="color: #333"></i>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</div>
|
|
||||||
<div class="extra" @click="toMain(item.nid)">
|
|
||||||
<img class="extra-image" :src="item.noteCover" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<!-- <li class="message-item">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<img
|
|
||||||
class="avatar-item"
|
|
||||||
src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>这是名词</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span>评论了您的笔记 </span><span>2021-10-9</span>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-content">
|
|
||||||
<span>这是具体内容</span>
|
|
||||||
</div>
|
|
||||||
<div class="action">
|
|
||||||
<div class="comment-wrapper action-comment">
|
|
||||||
<div class="input-wrapper">
|
|
||||||
<textarea
|
|
||||||
rows="1"
|
|
||||||
class="comment-input"
|
|
||||||
type="text"
|
|
||||||
placeholder="回复 你好"
|
|
||||||
style="height: 40px"
|
|
||||||
></textarea>
|
|
||||||
<div class="input-buttons">
|
|
||||||
<Star style="width: 1.2em; height: 1.2em; margin: 0 6px" />
|
|
||||||
<Star style="width: 1.2em; height: 1.2em; margin: 0 6px" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="submit">发送</button>
|
|
||||||
</div>
|
|
||||||
<div class="action-cancel">取消</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra">
|
|
||||||
<img
|
|
||||||
class="extra-image"
|
|
||||||
src="https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li> -->
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
// import { ChatRound, Star } from "@element-plus/icons-vue";
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { formateTime } from "@/utils/util";
|
|
||||||
import { getNoticeComment } from "@/api/comment";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const router = useRouter();
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 12;
|
|
||||||
const dataList = ref<Array<any>>([]);
|
|
||||||
const dataTotal = ref(0);
|
|
||||||
const currentUid = ref("");
|
|
||||||
|
|
||||||
const emit = defineEmits(["clickMain"]);
|
|
||||||
|
|
||||||
const loadMore = () => {
|
|
||||||
currentPage.value += 1;
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toMain = (nid: string) => {
|
|
||||||
emit("clickMain", nid);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPageData = () => {
|
|
||||||
getNoticeComment(currentPage.value, pageSize).then((res) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
dataTotal.value = total;
|
|
||||||
records.forEach((item: any) => {
|
|
||||||
item.time = formateTime(item.time);
|
|
||||||
dataList.value.push(item);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
currentUid.value = userStore.getUserInfo().id;
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
textarea {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-container {
|
|
||||||
width: 40rem;
|
|
||||||
height: 90vh;
|
|
||||||
|
|
||||||
.message-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 24px;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.msg-count {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info {
|
|
||||||
font-size: 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info::before {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-right: 6px;
|
|
||||||
width: 4px;
|
|
||||||
height: 17px;
|
|
||||||
background: rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action {
|
|
||||||
display: flex;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.action-reply {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 88px;
|
|
||||||
height: 40px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 999px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.action-text {
|
|
||||||
margin-left: 4px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-like {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
margin-left: 12px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 999px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-comment {
|
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.input-wrapper {
|
|
||||||
height: auto;
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: calc(100% - 70px);
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: flex 0.3s;
|
|
||||||
|
|
||||||
.comment-input:placeholder-shown {
|
|
||||||
background-image: none;
|
|
||||||
padding: 12px 92px 12px 36px;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEUAAAA0NDQyMjIzMzM2NjY2NjYyMjI0NDQ1NTU1NTUzMzM1NTU1NTUzMzM1NTUzMzM1NTU1NTVl84gVAAAAEnRSTlMAmUyGEzlgc2AmfRx9aToKQzCSoXt+AAAAhElEQVRIx+3Uuw6DMAyF4XOcBOdCafv+L9vQkQFyJBak/JOHT7K8GLM7epuHusRhHwP/mejJ77i32CpZh33aD+lDFDzgZFE8+tgUv5BB9NxEb9NPL3i46JvoUUhXPBKZFQ/rTPHI3ZXt8xr12KX055LoAVtXz9kKHprxNMMxXqRvmAn9ACQ7A/tTXYAxAAAAAElFTkSuQmCC);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 16px 16px;
|
|
||||||
background-position: 16px 12px;
|
|
||||||
color: rgba(51, 51, 51, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-input {
|
|
||||||
padding: 12px 92px 12px 16px;
|
|
||||||
width: 100%;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 16px;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
caret-color: rgba(51, 51, 51, 0.3);
|
|
||||||
border-radius: 22px;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
resize: none;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-buttons {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 92px;
|
|
||||||
color: rgba(51, 51, 51, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit {
|
|
||||||
margin-left: 8px;
|
|
||||||
width: 60px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
flex-shrink: 0;
|
|
||||||
background: #3d8af5;
|
|
||||||
border-radius: 44px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-cancel {
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 40px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
padding: 10px 16px;
|
|
||||||
border-radius: 999px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment-wrapper {
|
|
||||||
display: flex;
|
|
||||||
font-size: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra {
|
|
||||||
min-width: 48px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 24px;
|
|
||||||
|
|
||||||
.extra-image {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 6px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,234 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul class="agree-container" v-infinite-scroll="loadMore">
|
|
||||||
<li class="agree-item" v-for="(item, index) in dataList" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<!-- https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png -->
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.uid)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span>开始关注你了 </span><span>{{ item.time }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra">
|
|
||||||
<el-button
|
|
||||||
class="button"
|
|
||||||
type="info"
|
|
||||||
round
|
|
||||||
size="large"
|
|
||||||
v-if="item.isFollow"
|
|
||||||
@click="follow(item.uid, index, 1)"
|
|
||||||
>互相关注</el-button
|
|
||||||
>
|
|
||||||
<el-button class="button" type="danger" round size="large" v-else @click="follow(item.uid, index, -1)"
|
|
||||||
>回关</el-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { getNoticeFollower } from "@/api/follower";
|
|
||||||
import { formateTime } from "@/utils/util";
|
|
||||||
import { followById } from "@/api/follower";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 12;
|
|
||||||
const dataList = ref<Array<any>>([]);
|
|
||||||
const dataTotal = ref(0);
|
|
||||||
|
|
||||||
const getPageData = () => {
|
|
||||||
getNoticeFollower(currentPage.value, pageSize).then((res) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
dataTotal.value = total;
|
|
||||||
records.forEach((item: any) => {
|
|
||||||
item.time = formateTime(item.time);
|
|
||||||
dataList.value.push(item);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const follow = (fid: string, index: number, type: number) => {
|
|
||||||
followById(fid).then(() => {
|
|
||||||
dataList.value[index].isFollow = type == -1;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMore = () => {
|
|
||||||
currentPage.value += 1;
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
textarea {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agree-container {
|
|
||||||
width: 40rem;
|
|
||||||
height: 90vh;
|
|
||||||
|
|
||||||
.agree-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 24px;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.msg-count {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info {
|
|
||||||
font-size: 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info::before {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-right: 6px;
|
|
||||||
width: 4px;
|
|
||||||
height: 17px;
|
|
||||||
background: rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
width: 100px;
|
|
||||||
height: 40px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra {
|
|
||||||
min-width: 48px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 24px;
|
|
||||||
|
|
||||||
.follow-button {
|
|
||||||
width: 96px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new.large {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 16px;
|
|
||||||
padding: 0 24px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new.primary {
|
|
||||||
background-color: #ff2e4d;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
outline: none;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
vertical-align: middle;
|
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 100px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,200 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul class="agree-container" v-infinite-scroll="loadMore">
|
|
||||||
<li class="agree-item" v-for="(item, index) in dataList" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<!-- https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png -->
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.uid)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span v-if="item.type == 1">赞了你的笔记</span>
|
|
||||||
<span v-if="item.type == 2">赞了你的评论</span>
|
|
||||||
<span v-if="item.type == 3">收藏你的笔记</span>
|
|
||||||
<span v-if="item.type == 4">赞了你的{{ item.content }}专辑</span>
|
|
||||||
<span>{{ item.time }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="interaction-content">
|
|
||||||
<span>这是具体内容</span>
|
|
||||||
</div> -->
|
|
||||||
<div class="quote-info" v-if="item.type == 2">{{ item.content }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra" @click="toPage(item.itemId)">
|
|
||||||
<img class="extra-image" :src="item.itemCover" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { getNoticeLikeOrCollection } from "@/api/likeOrCollection";
|
|
||||||
import { formateTime } from "@/utils/util";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const emit = defineEmits(["clickMain"]);
|
|
||||||
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 12;
|
|
||||||
const dataList = ref<Array<any>>([]);
|
|
||||||
const dataTotal = ref(0);
|
|
||||||
|
|
||||||
const toPage = (nid: string) => {
|
|
||||||
emit("clickMain", nid);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPageData = () => {
|
|
||||||
getNoticeLikeOrCollection(currentPage.value, pageSize).then((res) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
dataTotal.value = total;
|
|
||||||
records.forEach((item: any) => {
|
|
||||||
item.time = formateTime(item.time);
|
|
||||||
dataList.value.push(item);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMore = () => {
|
|
||||||
currentPage.value += 1;
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
getPageData();
|
|
||||||
};
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
textarea {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agree-container {
|
|
||||||
width: 40rem;
|
|
||||||
height: 90vh;
|
|
||||||
|
|
||||||
.agree-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 24px;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.msg-count {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info {
|
|
||||||
font-size: 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info::before {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin-right: 6px;
|
|
||||||
width: 4px;
|
|
||||||
height: 17px;
|
|
||||||
background: rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra {
|
|
||||||
min-width: 48px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 24px;
|
|
||||||
|
|
||||||
.extra-image {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 6px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,171 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul class="message-container">
|
|
||||||
<li class="message-item" v-for="(item, index) in dataList" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<!-- https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png -->
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.uid)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span>{{ item.time }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="interaction-content" @click="toChat(item.uid, index)">
|
|
||||||
<span>{{ item.content }}</span>
|
|
||||||
<div class="msg-count" v-show="item.count > 0">{{ item.count }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<Chat
|
|
||||||
v-if="chatShow"
|
|
||||||
:acceptUid="acceptUid"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-chat="close"
|
|
||||||
></Chat>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { useImStore } from "@/store/imStore";
|
|
||||||
import { ref, watchEffect } from "vue";
|
|
||||||
import { formateTime } from "@/utils/util";
|
|
||||||
import Chat from "@/components/Chat.vue";
|
|
||||||
import { clearMessageCount } from "@/api/im";
|
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const imStore = useImStore();
|
|
||||||
const dataList = ref<Array<any>>([]);
|
|
||||||
const chatShow = ref(false);
|
|
||||||
const acceptUid = ref("");
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
dataList.value = [];
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
_countMessage.chatCount = 0;
|
|
||||||
imStore.userList.forEach((item) => {
|
|
||||||
item.time = formateTime(item.timestamp);
|
|
||||||
_countMessage.chatCount += item.count;
|
|
||||||
dataList.value.push(item);
|
|
||||||
});
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
const toChat = (uid: string, index: number) => {
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
clearMessageCount(uid, 3).then(() => {
|
|
||||||
const chatCount = dataList.value[index].count;
|
|
||||||
_countMessage.chatCount -= chatCount;
|
|
||||||
dataList.value[index].count = 0;
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
acceptUid.value = uid;
|
|
||||||
chatShow.value = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = (uid: string) => {
|
|
||||||
const index = dataList.value.findIndex((item) => item.uid === uid);
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
clearMessageCount(uid, 3).then(() => {
|
|
||||||
const chatCount = dataList.value[index].count;
|
|
||||||
_countMessage.chatCount -= chatCount;
|
|
||||||
dataList.value[index].count = 0;
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
chatShow.value = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.message-container {
|
|
||||||
width: 40rem;
|
|
||||||
|
|
||||||
.message-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 24px;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 24px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.msg-count {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,212 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div v-if="isLogin">
|
|
||||||
<div class style="height: 72px">
|
|
||||||
<div class="reds-sticky">
|
|
||||||
<div class="reds-tabs-list">
|
|
||||||
<el-badge :value="_countMessage.chatCount" :max="99" :hidden="_countMessage.chatCount == 0">
|
|
||||||
<div :class="type === 3 ? 'reds-tab-item active tab-item' : 'reds-tab-item tab-item'">
|
|
||||||
<div class="badge-container" @click="toPage(3)">
|
|
||||||
<span>我的消息</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-badge>
|
|
||||||
<el-badge :value="_countMessage.commentCount" :max="99" :hidden="_countMessage.commentCount == 0">
|
|
||||||
<div :class="type === 1 ? 'reds-tab-item active tab-item' : 'reds-tab-item tab-item'">
|
|
||||||
<div class="badge-container" @click="toPage(1)">
|
|
||||||
<span>评论和@</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-badge>
|
|
||||||
<el-badge
|
|
||||||
:value="_countMessage.likeOrCollectionCount"
|
|
||||||
:max="99"
|
|
||||||
:hidden="_countMessage.likeOrCollectionCount == 0"
|
|
||||||
>
|
|
||||||
<div :class="type === 0 ? 'reds-tab-item active tab-item' : 'reds-tab-item tab-item'">
|
|
||||||
<div class="badge-container" @click="toPage(0)">
|
|
||||||
<span>赞和收藏</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-badge>
|
|
||||||
<el-badge :value="_countMessage.followCount" :max="99" :hidden="_countMessage.followCount == 0">
|
|
||||||
<div :class="type === 2 ? 'reds-tab-item active tab-item' : 'reds-tab-item tab-item'">
|
|
||||||
<div class="badge-container" @click="toPage(2)">
|
|
||||||
<span>新增关注</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-badge>
|
|
||||||
</div>
|
|
||||||
<div class="divider" style="margin: 16px 32px 0px"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Message v-if="type == 3"></Message>
|
|
||||||
<Comment v-if="type == 1" @click-main="toMain"></Comment>
|
|
||||||
<LikeCollection v-if="type == 0" @click-main="toMain"></LikeCollection>
|
|
||||||
<Follower v-if="type == 2"></Follower>
|
|
||||||
<!-- <router-view /> -->
|
|
||||||
|
|
||||||
<Main
|
|
||||||
v-show="mainShow"
|
|
||||||
:nid="nid"
|
|
||||||
:nowTime="new Date()"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-main="close"
|
|
||||||
></Main>
|
|
||||||
|
|
||||||
<el-backtop :bottom="80" :right="24">
|
|
||||||
<div class="back-top">
|
|
||||||
<Top style="width: 1.2em; height: 1.2em" color="rgba(51, 51, 51, 0.8)" />
|
|
||||||
</div>
|
|
||||||
</el-backtop>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<el-empty description="用户未登录" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Top } from "@element-plus/icons-vue";
|
|
||||||
import { ref, watchEffect } from "vue";
|
|
||||||
import Message from "@/pages/message/children/message.vue";
|
|
||||||
import LikeCollection from "@/pages/message/children/like-collection.vue";
|
|
||||||
import Follower from "@/pages/message/children/follower.vue";
|
|
||||||
import Comment from "@/pages/message/children/comment.vue";
|
|
||||||
import Main from "@/pages/main/main.vue";
|
|
||||||
import { useImStore } from "@/store/imStore";
|
|
||||||
import { clearMessageCount } from "@/api/im";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
const imStore = useImStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
const type = ref(3);
|
|
||||||
const nid = ref("");
|
|
||||||
const currentUid = ref("");
|
|
||||||
const mainShow = ref(false);
|
|
||||||
const _countMessage = ref({
|
|
||||||
chatCount: 0,
|
|
||||||
likeOrCollectionCount: 0,
|
|
||||||
commentCount: 0,
|
|
||||||
followCount: 0,
|
|
||||||
});
|
|
||||||
const isLogin = ref(false);
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
_countMessage.value = imStore.countMessage;
|
|
||||||
});
|
|
||||||
|
|
||||||
const toPage = (val: number) => {
|
|
||||||
const _countMessage = imStore.countMessage;
|
|
||||||
clearMessageCount(currentUid.value, val).then(() => {
|
|
||||||
switch (val) {
|
|
||||||
case 0:
|
|
||||||
_countMessage.likeOrCollectionCount = 0;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
_countMessage.commentCount = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
_countMessage.followCount = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
imStore.setCountMessage(_countMessage);
|
|
||||||
type.value = val;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
mainShow.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toMain = (val: string) => {
|
|
||||||
nid.value = val;
|
|
||||||
mainShow.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
isLogin.value = userStore.isLogin();
|
|
||||||
if (isLogin.value) {
|
|
||||||
currentUid.value = userStore.getUserInfo().id;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.container {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 24px;
|
|
||||||
padding-top: 72px;
|
|
||||||
width: 67%;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.reds-sticky {
|
|
||||||
top: 72px;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1;
|
|
||||||
width: 40rem;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 72px;
|
|
||||||
padding-top: 16px;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
.reds-tabs-list {
|
|
||||||
justify-content: flex-start;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
position: relative;
|
|
||||||
font-size: 16px;
|
|
||||||
padding: 0 32px;
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #333;
|
|
||||||
background-color: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-tab-item {
|
|
||||||
padding: 0px 16px;
|
|
||||||
margin-right: 0px;
|
|
||||||
font-size: 16px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 40px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: transform 0.3s cubic-bezier(0.2, 0, 0.25, 1);
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.badge-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
margin: 4px 8px;
|
|
||||||
list-style: none;
|
|
||||||
height: 0;
|
|
||||||
border: solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-width: 1px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-top {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 100px;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,607 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="container" id="container">
|
|
||||||
<div v-if="isLogin" class="push-container">
|
|
||||||
<div class="header"><span class="header-icon"></span><span class="header-title">发布图文</span></div>
|
|
||||||
<div class="img-list">
|
|
||||||
<el-upload
|
|
||||||
v-model:file-list="fileList"
|
|
||||||
action="http://localhost:88/api/util/oss/saveBatch/0"
|
|
||||||
list-type="picture-card"
|
|
||||||
multiple
|
|
||||||
:limit="9"
|
|
||||||
:headers="uploadHeader"
|
|
||||||
:auto-upload="false"
|
|
||||||
>
|
|
||||||
<el-icon>
|
|
||||||
<Plus />
|
|
||||||
</el-icon>
|
|
||||||
</el-upload>
|
|
||||||
|
|
||||||
<el-dialog v-model="dialogVisible">
|
|
||||||
<img w-full :src="dialogImageUrl" alt="Preview Image" />
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
<el-divider style="margin: 0.75rem; width: 96%" />
|
|
||||||
<div class="push-content">
|
|
||||||
<el-input
|
|
||||||
v-model="note.title"
|
|
||||||
maxlength="20"
|
|
||||||
show-word-limit
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入标题"
|
|
||||||
class="input-title"
|
|
||||||
/>
|
|
||||||
<el-input
|
|
||||||
v-model="note.content"
|
|
||||||
maxlength="250"
|
|
||||||
show-word-limit
|
|
||||||
:autosize="{ minRows: 4, maxRows: 5 }"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="填写更全面的描述信息,让更多的人看到你吧❤️"
|
|
||||||
/>
|
|
||||||
<div class="tag-list">
|
|
||||||
<el-tag
|
|
||||||
v-for="tag in dynamicTags"
|
|
||||||
:key="tag"
|
|
||||||
closable
|
|
||||||
:disable-transitions="false"
|
|
||||||
@close="handleClose(tag)"
|
|
||||||
class="tag-item"
|
|
||||||
type="danger"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</el-tag>
|
|
||||||
<el-input
|
|
||||||
v-if="inputVisible"
|
|
||||||
ref="InputRef"
|
|
||||||
v-model="inputValue"
|
|
||||||
style="width: 3.125rem"
|
|
||||||
size="small"
|
|
||||||
@keyup.enter="handleInputConfirm"
|
|
||||||
@blur="handleInputBlur"
|
|
||||||
/>
|
|
||||||
<el-button v-else type="warning" size="small" @click="showInput" plain id="tagContainer"> + 标签 </el-button>
|
|
||||||
</div>
|
|
||||||
<!-- <div
|
|
||||||
v-infinite-scroll="loadMoreData"
|
|
||||||
class="scroll-tag-container"
|
|
||||||
v-show="showTagState"
|
|
||||||
:infinite-scroll-distance="50"
|
|
||||||
>
|
|
||||||
<p v-for="(item, index) in selectTagList" :key="index" class="scrollbar-tag-item" @click="selectTag(item)">
|
|
||||||
{{ item.title }}
|
|
||||||
</p>
|
|
||||||
</div> -->
|
|
||||||
<div class="hot-tag">
|
|
||||||
<span class="tag-title-text">推荐标签:</span>
|
|
||||||
<el-tag v-for="tag in hotTags" :key="tag" class="hot-tag-item" type="danger" @click="selectHotTag(tag)">
|
|
||||||
{{ tag }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<el-divider style="margin: 0.75rem; width: 96%" />
|
|
||||||
<div class="categorys">
|
|
||||||
<el-cascader
|
|
||||||
ref="CascaderRef"
|
|
||||||
v-model="categoryList"
|
|
||||||
:options="options"
|
|
||||||
@change="handleChange"
|
|
||||||
:props="props"
|
|
||||||
placeholder="请选择分类"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="btns">
|
|
||||||
<button class="css-fm44j css-osq2ks dyn">
|
|
||||||
<span class="btn-content" @click="addTag"># 话题</span></button
|
|
||||||
><button class="css-fm44j css-osq2ks dyn">
|
|
||||||
<span class="btn-content"><span>@</span> 用户</span></button
|
|
||||||
><button class="css-fm44j css-osq2ks dyn">
|
|
||||||
<span class="btn-content">
|
|
||||||
<div class="smile"></div>
|
|
||||||
表情
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<div class="submit">
|
|
||||||
<el-button type="danger" loading :disabled="true" v-if="pushLoading">发布</el-button>
|
|
||||||
<button class="publishBtn" @click="pubslish()" v-else>
|
|
||||||
<span class="btn-content">发布</span>
|
|
||||||
</button>
|
|
||||||
<button class="clearBtn">
|
|
||||||
<span class="btn-content" @click="resetData">取消</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<el-empty description="用户未登录" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, watch, nextTick, onMounted } from "vue";
|
|
||||||
import { Plus } from "@element-plus/icons-vue";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import type { UploadUserFile, CascaderProps, ElInput } from "element-plus";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
import { getCategoryTreeData } from "@/api/category";
|
|
||||||
import { saveNoteByDTO, getNoteById, updateNoteByDTO } from "@/api/note";
|
|
||||||
import { getTagByKeyword } from "@/api/tag";
|
|
||||||
import { getFileFromUrl } from "@/utils/util";
|
|
||||||
// import Schema from "async-validator";
|
|
||||||
// import Crop from "@/components/Crop.vue";
|
|
||||||
const props: CascaderProps = {
|
|
||||||
label: "title",
|
|
||||||
value: "id",
|
|
||||||
checkStrictly: true, // 允许选择父级节点
|
|
||||||
};
|
|
||||||
const CascaderRef = ref<any>(null);
|
|
||||||
|
|
||||||
// const rules = {
|
|
||||||
// title: { required: true, message: "标题不能为空" },
|
|
||||||
// content: { required: true, message: "内容不能为空" },
|
|
||||||
// category: { required: true, message: "分类不能为空" },
|
|
||||||
// };
|
|
||||||
// const validator = new Schema(rules);
|
|
||||||
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const fileList = ref<UploadUserFile[]>([]);
|
|
||||||
|
|
||||||
const dialogImageUrl = ref("");
|
|
||||||
const dialogVisible = ref(false);
|
|
||||||
const uploadHeader = ref({
|
|
||||||
accessToken: userStore.getToken(),
|
|
||||||
});
|
|
||||||
const categoryList = ref<Array<any>>([]);
|
|
||||||
const options = ref([]);
|
|
||||||
const note = ref<any>({});
|
|
||||||
const showTagState = ref(false);
|
|
||||||
const selectTagList = ref<Array<any>>([]);
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 10;
|
|
||||||
const tagTotal = ref(0);
|
|
||||||
const pushLoading = ref(false);
|
|
||||||
const isLogin = ref(false);
|
|
||||||
const inputValue = ref("");
|
|
||||||
const dynamicTags = ref<Array<string>>([]);
|
|
||||||
const inputVisible = ref(false);
|
|
||||||
const InputRef = ref<InstanceType<typeof ElInput>>();
|
|
||||||
const hotTags = ref<Array<string>>([]);
|
|
||||||
|
|
||||||
const handleClose = (tag: string) => {
|
|
||||||
dynamicTags.value.splice(dynamicTags.value.indexOf(tag), 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputBlur = () => {
|
|
||||||
inputVisible.value = false;
|
|
||||||
// showTagState.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const showInput = () => {
|
|
||||||
inputVisible.value = true;
|
|
||||||
nextTick(() => {
|
|
||||||
InputRef.value!.input!.focus();
|
|
||||||
addTag();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputConfirm = () => {
|
|
||||||
if (inputValue.value) {
|
|
||||||
dynamicTags.value.push(inputValue.value);
|
|
||||||
}
|
|
||||||
inputVisible.value = false;
|
|
||||||
inputValue.value = "";
|
|
||||||
showTagState.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => inputValue.value,
|
|
||||||
() => {
|
|
||||||
addTag();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 监听外部点击
|
|
||||||
onMounted(() => {
|
|
||||||
if (!isLogin.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
document.getElementById("container")!.addEventListener("click", function (e) {
|
|
||||||
var event = e || window.event;
|
|
||||||
var target = event.target || (event.srcElement as any);
|
|
||||||
// if(target.id == "name") {
|
|
||||||
const tagContainer = document.getElementById("tagContainer");
|
|
||||||
if (tagContainer == null) return;
|
|
||||||
|
|
||||||
if (tagContainer.contains(target)) {
|
|
||||||
console.log("in");
|
|
||||||
} else {
|
|
||||||
showTagState.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const addTag = () => {
|
|
||||||
selectTagList.value = [];
|
|
||||||
currentPage.value = 1;
|
|
||||||
setData();
|
|
||||||
showTagState.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setData = () => {
|
|
||||||
getTagByKeyword(currentPage.value, pageSize, inputValue.value).then((res) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
selectTagList.value.push(...records);
|
|
||||||
tagTotal.value = total;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// const selectTag = (val: any) => {
|
|
||||||
// dynamicTags.value.push(val.title);
|
|
||||||
// showTagState.value = false;
|
|
||||||
// inputVisible.value = false;
|
|
||||||
// inputValue.value = "";
|
|
||||||
// };
|
|
||||||
|
|
||||||
const selectHotTag = (val: string) => {
|
|
||||||
dynamicTags.value.push(val);
|
|
||||||
};
|
|
||||||
|
|
||||||
// const loadMoreData = () => {
|
|
||||||
// currentPage.value += 1;
|
|
||||||
// setData();
|
|
||||||
// };
|
|
||||||
|
|
||||||
const handleChange = (ids: Array<any>) => {
|
|
||||||
categoryList.value = ids;
|
|
||||||
// 选中后关闭下拉框
|
|
||||||
CascaderRef.value.togglePopperVisible();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getHotTag = () => {
|
|
||||||
getTagByKeyword(1, pageSize, "").then((res) => {
|
|
||||||
const { records } = res.data;
|
|
||||||
records.forEach((item: any) => {
|
|
||||||
hotTags.value.push(item.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteByIdMethod = (noteId: string) => {
|
|
||||||
getNoteById(noteId).then((res) => {
|
|
||||||
const { data } = res;
|
|
||||||
note.value = data;
|
|
||||||
const urls = JSON.parse(data.urls);
|
|
||||||
urls.forEach((item: string) => {
|
|
||||||
const fileName = item.substring(item.lastIndexOf("/") + 1);
|
|
||||||
|
|
||||||
getFileFromUrl(item, fileName).then((res: any) => {
|
|
||||||
fileList.value.push({ name: fileName, url: item, raw: res });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
categoryList.value.push(data.cpid);
|
|
||||||
categoryList.value.push(data.cid);
|
|
||||||
|
|
||||||
data.tagList.forEach((item: any) => {
|
|
||||||
dynamicTags.value.push(item.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 上传图片功能
|
|
||||||
const pubslish = () => {
|
|
||||||
//验证
|
|
||||||
if (fileList.value.length <= 0 || note.value.title === null || categoryList.value.length <= 0) {
|
|
||||||
ElMessage.error("请选择图片,标签,分类~");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pushLoading.value = true;
|
|
||||||
let params = new FormData();
|
|
||||||
//注意此处对文件数组进行了参数循环添加
|
|
||||||
|
|
||||||
fileList.value.forEach((file: any) => {
|
|
||||||
params.append("uploadFiles", file.raw);
|
|
||||||
});
|
|
||||||
|
|
||||||
note.value.count = fileList.value.length;
|
|
||||||
note.value.type = 1;
|
|
||||||
// note.value.content = document.getElementById("post-textarea")!.innerHTML.replace(/<[^>]*>[^<]*(<[^>]*>)?/gi, "");
|
|
||||||
note.value.cpid = categoryList.value[0];
|
|
||||||
note.value.cid = categoryList.value[1];
|
|
||||||
note.value.tagList = dynamicTags.value;
|
|
||||||
const coverImage = new Image();
|
|
||||||
coverImage.src = fileList.value[0].url!;
|
|
||||||
coverImage.onload = () => {
|
|
||||||
const size = coverImage.width / coverImage.height;
|
|
||||||
note.value.noteCoverHeight = size >= 1.3 ? 200 : 300;
|
|
||||||
const noteData = JSON.stringify(note.value);
|
|
||||||
params.append("noteData", noteData);
|
|
||||||
if (note.value.id !== null && note.value.id !== undefined) {
|
|
||||||
updateNote(params);
|
|
||||||
} else {
|
|
||||||
saveNote(params);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateNote = (params: FormData) => {
|
|
||||||
updateNoteByDTO(params)
|
|
||||||
.then(() => {
|
|
||||||
resetData();
|
|
||||||
ElMessage.success("修改成功");
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage.error("修改失败");
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
pushLoading.value = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveNote = (params: FormData) => {
|
|
||||||
saveNoteByDTO(params)
|
|
||||||
.then(() => {
|
|
||||||
resetData();
|
|
||||||
ElMessage.success("发布成功,请等待审核结果");
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage.error("发布失败");
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
pushLoading.value = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetData = () => {
|
|
||||||
note.value = {};
|
|
||||||
categoryList.value = [];
|
|
||||||
fileList.value = [];
|
|
||||||
pushLoading.value = false;
|
|
||||||
dynamicTags.value = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
isLogin.value = userStore.isLogin();
|
|
||||||
if (isLogin.value) {
|
|
||||||
const noteId = route.query.noteId as string;
|
|
||||||
if (noteId !== "" && noteId !== undefined) {
|
|
||||||
getNoteByIdMethod(noteId);
|
|
||||||
}
|
|
||||||
getCategoryTreeData().then((res) => {
|
|
||||||
options.value = res.data;
|
|
||||||
});
|
|
||||||
getHotTag();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
:deep(.el-upload-list--picture-card .el-upload-list__item) {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-upload-list__item.is-success .el-upload-list__item-status-label) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-upload--picture-card) {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
flex: 1;
|
|
||||||
padding-top: 72px;
|
|
||||||
|
|
||||||
.push-container {
|
|
||||||
margin-left: 11vw;
|
|
||||||
margin-top: 1vw;
|
|
||||||
width: 720px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
box-shadow: var(--el-box-shadow-lighter);
|
|
||||||
|
|
||||||
.header {
|
|
||||||
padding: 15px 20px;
|
|
||||||
line-height: 16px;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
position: relative;
|
|
||||||
top: 2px;
|
|
||||||
display: inline-block;
|
|
||||||
width: 6px;
|
|
||||||
height: 16px;
|
|
||||||
background: #3a64ff;
|
|
||||||
|
|
||||||
border-radius: 3px;
|
|
||||||
margin-right: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-list {
|
|
||||||
width: 650px;
|
|
||||||
margin: auto;
|
|
||||||
padding: 6px 6px 6px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.push-content {
|
|
||||||
padding: 6px 12px 6px 12px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.hot-tag {
|
|
||||||
.tag-title-text {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #484848;
|
|
||||||
margin: 0.125rem 0;
|
|
||||||
}
|
|
||||||
.hot-tag-item {
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0.3125rem 0.3125rem 0 0.3125rem;
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
transform: scale(1.2); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-list {
|
|
||||||
margin: 0.825rem 0;
|
|
||||||
.tag-item {
|
|
||||||
margin-right: 0.3125rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.scroll-tag-container {
|
|
||||||
position: absolute;
|
|
||||||
width: 98%;
|
|
||||||
background-color: #fff;
|
|
||||||
z-index: 99999;
|
|
||||||
border: 1px solid #f4f4f4;
|
|
||||||
height: 300px;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.scrollbar-tag-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 30px;
|
|
||||||
margin: 10px;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding-left: 2px;
|
|
||||||
color: #484848;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scrollbar-tag-item:hover {
|
|
||||||
background-color: #f8f8f8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-title {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-content {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content:empty::before {
|
|
||||||
content: attr(placeholder);
|
|
||||||
color: #ccc;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
cursor: text;
|
|
||||||
margin-top: 10px;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 90px;
|
|
||||||
max-height: 300px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 6px 12px 22px;
|
|
||||||
outline: none;
|
|
||||||
overflow-y: auto;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content:focus,
|
|
||||||
.post-content:hover {
|
|
||||||
border: 1px solid #3a64ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btns {
|
|
||||||
padding: 0 12px 10px 12px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
min-width: 62px;
|
|
||||||
width: 62px;
|
|
||||||
margin: 0 6px 0 0;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.css-fm44j {
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
appearance: none;
|
|
||||||
font-family:
|
|
||||||
RedNum,
|
|
||||||
RedZh,
|
|
||||||
RedEn,
|
|
||||||
-apple-system;
|
|
||||||
vertical-align: middle;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid rgb(217, 217, 217);
|
|
||||||
outline: none;
|
|
||||||
user-select: none;
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background-color: white;
|
|
||||||
color: rgb(38, 38, 38);
|
|
||||||
height: 24px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.categorys {
|
|
||||||
padding: 0 12px 10px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit {
|
|
||||||
padding: 10px 12px 10px 12px;
|
|
||||||
margin-top: 10px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 100px;
|
|
||||||
height: 36px;
|
|
||||||
font-size: 15px;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 250px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transition:
|
|
||||||
background-color 0.3s,
|
|
||||||
color 0.3s; /* 添加过渡效果 */
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
transform: scale(1.05); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.publishBtn {
|
|
||||||
background-color: #ff2442;
|
|
||||||
color: #fff;
|
|
||||||
border-radius: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearBtn {
|
|
||||||
border-radius: 24px;
|
|
||||||
margin-left: 10px;
|
|
||||||
border: 1px solid rgb(217, 217, 217);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,787 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="feeds-page">
|
|
||||||
<div class="middle">
|
|
||||||
<div id="search-type">
|
|
||||||
<div data-v-7f9e6aac="" class="scroll-container channel-scroll-container">
|
|
||||||
<div class="content-container">
|
|
||||||
<div :class="typeClass == 0 ? 'channel active' : 'channel'" @click="getNoteByType(0)">全部</div>
|
|
||||||
<div :class="typeClass == 1 ? 'channel active' : 'channel'" @click="getNoteByType(1)">最热</div>
|
|
||||||
<div :class="typeClass == 2 ? 'channel active' : 'channel'" @click="getNoteByType(2)">最新</div>
|
|
||||||
<div style="line-height: 40px">|</div>
|
|
||||||
<div :class="typeClass == 3 ? 'channel active' : 'channel'" @click="getUserData">用户</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="filter-box">
|
|
||||||
<div>
|
|
||||||
<div class="filter">
|
|
||||||
<!-- <span>综合</span> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="divider rec-filter"></div>
|
|
||||||
<div class="note-container" v-if="!isShowUser">
|
|
||||||
<div class="channel-container">
|
|
||||||
<div class="scroll-container channel-scroll-container">
|
|
||||||
<div class="content-container">
|
|
||||||
<div :class="categoryClass == '0' ? 'channel active' : 'channel'" @click="getNoteList">推荐</div>
|
|
||||||
<div
|
|
||||||
:class="categoryClass == item.id ? 'channel active' : 'channel'"
|
|
||||||
v-for="item in categoryList"
|
|
||||||
:key="item.id"
|
|
||||||
@click="getNoteListByCategory(item.id)"
|
|
||||||
>
|
|
||||||
{{ item.title }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="loading-container"></div>
|
|
||||||
<div
|
|
||||||
class="feeds-container"
|
|
||||||
v-infinite-scroll="loadMoreData"
|
|
||||||
:infinite-scroll-distance="50"
|
|
||||||
:infinite-scroll-disabled="mainShow || loadingMore"
|
|
||||||
>
|
|
||||||
<div class="feeds-loading-top animate__animated animate__zoomIn animate__delay-0.5s" v-show="topLoading">
|
|
||||||
<Loading style="width: 1.2em; height: 1.2em"></Loading>
|
|
||||||
</div>
|
|
||||||
<Waterfall
|
|
||||||
:list="noteList"
|
|
||||||
:width="options.width"
|
|
||||||
:gutter="options.gutter"
|
|
||||||
:hasAroundGutter="options.hasAroundGutter"
|
|
||||||
:animation-effect="options.animationEffect"
|
|
||||||
:animation-duration="options.animationDuration"
|
|
||||||
:animation-delay="options.animationDelay"
|
|
||||||
:breakpoints="options.breakpoints"
|
|
||||||
style="min-width: 46.25rem"
|
|
||||||
>
|
|
||||||
<template #item="{ item }">
|
|
||||||
<el-skeleton style="width: 15rem" :loading="!item.isLoading" animated>
|
|
||||||
<template #template>
|
|
||||||
<el-image
|
|
||||||
:src="item.noteCover"
|
|
||||||
:style="{
|
|
||||||
width: '15rem',
|
|
||||||
maxHeight: '18.75rem',
|
|
||||||
height: item.noteCoverHeight + 'px',
|
|
||||||
borderRadius: '.5rem',
|
|
||||||
}"
|
|
||||||
@load="handleLoad(item)"
|
|
||||||
></el-image>
|
|
||||||
<div style="padding: 0.875rem">
|
|
||||||
<el-skeleton-item variant="h3" style="width: 100%" />
|
|
||||||
<div style="display: flex; align-items: center; margin-top: 0.125rem; height: 1rem">
|
|
||||||
<el-skeleton style="--el-skeleton-circle-size: 1.25rem">
|
|
||||||
<template #template>
|
|
||||||
<el-skeleton-item variant="circle" />
|
|
||||||
</template>
|
|
||||||
</el-skeleton>
|
|
||||||
<el-skeleton-item variant="text" style="margin-left: 0.625rem" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default>
|
|
||||||
<div class="card" style="max-width: 15rem">
|
|
||||||
<el-image
|
|
||||||
:src="item.noteCover"
|
|
||||||
:style="{
|
|
||||||
width: '15rem',
|
|
||||||
maxHeight: '18.75rem',
|
|
||||||
height: item.noteCoverHeight + 'px',
|
|
||||||
borderRadius: '.5rem',
|
|
||||||
}"
|
|
||||||
fit="cover"
|
|
||||||
@click="toMain(item.id)"
|
|
||||||
></el-image>
|
|
||||||
<div class="footer">
|
|
||||||
<a class="title">
|
|
||||||
<span>{{ item.title }}</span>
|
|
||||||
</a>
|
|
||||||
<div class="author-wrapper">
|
|
||||||
<a class="author">
|
|
||||||
<img class="author-avatar" :src="item.avatar" />
|
|
||||||
<span class="name">{{ item.username }}</span>
|
|
||||||
</a>
|
|
||||||
<span class="like-wrapper like-active">
|
|
||||||
<i
|
|
||||||
class="iconfont icon-follow-fill"
|
|
||||||
:style="{ width: '1em', height: '1em', color: item.isLike ? 'red' : 'black' }"
|
|
||||||
v-if="item.isLike"
|
|
||||||
>
|
|
||||||
</i>
|
|
||||||
<i class="iconfont icon-follow" style="width: 1em; height: 1em" v-else></i>
|
|
||||||
<span class="count">{{ item.likeCount }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-skeleton>
|
|
||||||
</template>
|
|
||||||
</Waterfall>
|
|
||||||
|
|
||||||
<div class="feeds-loading">
|
|
||||||
<Loading style="width: 1.2em; height: 1.2em" v-show="botLoading"></Loading>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="user-container" v-else>
|
|
||||||
<ul class="agree-container" v-infinite-scroll="loadMoreUser" :infinite-scroll-distance="20">
|
|
||||||
<li class="agree-item" v-for="(item, index) in userDataList" :key="index">
|
|
||||||
<a class="user-avatar">
|
|
||||||
<img class="avatar-item" :src="item.avatar" @click="toUser(item.id)" />
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<div class="info">
|
|
||||||
<div class="user-info">
|
|
||||||
<a class>{{ item.username }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="interaction-hint">
|
|
||||||
<span>粉丝:</span><span>{{ item.fanCount }}</span
|
|
||||||
> | <span>关注: </span><span>{{ item.followerCount }}</span
|
|
||||||
> | <span>作品: </span><span>{{ item.trendCount }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="extra">
|
|
||||||
<el-button type="info" round size="large" v-if="item.isFollow" @click="follow(item.uid, index, 1)"
|
|
||||||
>已关注</el-button
|
|
||||||
>
|
|
||||||
<el-button type="danger" round size="large" v-else @click="follow(item.uid, index, -1)">回关</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FloatingBtn @click-refresh="refresh"></FloatingBtn>
|
|
||||||
<Main
|
|
||||||
v-show="mainShow"
|
|
||||||
:nid="nid"
|
|
||||||
:nowTime="new Date()"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-main="close"
|
|
||||||
>
|
|
||||||
</Main>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { Waterfall } from "vue-waterfall-plugin-next";
|
|
||||||
import "vue-waterfall-plugin-next/dist/style.css";
|
|
||||||
import { ref, watch } from "vue";
|
|
||||||
import { getNoteByDTO, getCategoryAgg } from "@/api/search";
|
|
||||||
import type { NoteDTO, NoteSearch } from "@/type/note";
|
|
||||||
import type { Category } from "@/type/category";
|
|
||||||
import Main from "@/pages/main/main.vue";
|
|
||||||
import FloatingBtn from "@/components/FloatingBtn.vue";
|
|
||||||
import { options } from "@/constant/constant";
|
|
||||||
import Loading from "@/components/Loading.vue";
|
|
||||||
import { refreshTab } from "@/utils/util";
|
|
||||||
import { useRoute, useRouter } from "vue-router";
|
|
||||||
import { getUserByKeyword } from "@/api/user";
|
|
||||||
import { followById } from "@/api/follower";
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
const topLoading = ref(false);
|
|
||||||
const botLoading = ref(false);
|
|
||||||
const noteList = ref<Array<NoteSearch>>([]);
|
|
||||||
const categoryList = ref<Array<Category>>([]);
|
|
||||||
const currentPage = ref(1);
|
|
||||||
const pageSize = 20;
|
|
||||||
const noteTotal = ref(0);
|
|
||||||
const currentUserPage = ref(1);
|
|
||||||
const userPageSize = 15;
|
|
||||||
const categoryClass = ref("0");
|
|
||||||
const typeClass = ref(0);
|
|
||||||
const mainShow = ref(false);
|
|
||||||
const nid = ref("");
|
|
||||||
const noteDTO = ref<NoteDTO>({
|
|
||||||
keyword: "",
|
|
||||||
type: 0,
|
|
||||||
cid: "",
|
|
||||||
cpid: "",
|
|
||||||
});
|
|
||||||
const loadingMore = ref(false);
|
|
||||||
const isShowUser = ref(false);
|
|
||||||
const userDataList = ref<Array<any>>([]);
|
|
||||||
const userTotal = ref(0);
|
|
||||||
|
|
||||||
const getUserData = () => {
|
|
||||||
typeClass.value = 3;
|
|
||||||
isShowUser.value = true;
|
|
||||||
currentUserPage.value = 1;
|
|
||||||
userDataList.value = [];
|
|
||||||
getUserByKeyword(currentUserPage.value, userPageSize, noteDTO.value.keyword).then((res: any) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
userDataList.value.push(...records);
|
|
||||||
userTotal.value = total;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const follow = (fid: string, index: number, type: number) => {
|
|
||||||
followById(fid).then(() => {
|
|
||||||
userDataList.value[index].isFollow = type == -1;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMoreUser = () => {
|
|
||||||
botLoading.value = true;
|
|
||||||
loadingMore.value = true;
|
|
||||||
currentUserPage.value += 1;
|
|
||||||
new Promise((resolve) => {
|
|
||||||
getUserByKeyword(currentUserPage.value, userPageSize, noteDTO.value.keyword).then((res: any) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
userDataList.value.push(...records);
|
|
||||||
userTotal.value = total;
|
|
||||||
resolve(false);
|
|
||||||
});
|
|
||||||
}).then((data) => {
|
|
||||||
loadingMore.value = data as boolean;
|
|
||||||
setTimeout(() => {
|
|
||||||
botLoading.value = false;
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [route.query.keyword],
|
|
||||||
(newVal) => {
|
|
||||||
noteDTO.value.keyword = newVal[0] as string;
|
|
||||||
noteDTO.value.cid = "";
|
|
||||||
noteDTO.value.type = 0;
|
|
||||||
categoryClass.value = "0";
|
|
||||||
isShowUser.value = false;
|
|
||||||
typeClass.value = 0;
|
|
||||||
getNoteListByKeyword();
|
|
||||||
getCategoryData();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const getNoteByType = (type: number) => {
|
|
||||||
isShowUser.value = false;
|
|
||||||
noteDTO.value.type = type;
|
|
||||||
typeClass.value = type;
|
|
||||||
getNoteListByKeyword();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toMain = (noteId: string) => {
|
|
||||||
// router.push({ name: "main", state: { nid: nid } });
|
|
||||||
nid.value = noteId;
|
|
||||||
mainShow.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
mainShow.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLoad = (item: any) => {
|
|
||||||
item.isLoading = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const toUser = (uid: string) => {
|
|
||||||
router.push({ name: "user", query: { uid: uid } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const refresh = () => {
|
|
||||||
// 使用回调函数优化代码
|
|
||||||
refreshTab(() => {
|
|
||||||
topLoading.value = true;
|
|
||||||
isShowUser.value = false;
|
|
||||||
typeClass.value = 0;
|
|
||||||
currentPage.value = 1;
|
|
||||||
currentUserPage.value = 1;
|
|
||||||
noteList.value = [];
|
|
||||||
userDataList.value = [];
|
|
||||||
setTimeout(() => {
|
|
||||||
getNoteList();
|
|
||||||
topLoading.value = false;
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadMoreData = () => {
|
|
||||||
botLoading.value = true;
|
|
||||||
loadingMore.value = true;
|
|
||||||
currentPage.value += 1;
|
|
||||||
new Promise((resolve) => {
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
resolve(false);
|
|
||||||
});
|
|
||||||
}).then((data) => {
|
|
||||||
loadingMore.value = data as boolean;
|
|
||||||
setTimeout(() => {
|
|
||||||
botLoading.value = false;
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setData = (res: any) => {
|
|
||||||
const { records, total } = res.data;
|
|
||||||
noteTotal.value = total;
|
|
||||||
noteList.value.push(...records);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteList = () => {
|
|
||||||
noteDTO.value.type = 0;
|
|
||||||
noteDTO.value.cid = "";
|
|
||||||
categoryClass.value = "0";
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteListByCategory = (id: string) => {
|
|
||||||
categoryClass.value = id;
|
|
||||||
noteDTO.value.cid = id;
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNoteListByKeyword = () => {
|
|
||||||
noteList.value = [] as Array<any>;
|
|
||||||
currentPage.value = 1;
|
|
||||||
getNoteByDTO(currentPage.value, pageSize, noteDTO.value).then((res) => {
|
|
||||||
setData(res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCategoryData = () => {
|
|
||||||
getCategoryAgg(noteDTO.value).then((res: any) => {
|
|
||||||
categoryList.value = res.data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
const keyword = route.query.keyword as string;
|
|
||||||
if (keyword.trim().length > 0) {
|
|
||||||
noteDTO.value.keyword = keyword as string;
|
|
||||||
noteDTO.value.cid = "";
|
|
||||||
categoryClass.value = "0";
|
|
||||||
getNoteListByKeyword();
|
|
||||||
getCategoryData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.feeds-page {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 1.5rem;
|
|
||||||
padding-top: 4.5rem;
|
|
||||||
height: 100vh;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.middle {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
.channel-scroll-container {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
height: 2.5rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: 4.5rem;
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
display: flex;
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.channel {
|
|
||||||
height: 2.5rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.active {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.active,
|
|
||||||
.channel:hover {
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 62.4375rem;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.filter {
|
|
||||||
height: 2.5rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 120%;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider.rec-filter {
|
|
||||||
height: 0.0625rem;
|
|
||||||
background: rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
|
|
||||||
.channel-scroll-container {
|
|
||||||
backdrop-filter: blur(1.25rem);
|
|
||||||
background-color: transparent;
|
|
||||||
width: calc(100vw - 1.5rem);
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
height: 2.5rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
height: 4.5rem;
|
|
||||||
|
|
||||||
.content-container::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
display: flex;
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
|
|
||||||
.active {
|
|
||||||
font-weight: 600;
|
|
||||||
background: rgba(226, 86, 105, 0.06);
|
|
||||||
color: #ff2442;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel {
|
|
||||||
height: 2.5rem;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
//background: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 0.625rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feeds-container {
|
|
||||||
position: relative;
|
|
||||||
transition: width 0.5s;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
.feeds-loading {
|
|
||||||
margin: 3vh;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feeds-loading-top {
|
|
||||||
text-align: center;
|
|
||||||
line-height: 6vh;
|
|
||||||
height: 6vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noteImg {
|
|
||||||
width: 15rem;
|
|
||||||
max-height: 18.75rem;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
padding: 0.75rem;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
word-break: break-all;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
overflow: hidden;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 140%;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 1.25rem;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
font-size: 0.75rem;
|
|
||||||
transition: color 1s;
|
|
||||||
|
|
||||||
.author {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: inherit;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin-right: 0.75rem;
|
|
||||||
|
|
||||||
.author-avatar {
|
|
||||||
margin-right: 0.375rem;
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
border: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
flex-shrink: 0;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.like-wrapper {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.count {
|
|
||||||
margin-left: 0.125rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-btn-sets {
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 2.5rem;
|
|
||||||
grid-gap: 0.5rem;
|
|
||||||
gap: 0.5rem;
|
|
||||||
right: 1.5rem;
|
|
||||||
bottom: 1.5rem;
|
|
||||||
|
|
||||||
.back-top {
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
background: #fff;
|
|
||||||
border: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
border-radius: 6.25rem;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
// transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reload {
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
background: #fff;
|
|
||||||
border: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
box-shadow:
|
|
||||||
0 0.125rem 0.5rem 0 rgba(0, 0, 0, 0.1),
|
|
||||||
0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 6.25rem;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
//transition: background 0.2s;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.agree-container {
|
|
||||||
.agree-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-top: 1.5rem;
|
|
||||||
|
|
||||||
.user-avatar {
|
|
||||||
margin-right: 1.5rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
.avatar-item {
|
|
||||||
width: 3rem;
|
|
||||||
height: 3rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-bottom: 0.75rem;
|
|
||||||
border-bottom: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
.info {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 0.25rem;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #333;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-hint {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interaction-content {
|
|
||||||
display: flex;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: #333;
|
|
||||||
line-height: 140%;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
|
|
||||||
.msg-count {
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
line-height: 1.25rem;
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
color: #fff;
|
|
||||||
background-color: red;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-info::before {
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
margin-right: 0.375rem;
|
|
||||||
width: 0.25rem;
|
|
||||||
height: 1.0625rem;
|
|
||||||
background: rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.extra {
|
|
||||||
min-width: 3rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-left: 1.5rem;
|
|
||||||
|
|
||||||
.follow-button {
|
|
||||||
width: 6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new.large {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 1rem;
|
|
||||||
padding: 0 1.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new.primary {
|
|
||||||
background-color: #ff2e4d;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-button-new {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
white-space: nowrap;
|
|
||||||
outline: none;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
vertical-align: middle;
|
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 6.25rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,145 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="note-detail-mask"
|
|
||||||
style="transition: background-color 0.4s ease 0s;
|
|
||||||
hsla(0,0%,100%,0.98)"
|
|
||||||
>
|
|
||||||
<div class="note-container">
|
|
||||||
<div class="bug-info">
|
|
||||||
<el-input v-model="emailVal" placeholder="请输入邮箱" style="margin-bottom: 0.625rem" />
|
|
||||||
<el-input
|
|
||||||
v-model="content"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="有什么想对博主说"
|
|
||||||
:autosize="{ minRows: 5, maxRows: 8 }"
|
|
||||||
style="margin-bottom: 1.25rem"
|
|
||||||
/>
|
|
||||||
<el-button type="primary" @click="submit" style="width: 6.25rem"> 发送 </el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="close-cricle" @click="close">
|
|
||||||
<div class="close close-mask-white">
|
|
||||||
<Close style="width: 1.2em; height: 1.2em; color: rgba(51, 51, 51, 0.8)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="back-desk"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { Close } from "@element-plus/icons-vue";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { toUp } from "@/api/util";
|
|
||||||
|
|
||||||
const emailVal = ref("");
|
|
||||||
const content = ref("");
|
|
||||||
|
|
||||||
const emit = defineEmits(["clickToUp"]);
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
emit("clickToUp");
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = () => {
|
|
||||||
const reg = /^([a-zA-Z]|[0-9])(\w|-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/;
|
|
||||||
if (!reg.test(emailVal.value)) {
|
|
||||||
ElMessage.warning("请输入正确的邮箱");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content.value === "") {
|
|
||||||
ElMessage.warning("请输入内容");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dto = {} as any;
|
|
||||||
dto.email = emailVal.value;
|
|
||||||
dto.content = content.value;
|
|
||||||
toUp(dto).then(() => {
|
|
||||||
content.value = "";
|
|
||||||
ElMessage.success("发送成功");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
.note-detail-mask {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
display: flex;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
z-index: 20;
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.back-desk {
|
|
||||||
position: fixed;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
opacity: 0.5;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
z-index: 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-cricle {
|
|
||||||
left: 40vw;
|
|
||||||
top: 1.3vw;
|
|
||||||
position: fixed;
|
|
||||||
display: flex;
|
|
||||||
z-index: 100;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.close-mask-white {
|
|
||||||
box-shadow:
|
|
||||||
0 0.125rem 0.5rem 0 rgba(0, 0, 0, 0.04),
|
|
||||||
0 0.0625rem 0.125rem 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border: 0.0625rem solid rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.close {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 100%;
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
border-radius: 2.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-container {
|
|
||||||
margin-left: 38vw;
|
|
||||||
width: 20%;
|
|
||||||
height: 50%;
|
|
||||||
transition:
|
|
||||||
transform 0.4s ease 0s,
|
|
||||||
width 0.4s ease 0s;
|
|
||||||
transform: translate(6.5rem, 2rem) scale(1);
|
|
||||||
overflow: visible;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
box-shadow:
|
|
||||||
0 0.5rem 4rem 0 rgba(0, 0, 0, 0.04),
|
|
||||||
0 0.0625rem 0.25rem 0 rgba(0, 0, 0, 0.02);
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
background: #f8f8f8;
|
|
||||||
transform-origin: left top;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.bug-info {
|
|
||||||
width: 80%;
|
|
||||||
height: 80%;
|
|
||||||
margin: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,557 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="user-page">
|
|
||||||
<div class="user">
|
|
||||||
<div class="user-info">
|
|
||||||
<div class="avatar">
|
|
||||||
<div class="avatar-wrapper">
|
|
||||||
<img :src="userInfo.avatar" class="user-image" style="border: 0.0625rem solid rgba(0, 0, 0, 0.08)" />
|
|
||||||
<div class="img-edit">
|
|
||||||
<el-upload
|
|
||||||
v-show="uid === currentUid"
|
|
||||||
class="upload-demo"
|
|
||||||
:action="fileAction"
|
|
||||||
:show-file-list="false"
|
|
||||||
:on-success="handleAvatarSuccess"
|
|
||||||
:headers="uploadHeader"
|
|
||||||
>
|
|
||||||
<button class="btn-avatr">更换</button>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info-part">
|
|
||||||
<div class="info">
|
|
||||||
<div class="basic-info">
|
|
||||||
<div class="user-basic">
|
|
||||||
<div class="user-nickname">
|
|
||||||
<div class="user-name" v-if="uid === currentUid">
|
|
||||||
<span v-if="!_isEditInfo"> {{ userInfo.username }}</span>
|
|
||||||
<el-input
|
|
||||||
v-else
|
|
||||||
v-model="userInfo.username"
|
|
||||||
style="width: 15rem"
|
|
||||||
maxlength="10"
|
|
||||||
placeholder="Please input"
|
|
||||||
show-word-limit
|
|
||||||
@keyup.enter="confirmUserInfo"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
:icon="Edit"
|
|
||||||
v-show="!_isEditInfo"
|
|
||||||
@click="_isEditInfo = true"
|
|
||||||
circle
|
|
||||||
size="small"
|
|
||||||
style="margin-left: 0.3125rem"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="user-name" v-else>
|
|
||||||
{{ userInfo.username }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="user-content">
|
|
||||||
<span class="user-redId">小红书号:{{ userInfo.hsId }}</span
|
|
||||||
><span class="user-IP"> IP属地:{{ userInfo.address }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="user-desc">
|
|
||||||
<div v-if="!_isEditInfo">
|
|
||||||
<span v-if="userInfo.description === null">这个人什么都没有写~</span>
|
|
||||||
<span v-else>{{ userInfo.description }}</span>
|
|
||||||
</div>
|
|
||||||
<el-input
|
|
||||||
v-else
|
|
||||||
v-model="userInfo.description"
|
|
||||||
maxlength="250"
|
|
||||||
placeholder="Please input"
|
|
||||||
@keyup.enter="confirmUserInfo"
|
|
||||||
show-word-limit
|
|
||||||
:autosize="true"
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="user-tags">
|
|
||||||
<el-tag
|
|
||||||
style="margin-left: 0.3125rem"
|
|
||||||
v-for="tag in tagList"
|
|
||||||
:key="tag"
|
|
||||||
:closable="uid === currentUid"
|
|
||||||
:disable-transitions="false"
|
|
||||||
@close="handleClose(tag)"
|
|
||||||
effect="light"
|
|
||||||
type="info"
|
|
||||||
round
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</el-tag>
|
|
||||||
<el-input
|
|
||||||
v-if="inputVisible"
|
|
||||||
ref="InputRef"
|
|
||||||
v-model="inputTagValue"
|
|
||||||
style="width: 3.125rem; margin-left: 0.3125rem"
|
|
||||||
size="small"
|
|
||||||
@keyup.enter="handleInputConfirm"
|
|
||||||
@blur="handleInputConfirm"
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
style="margin-left: 0.3125rem"
|
|
||||||
v-else
|
|
||||||
class="button-new-tag"
|
|
||||||
size="small"
|
|
||||||
@click="showInput"
|
|
||||||
round
|
|
||||||
v-show="uid === currentUid"
|
|
||||||
>
|
|
||||||
+
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<div class="data-info">
|
|
||||||
<div class="user-interactions">
|
|
||||||
<div>
|
|
||||||
<span class="count">{{ userInfo.trendCount }}</span
|
|
||||||
><span class="shows">作品</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="count">{{ userInfo.followerCount }}</span
|
|
||||||
><span class="shows">关注</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="count">{{ userInfo.fanCount }}</span
|
|
||||||
><span class="shows">粉丝</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="follow"></div>
|
|
||||||
</div>
|
|
||||||
<div class="tool-btn" v-show="uid !== currentUid">
|
|
||||||
<el-button :icon="ChatLineRound" circle @click="chatShow = true" />
|
|
||||||
<el-button type="info" round v-if="_isFollow" @click="follow(uid, 1)">已关注</el-button>
|
|
||||||
<el-button type="danger" round v-else @click="follow(uid, 0)">关注</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="reds-sticky-box user-page-sticky" style="--1ee3a37c: all 0.4s cubic-bezier(0.2, 0, 0.25, 1) 0s">
|
|
||||||
<div class="reds-sticky" style="">
|
|
||||||
<div class="tertiary center reds-tabs-list" style="padding: 0rem 0.75rem">
|
|
||||||
<div
|
|
||||||
:class="type == 1 ? 'reds-tab-item active' : 'reds-tab-item'"
|
|
||||||
style="padding: 0rem 1rem; margin-right: 0rem; font-size: 1rem"
|
|
||||||
>
|
|
||||||
<span @click="toPage(1)">笔记</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
:class="type == 2 ? 'reds-tab-item active' : 'reds-tab-item'"
|
|
||||||
style="padding: 0rem 1rem; margin-right: 0rem; font-size: 1rem"
|
|
||||||
>
|
|
||||||
<span @click="toPage(2)">点赞</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
:class="type == 3 ? 'reds-tab-item active' : 'reds-tab-item'"
|
|
||||||
style="padding: 0rem 1rem; margin-right: 0rem; font-size: 1rem"
|
|
||||||
>
|
|
||||||
<span @click="toPage(3)">收藏</span>
|
|
||||||
</div>
|
|
||||||
<div class="active-tag" style="width: 4rem; left: 39.1875rem"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="feeds-tab-container" style="--1ee3a37c: all 0.4s cubic-bezier(0.2, 0, 0.25, 1) 0s">
|
|
||||||
<Chat
|
|
||||||
v-if="chatShow"
|
|
||||||
:acceptUid="uid"
|
|
||||||
class="animate__animated animate__zoomIn animate__delay-0.5s"
|
|
||||||
@click-chat="chatShow = false"
|
|
||||||
>
|
|
||||||
</Chat>
|
|
||||||
<Note :type="type"> </Note>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ChatLineRound, Edit } from "@element-plus/icons-vue";
|
|
||||||
import { ref, nextTick } from "vue";
|
|
||||||
import { getUserById, updateUser } from "@/api/user";
|
|
||||||
import Note from "@/components/Note.vue";
|
|
||||||
import { useUserStore } from "@/store/userStore";
|
|
||||||
import Chat from "@/components/Chat.vue";
|
|
||||||
import { followById, isFollow } from "@/api/follower";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { ElInput, ElMessage, UploadProps } from "element-plus";
|
|
||||||
import { baseURL } from "@/constant/constant";
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const uploadHeader = ref({
|
|
||||||
accessToken: userStore.getToken(),
|
|
||||||
});
|
|
||||||
const currentUid = userStore.getUserInfo().id;
|
|
||||||
const userInfo = ref<any>({});
|
|
||||||
//const uid = history.state.uid;
|
|
||||||
const uid = route.query.uid as string;
|
|
||||||
const type = ref(1);
|
|
||||||
const chatShow = ref(false);
|
|
||||||
const _isFollow = ref(false);
|
|
||||||
const _isEditInfo = ref(false);
|
|
||||||
const tagList = ref<string[]>([]);
|
|
||||||
const inputVisible = ref(false);
|
|
||||||
const InputRef = ref<InstanceType<typeof ElInput>>();
|
|
||||||
const inputTagValue = ref("");
|
|
||||||
const fileAction = baseURL + "web/oss/save/1";
|
|
||||||
|
|
||||||
const showInput = () => {
|
|
||||||
inputVisible.value = true;
|
|
||||||
nextTick(() => {
|
|
||||||
InputRef.value!.input!.focus();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (tag: string) => {
|
|
||||||
tagList.value.splice(tagList.value.indexOf(tag), 1);
|
|
||||||
commonUpdateUser();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInputConfirm = () => {
|
|
||||||
if (inputTagValue.value) {
|
|
||||||
tagList.value.push(inputTagValue.value);
|
|
||||||
commonUpdateUser();
|
|
||||||
}
|
|
||||||
// _isClosable.value = false;
|
|
||||||
inputVisible.value = false;
|
|
||||||
inputTagValue.value = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const commonUpdateUser = () => {
|
|
||||||
// 检查标签数量并处理
|
|
||||||
if (tagList.value.length > 4) {
|
|
||||||
tagList.value.splice(4);
|
|
||||||
ElMessage.warning("最多支持4个标签!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 创建用户DTO对象并赋值
|
|
||||||
let userDTO = {
|
|
||||||
id: userInfo.value.id,
|
|
||||||
avatar: userInfo.value.avatar,
|
|
||||||
username: userInfo.value.username,
|
|
||||||
description: userInfo.value.description,
|
|
||||||
tags: JSON.stringify(tagList.value),
|
|
||||||
};
|
|
||||||
updateUser(userDTO)
|
|
||||||
.then(() => {
|
|
||||||
// 更新用户存储信息
|
|
||||||
ElMessage.success("修改成功~");
|
|
||||||
const user = userStore.getUserInfo();
|
|
||||||
user.avatar = userInfo.value.avatar;
|
|
||||||
userStore.setUserInfo(user);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage.error("更新失败,请稍后再试!");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmUserInfo = () => {
|
|
||||||
_isEditInfo.value = false;
|
|
||||||
commonUpdateUser();
|
|
||||||
};
|
|
||||||
|
|
||||||
const toPage = (val: number) => {
|
|
||||||
type.value = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAvatarSuccess: UploadProps["onSuccess"] = (response) => {
|
|
||||||
userInfo.value.avatar = response.data;
|
|
||||||
commonUpdateUser();
|
|
||||||
};
|
|
||||||
|
|
||||||
const follow = (fid: string, type: number) => {
|
|
||||||
followById(fid).then(() => {
|
|
||||||
_isFollow.value = type == 0;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initData = () => {
|
|
||||||
getUserById(uid).then((res) => {
|
|
||||||
userInfo.value = res.data;
|
|
||||||
if (res.data.tags != null) {
|
|
||||||
tagList.value = JSON.parse(res.data.tags);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
isFollow(uid).then((res) => {
|
|
||||||
_isFollow.value = res.data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
initData();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" scoped>
|
|
||||||
:deep(.el-button:hover) {
|
|
||||||
background-color: #fff;
|
|
||||||
color: black;
|
|
||||||
border-color: #f4f4f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-tag) {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-page {
|
|
||||||
background: #fff;
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
.user {
|
|
||||||
padding-top: 4.5rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.user-info {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 3rem 0;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
.avatar-wrapper {
|
|
||||||
text-align: center;
|
|
||||||
width: 15.6667rem;
|
|
||||||
height: 10.9667rem;
|
|
||||||
|
|
||||||
.user-image {
|
|
||||||
border-radius: 50%;
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 70%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-avatr {
|
|
||||||
border: 0.0625rem solid #f4f4f4;
|
|
||||||
width: 2.875rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
height: 1.75rem;
|
|
||||||
color: #1f1e1e;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
.btn-avatr:hover {
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-part {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.info {
|
|
||||||
@media screen and (min-width: 108rem) {
|
|
||||||
width: 33.3333rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
margin-left: 2rem;
|
|
||||||
|
|
||||||
.basic-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.user-basic {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.user-nickname {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
max-width: calc(100% - 6rem);
|
|
||||||
|
|
||||||
.user-name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
line-height: 120%;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-content {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
line-height: 120%;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
display: flex;
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
|
|
||||||
.user-redId {
|
|
||||||
padding-right: 0.75rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-desc {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 140%;
|
|
||||||
color: #333;
|
|
||||||
margin-top: 1rem;
|
|
||||||
white-space: pre-line;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-tags {
|
|
||||||
height: 1.5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 120%;
|
|
||||||
|
|
||||||
.tag-item :first-child {
|
|
||||||
padding: 0.1875rem 0.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
grid-gap: 0.25rem;
|
|
||||||
gap: 0.25rem;
|
|
||||||
height: 1.125rem;
|
|
||||||
border-radius: 2.5625rem;
|
|
||||||
background: rgba(0, 0, 0, 0.03);
|
|
||||||
height: 1.5rem;
|
|
||||||
line-height: 1.5rem;
|
|
||||||
margin-right: 0.375rem;
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.15); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: 1.25rem;
|
|
||||||
|
|
||||||
.user-interactions {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.count {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
margin-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shows {
|
|
||||||
color: rgba(51, 51, 51, 0.6);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
line-height: 120%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-interactions > div {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.follow {
|
|
||||||
position: absolute;
|
|
||||||
margin-left: auto;
|
|
||||||
display: block;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 10%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.tool-btn {
|
|
||||||
@media screen and (min-width: 108rem) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-btn {
|
|
||||||
@media screen and (min-width: 90.3958rem) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-btn {
|
|
||||||
@media screen and (min-width: 109.375rem) {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-sticky {
|
|
||||||
padding: 1rem 0;
|
|
||||||
z-index: 5 !important;
|
|
||||||
background: hsla(0, 0%, 100%, 0.98);
|
|
||||||
|
|
||||||
.reds-tabs-list {
|
|
||||||
@media screen and (min-width: 108rem) {
|
|
||||||
width: 90.3333rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
position: relative;
|
|
||||||
font-size: 1rem;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.reds-tab-item {
|
|
||||||
padding: 0rem 1rem;
|
|
||||||
margin-right: 0rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 2.5rem;
|
|
||||||
cursor: pointer;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: transform 0.3s cubic-bezier(0.2, 0, 0.25, 1);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
:hover {
|
|
||||||
cursor: pointer; /* 显示小手指针 */
|
|
||||||
transform: scale(1.15); /* 鼠标移入时按钮稍微放大 */
|
|
||||||
}
|
|
||||||
|
|
||||||
.reds-tab-item.active {
|
|
||||||
background-color: rgba(0, 0, 0, 0.03);
|
|
||||||
border-radius: 1.25rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: rgba(51, 51, 51, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.feeds-tab-container {
|
|
||||||
padding-left: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@@ -1,6 +1,6 @@
|
|||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
import Login from "@/pages/login.vue";
|
import Login from "@/views/login.vue";
|
||||||
import Dashboard from "@/pages/dashboard/dashboard.vue";
|
import Dashboard from "@/views/dashboard/dashboard.vue";
|
||||||
|
|
||||||
export const routes = [
|
export const routes = [
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,7 @@ export const routes = [
|
|||||||
{
|
{
|
||||||
name: "index",
|
name: "index",
|
||||||
path: "/index",
|
path: "/index",
|
||||||
component: () => import("@/pages/index.vue"),
|
component: () => import("@/views/index.vue"),
|
||||||
redirect: "/dashboard",
|
redirect: "/dashboard",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -25,27 +25,27 @@ export const routes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/followTrend",
|
path: "/followTrend",
|
||||||
component: () => import("@/pages/follow-trend/follow-trend.vue"),
|
component: () => import("@/views/follow-trend/follow-trend.vue"),
|
||||||
name: "followTrend",
|
name: "followTrend",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/notice",
|
path: "/notice",
|
||||||
component: () => import("@/pages/message/index.vue"),
|
component: () => import("@/views/message/index.vue"),
|
||||||
name: "notice",
|
name: "notice",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/user",
|
path: "/user",
|
||||||
component: () => import("@/pages/user/index.vue"),
|
component: () => import("@/views/user/index.vue"),
|
||||||
name: "user",
|
name: "user",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/push",
|
path: "/push",
|
||||||
component: () => import("@/pages/push/index.vue"),
|
component: () => import("@/views/push/index.vue"),
|
||||||
name: "push",
|
name: "push",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/search",
|
path: "/search",
|
||||||
component: () => import("@/pages/search/index.vue"),
|
component: () => import("@/views/search/index.vue"),
|
||||||
name: "search",
|
name: "search",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -85,7 +85,7 @@ service.interceptors.response.use(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (code == 401) {
|
} else if (code == 401) {
|
||||||
ElMessage.error("登录过期,请重新登录");
|
ElMessage.warning("登录过期,请重新登录");
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
}
|
}
|
||||||
return Promise.reject(response.data);
|
return Promise.reject(response.data);
|
||||||
|
@@ -12,7 +12,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
// build: {
|
// build: {
|
||||||
// rollupOptions: {
|
// rollupOptions: {
|
||||||
// input: resolve(__dirname, 'src/pages/index.html'), // 确保输入路径正确
|
// input: resolve(__dirname, 'src/views/index.html'), // 确保输入路径正确
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// server: {
|
// server: {
|
||||||
|
275
yarn.lock
@@ -7,10 +7,30 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz"
|
"resolved" "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz"
|
||||||
"version" "1.2.6"
|
"version" "1.2.6"
|
||||||
|
|
||||||
"@babel/parser@^7.23.9":
|
"@babel/helper-string-parser@^7.25.9":
|
||||||
"integrity" "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA=="
|
"integrity" "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
|
||||||
"resolved" "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.9.tgz"
|
"resolved" "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz"
|
||||||
"version" "7.23.9"
|
"version" "7.25.9"
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.25.9":
|
||||||
|
"integrity" "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz"
|
||||||
|
"version" "7.25.9"
|
||||||
|
|
||||||
|
"@babel/parser@^7.25.3":
|
||||||
|
"integrity" "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.3.tgz"
|
||||||
|
"version" "7.26.3"
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.26.3"
|
||||||
|
|
||||||
|
"@babel/types@^7.26.3":
|
||||||
|
"integrity" "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@babel/types/-/types-7.26.3.tgz"
|
||||||
|
"version" "7.26.3"
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.25.9"
|
||||||
|
"@babel/helper-validator-identifier" "^7.25.9"
|
||||||
|
|
||||||
"@bassist/utils@^0.4.0":
|
"@bassist/utils@^0.4.0":
|
||||||
"integrity" "sha512-aoFTl0jUjm8/tDZodP41wnEkvB+C5O9NFCuYN/ztL6jSUSsuBkXq90/1ifBm1XhV/zySHgLYlU1+tgo3XtQ+nA=="
|
"integrity" "sha512-aoFTl0jUjm8/tDZodP41wnEkvB+C5O9NFCuYN/ztL6jSUSsuBkXq90/1ifBm1XhV/zySHgLYlU1+tgo3XtQ+nA=="
|
||||||
@@ -53,9 +73,9 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz"
|
"resolved" "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz"
|
||||||
"version" "2.3.1"
|
"version" "2.3.1"
|
||||||
|
|
||||||
"@esbuild/darwin-arm64@0.18.20":
|
"@esbuild/darwin-x64@0.18.20":
|
||||||
"integrity" "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="
|
"integrity" "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="
|
||||||
"resolved" "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz"
|
"resolved" "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz"
|
||||||
"version" "0.18.20"
|
"version" "0.18.20"
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||||
@@ -129,10 +149,10 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz"
|
"resolved" "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz"
|
||||||
"version" "2.0.2"
|
"version" "2.0.2"
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.15":
|
"@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
"integrity" "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
|
"integrity" "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
|
||||||
"resolved" "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
"resolved" "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
|
||||||
"version" "1.4.15"
|
"version" "1.5.0"
|
||||||
|
|
||||||
"@lhlyu/vue-virtual-waterfall@^1.0.2":
|
"@lhlyu/vue-virtual-waterfall@^1.0.2":
|
||||||
"integrity" "sha512-hv0aZNBfkz7TxGLE+SdWGVqjd6/ez0ZPXn1LO/KVdshLr5ORLrZz4tYblTlP7ry7+NrRT5FegflhLD+RrBr9fw=="
|
"integrity" "sha512-hv0aZNBfkz7TxGLE+SdWGVqjd6/ez0ZPXn1LO/KVdshLr5ORLrZz4tYblTlP7ry7+NrRT5FegflhLD+RrBr9fw=="
|
||||||
@@ -2906,47 +2926,47 @@
|
|||||||
"@volar/language-core" "1.11.1"
|
"@volar/language-core" "1.11.1"
|
||||||
"path-browserify" "^1.0.1"
|
"path-browserify" "^1.0.1"
|
||||||
|
|
||||||
"@vue/compiler-core@3.4.18":
|
"@vue/compiler-core@3.5.13":
|
||||||
"integrity" "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ=="
|
"integrity" "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.23.9"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
"entities" "^4.5.0"
|
"entities" "^4.5.0"
|
||||||
"estree-walker" "^2.0.2"
|
"estree-walker" "^2.0.2"
|
||||||
"source-map-js" "^1.0.2"
|
"source-map-js" "^1.2.0"
|
||||||
|
|
||||||
"@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@3.4.18":
|
"@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@3.5.13":
|
||||||
"integrity" "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA=="
|
"integrity" "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-core" "3.4.18"
|
"@vue/compiler-core" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.4.18":
|
"@vue/compiler-sfc@3.5.13":
|
||||||
"integrity" "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw=="
|
"integrity" "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.23.9"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/compiler-core" "3.4.18"
|
"@vue/compiler-core" "3.5.13"
|
||||||
"@vue/compiler-dom" "3.4.18"
|
"@vue/compiler-dom" "3.5.13"
|
||||||
"@vue/compiler-ssr" "3.4.18"
|
"@vue/compiler-ssr" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
"estree-walker" "^2.0.2"
|
"estree-walker" "^2.0.2"
|
||||||
"magic-string" "^0.30.6"
|
"magic-string" "^0.30.11"
|
||||||
"postcss" "^8.4.33"
|
"postcss" "^8.4.48"
|
||||||
"source-map-js" "^1.0.2"
|
"source-map-js" "^1.2.0"
|
||||||
|
|
||||||
"@vue/compiler-ssr@3.4.18":
|
"@vue/compiler-ssr@3.5.13":
|
||||||
"integrity" "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ=="
|
"integrity" "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.18"
|
"@vue/compiler-dom" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"@vue/devtools-api@^6.5.0":
|
"@vue/devtools-api@^6.5.0":
|
||||||
"integrity" "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
|
"integrity" "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA=="
|
||||||
@@ -2968,52 +2988,63 @@
|
|||||||
"path-browserify" "^1.0.1"
|
"path-browserify" "^1.0.1"
|
||||||
"vue-template-compiler" "^2.7.14"
|
"vue-template-compiler" "^2.7.14"
|
||||||
|
|
||||||
"@vue/reactivity@3.4.18":
|
"@vue/reactivity@3.5.13":
|
||||||
"integrity" "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg=="
|
"integrity" "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"@vue/runtime-core@3.4.18":
|
"@vue/runtime-core@3.5.13":
|
||||||
"integrity" "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ=="
|
"integrity" "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.4.18"
|
"@vue/reactivity" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"@vue/runtime-dom@3.4.18":
|
"@vue/runtime-dom@3.5.13":
|
||||||
"integrity" "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA=="
|
"integrity" "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/runtime-core" "3.4.18"
|
"@vue/reactivity" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/runtime-core" "3.5.13"
|
||||||
|
"@vue/shared" "3.5.13"
|
||||||
"csstype" "^3.1.3"
|
"csstype" "^3.1.3"
|
||||||
|
|
||||||
"@vue/server-renderer@3.4.18":
|
"@vue/server-renderer@3.5.13":
|
||||||
"integrity" "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA=="
|
"integrity" "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-ssr" "3.4.18"
|
"@vue/compiler-ssr" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"@vue/shared@^3.3.0", "@vue/shared@3.4.18":
|
"@vue/shared@^3.3.0", "@vue/shared@3.5.13":
|
||||||
"integrity" "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q=="
|
"integrity" "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vue/shared/-/shared-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
|
|
||||||
"@vueuse/core@^10.7.0":
|
"@vueuse/core@^10.7.0":
|
||||||
"integrity" "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ=="
|
"integrity" "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vueuse/core/-/core-10.7.2.tgz"
|
"resolved" "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz"
|
||||||
"version" "10.7.2"
|
"version" "10.11.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/web-bluetooth" "^0.0.20"
|
"@types/web-bluetooth" "^0.0.20"
|
||||||
"@vueuse/metadata" "10.7.2"
|
"@vueuse/metadata" "10.11.1"
|
||||||
"@vueuse/shared" "10.7.2"
|
"@vueuse/shared" "10.11.1"
|
||||||
"vue-demi" ">=0.14.6"
|
"vue-demi" ">=0.14.8"
|
||||||
|
|
||||||
|
"@vueuse/core@^12.3.0":
|
||||||
|
"integrity" "sha512-cnV8QDKZrsyKC7tWjPbeEUz2cD9sa9faxF2YkR8QqNwfofgbOhmfIgvSYmkp+ttSvfOw4E6hLcQx15mRPr0yBA=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@vueuse/core/-/core-12.3.0.tgz"
|
||||||
|
"version" "12.3.0"
|
||||||
|
dependencies:
|
||||||
|
"@types/web-bluetooth" "^0.0.20"
|
||||||
|
"@vueuse/metadata" "12.3.0"
|
||||||
|
"@vueuse/shared" "12.3.0"
|
||||||
|
"vue" "^3.5.13"
|
||||||
|
|
||||||
"@vueuse/core@^8.0.1":
|
"@vueuse/core@^8.0.1":
|
||||||
"integrity" "sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q=="
|
"integrity" "sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q=="
|
||||||
@@ -3035,10 +3066,15 @@
|
|||||||
"@vueuse/shared" "9.13.0"
|
"@vueuse/shared" "9.13.0"
|
||||||
"vue-demi" "*"
|
"vue-demi" "*"
|
||||||
|
|
||||||
"@vueuse/metadata@10.7.2":
|
"@vueuse/metadata@10.11.1":
|
||||||
"integrity" "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ=="
|
"integrity" "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.7.2.tgz"
|
"resolved" "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz"
|
||||||
"version" "10.7.2"
|
"version" "10.11.1"
|
||||||
|
|
||||||
|
"@vueuse/metadata@12.3.0":
|
||||||
|
"integrity" "sha512-M/iQHHjMffOv2npsw2ihlUx1CTiBwPEgb7DzByLq7zpg1+Ke8r7s9p5ybUWc5OIeGewtpY4Xy0R2cKqFqM8hFg=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.3.0.tgz"
|
||||||
|
"version" "12.3.0"
|
||||||
|
|
||||||
"@vueuse/metadata@8.9.4":
|
"@vueuse/metadata@8.9.4":
|
||||||
"integrity" "sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw=="
|
"integrity" "sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw=="
|
||||||
@@ -3050,12 +3086,19 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz"
|
"resolved" "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz"
|
||||||
"version" "9.13.0"
|
"version" "9.13.0"
|
||||||
|
|
||||||
"@vueuse/shared@10.7.2":
|
"@vueuse/shared@10.11.1":
|
||||||
"integrity" "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA=="
|
"integrity" "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA=="
|
||||||
"resolved" "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.7.2.tgz"
|
"resolved" "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz"
|
||||||
"version" "10.7.2"
|
"version" "10.11.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"vue-demi" ">=0.14.6"
|
"vue-demi" ">=0.14.8"
|
||||||
|
|
||||||
|
"@vueuse/shared@12.3.0":
|
||||||
|
"integrity" "sha512-X3YD35GUeW0d5Gajcwv9jdLAJTV2Jdb/Ll6Ii2JIYcKLYZqv5wxyLeKtiQkqWmHg3v0J0ZWjDUMVOw2E7RCXfA=="
|
||||||
|
"resolved" "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.3.0.tgz"
|
||||||
|
"version" "12.3.0"
|
||||||
|
dependencies:
|
||||||
|
"vue" "^3.5.13"
|
||||||
|
|
||||||
"@vueuse/shared@8.9.4":
|
"@vueuse/shared@8.9.4":
|
||||||
"integrity" "sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag=="
|
"integrity" "sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag=="
|
||||||
@@ -4069,12 +4112,12 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"vue" "^3.2.13"
|
"vue" "^3.2.13"
|
||||||
|
|
||||||
"magic-string@^0.30.6":
|
"magic-string@^0.30.11":
|
||||||
"integrity" "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA=="
|
"integrity" "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="
|
||||||
"resolved" "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.7.tgz"
|
"resolved" "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz"
|
||||||
"version" "0.30.7"
|
"version" "0.30.17"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
"make-dir@^2.1.0":
|
"make-dir@^2.1.0":
|
||||||
"integrity" "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="
|
"integrity" "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA=="
|
||||||
@@ -4313,10 +4356,10 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz"
|
"resolved" "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz"
|
||||||
"version" "4.0.0"
|
"version" "4.0.0"
|
||||||
|
|
||||||
"picocolors@^1.0.0":
|
"picocolors@^1.1.1":
|
||||||
"integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
|
"integrity" "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||||
"resolved" "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz"
|
"resolved" "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"
|
||||||
"version" "1.0.0"
|
"version" "1.1.1"
|
||||||
|
|
||||||
"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.3.1":
|
"picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.3.1":
|
||||||
"integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
"integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||||
@@ -4354,14 +4397,14 @@
|
|||||||
"cssesc" "^3.0.0"
|
"cssesc" "^3.0.0"
|
||||||
"util-deprecate" "^1.0.2"
|
"util-deprecate" "^1.0.2"
|
||||||
|
|
||||||
"postcss@^8.4.27", "postcss@^8.4.33":
|
"postcss@^8.4.27", "postcss@^8.4.48":
|
||||||
"integrity" "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA=="
|
"integrity" "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="
|
||||||
"resolved" "https://registry.npmmirror.com/postcss/-/postcss-8.4.35.tgz"
|
"resolved" "https://registry.npmmirror.com/postcss/-/postcss-8.4.49.tgz"
|
||||||
"version" "8.4.35"
|
"version" "8.4.49"
|
||||||
dependencies:
|
dependencies:
|
||||||
"nanoid" "^3.3.7"
|
"nanoid" "^3.3.7"
|
||||||
"picocolors" "^1.0.0"
|
"picocolors" "^1.1.1"
|
||||||
"source-map-js" "^1.0.2"
|
"source-map-js" "^1.2.1"
|
||||||
|
|
||||||
"prelude-ls@^1.2.1":
|
"prelude-ls@^1.2.1":
|
||||||
"integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
"integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
||||||
@@ -4566,10 +4609,10 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz"
|
"resolved" "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz"
|
||||||
"version" "3.0.0"
|
"version" "3.0.0"
|
||||||
|
|
||||||
"source-map-js@^1.0.2", "source-map-js@>=0.6.2 <2.0.0":
|
"source-map-js@^1.2.0", "source-map-js@^1.2.1", "source-map-js@>=0.6.2 <2.0.0":
|
||||||
"integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
"integrity" "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||||
"resolved" "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz"
|
"resolved" "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz"
|
||||||
"version" "1.0.2"
|
"version" "1.2.1"
|
||||||
|
|
||||||
"source-map@~0.6.0":
|
"source-map@~0.6.0":
|
||||||
"integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
"integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||||
@@ -4708,10 +4751,10 @@
|
|||||||
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.7.tgz"
|
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.7.tgz"
|
||||||
"version" "0.14.7"
|
"version" "0.14.7"
|
||||||
|
|
||||||
"vue-demi@>=0.14.6":
|
"vue-demi@>=0.14.8":
|
||||||
"integrity" "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA=="
|
"integrity" "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="
|
||||||
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.7.tgz"
|
"resolved" "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz"
|
||||||
"version" "0.14.7"
|
"version" "0.14.10"
|
||||||
|
|
||||||
"vue-eslint-parser@^9.4.2":
|
"vue-eslint-parser@^9.4.2":
|
||||||
"integrity" "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ=="
|
"integrity" "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ=="
|
||||||
@@ -4782,16 +4825,16 @@
|
|||||||
"element-plus" "2.1.4"
|
"element-plus" "2.1.4"
|
||||||
"vue-router" "4"
|
"vue-router" "4"
|
||||||
|
|
||||||
"vue@*", "vue@^2.0.0 || >=3.0.0", "vue@^2.6.0 || ^3.2.0", "vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.0.0-alpha.9", "vue@^3.2.0", "vue@^3.2.13", "vue@^3.2.25", "vue@^3.3.4", "vue@>=3.0.0", "vue@>=3.2.13", "vue@3.4.18":
|
"vue@*", "vue@^2.0.0 || >=3.0.0", "vue@^2.6.0 || ^3.2.0", "vue@^2.6.14 || ^3.3.0", "vue@^3.0.0-0 || ^2.6.0", "vue@^3.0.0-alpha.9", "vue@^3.2.0", "vue@^3.2.13", "vue@^3.2.25", "vue@^3.3.4", "vue@^3.5.13", "vue@>=3.2.13", "vue@3.5.13":
|
||||||
"integrity" "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A=="
|
"integrity" "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="
|
||||||
"resolved" "https://registry.npmmirror.com/vue/-/vue-3.4.18.tgz"
|
"resolved" "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz"
|
||||||
"version" "3.4.18"
|
"version" "3.5.13"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.18"
|
"@vue/compiler-dom" "3.5.13"
|
||||||
"@vue/compiler-sfc" "3.4.18"
|
"@vue/compiler-sfc" "3.5.13"
|
||||||
"@vue/runtime-dom" "3.4.18"
|
"@vue/runtime-dom" "3.5.13"
|
||||||
"@vue/server-renderer" "3.4.18"
|
"@vue/server-renderer" "3.5.13"
|
||||||
"@vue/shared" "3.4.18"
|
"@vue/shared" "3.5.13"
|
||||||
|
|
||||||
"vue3-lazy@^1.0.0-alpha.1":
|
"vue3-lazy@^1.0.0-alpha.1":
|
||||||
"integrity" "sha512-dpjpKK4DC5q+wZVtS/VY3X6pYBJHxRmYanr20s39RB6o6fvbneQ/DNuz37bipYfEdrEvbSZ95Y2SCexuznQNrQ=="
|
"integrity" "sha512-dpjpKK4DC5q+wZVtS/VY3X6pYBJHxRmYanr20s39RB6o6fvbneQ/DNuz37bipYfEdrEvbSZ95Y2SCexuznQNrQ=="
|
||||||
|