mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-07 18:47:44 +08:00
Merge branch 'main' of github.com:nagisa77/OpenIsle
This commit is contained in:
@@ -77,8 +77,8 @@ public class SecurityConfig {
|
|||||||
"http://127.0.0.1",
|
"http://127.0.0.1",
|
||||||
"http://localhost:8080",
|
"http://localhost:8080",
|
||||||
"http://localhost",
|
"http://localhost",
|
||||||
"http://30.211.97.254:8080",
|
"http://30.211.98.193:8080",
|
||||||
"http://30.211.97.254",
|
"http://30.211.98.193",
|
||||||
"http://192.168.7.70",
|
"http://192.168.7.70",
|
||||||
"http://192.168.7.70:8080",
|
"http://192.168.7.70:8080",
|
||||||
websiteUrl,
|
websiteUrl,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "OpenIsle",
|
"name": "OpenIsle",
|
||||||
"short_name": "OpenIsle",
|
"short_name": "OpenIsle",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
"display": "standalone",
|
"display": "fullscreen",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/icon-192.png",
|
"src": "/icon-192.png",
|
||||||
|
|||||||
@@ -68,9 +68,13 @@ export default {
|
|||||||
.menu-container {}
|
.menu-container {}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
|
/* height: calc(100vh - var(--header-height)); */
|
||||||
|
padding-top: var(--header-height);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
transition: max-width 0.3s ease;
|
transition: max-width 0.3s ease;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
min-height: calc(100vh - var(--header-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
.content.menu-open {
|
.content.menu-open {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
--header-text-color: black;
|
--header-text-color: black;
|
||||||
--menu-background-color: white;
|
--menu-background-color: white;
|
||||||
--background-color: white;
|
--background-color: white;
|
||||||
--background-color-blur: rgba(255, 255, 255, 0.57);
|
/* --background-color-blur: rgba(255, 255, 255, 0.57); */
|
||||||
|
--background-color-blur: var(--background-color);
|
||||||
--menu-border-color: lightgray;
|
--menu-border-color: lightgray;
|
||||||
--normal-border-color: lightgray;
|
--normal-border-color: lightgray;
|
||||||
--menu-selected-background-color: rgba(208, 250, 255, 0.659);
|
--menu-selected-background-color: rgba(208, 250, 255, 0.659);
|
||||||
@@ -34,7 +35,8 @@
|
|||||||
--header-text-color: white;
|
--header-text-color: white;
|
||||||
--menu-background-color: #333;
|
--menu-background-color: #333;
|
||||||
--background-color: #333;
|
--background-color: #333;
|
||||||
--background-color-blur: #333333a4;
|
/* --background-color-blur: #333333a4; */
|
||||||
|
--background-color-blur: var(--background-color);
|
||||||
--menu-border-color: #555;
|
--menu-border-color: #555;
|
||||||
--normal-border-color: #555;
|
--normal-border-color: #555;
|
||||||
--menu-selected-background-color: rgba(255, 255, 255, 0.1);
|
--menu-selected-background-color: rgba(255, 255, 255, 0.1);
|
||||||
@@ -54,10 +56,10 @@ body {
|
|||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
background-color: var(--normal-background-color);
|
background-color: var(--normal-background-color);
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
overflow: hidden; /* 禁止滚动 */
|
/* 禁止滚动 */
|
||||||
|
/* overflow: hidden; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*************************
|
/*************************
|
||||||
* Vditor 自定义皮肤覆写
|
* Vditor 自定义皮肤覆写
|
||||||
*************************/
|
*************************/
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.callback-page {
|
.callback-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
|
||||||
padding-top: var(--header-height);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -268,6 +268,8 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.menu {
|
.menu {
|
||||||
|
position: sticky;
|
||||||
|
top: var(--header-height);
|
||||||
width: 200px;
|
width: 200px;
|
||||||
background-color: var(--menu-background-color);
|
background-color: var(--menu-background-color);
|
||||||
height: calc(100vh - 20px - var(--header-height));
|
height: calc(100vh - 20px - var(--header-height));
|
||||||
@@ -277,7 +279,6 @@ export default {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
padding-top: calc(var(--header-height) + 10px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item-container {
|
.menu-item-container {
|
||||||
@@ -418,7 +419,6 @@ export default {
|
|||||||
top: calc(var(--header-height) + 10px);
|
top: calc(var(--header-height) + 10px);
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-enter-active,
|
.slide-enter-active,
|
||||||
|
|||||||
31
frontend/src/utils/loadMore.js
Normal file
31
frontend/src/utils/loadMore.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { ref, onMounted, onUnmounted, onActivated, nextTick } from 'vue'
|
||||||
|
|
||||||
|
export function useScrollLoadMore(loadMore, offset = 50) {
|
||||||
|
const savedScrollTop = ref(0)
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
const scrollTop = window.scrollY || document.documentElement.scrollTop
|
||||||
|
const scrollHeight = document.documentElement.scrollHeight
|
||||||
|
const windowHeight = window.innerHeight
|
||||||
|
savedScrollTop.value = scrollTop
|
||||||
|
if (scrollHeight - (scrollTop + windowHeight) <= offset) {
|
||||||
|
loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('scroll', handleScroll, { passive: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('scroll', handleScroll)
|
||||||
|
})
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
window.scrollTo({ top: savedScrollTop.value })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return { savedScrollTop }
|
||||||
|
}
|
||||||
@@ -75,8 +75,7 @@ export default {
|
|||||||
max-width: var(--page-max-width);
|
max-width: var(--page-max-width);
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +84,6 @@ export default {
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
border-bottom: 1px solid var(--normal-border-color);
|
border-bottom: 1px solid var(--normal-border-color);
|
||||||
|
|||||||
@@ -73,9 +73,8 @@ export default {
|
|||||||
.activity-list-page {
|
.activity-list-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: calc(100vh - var(--header-height) - 40px);
|
height: calc(100% - 40px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding-top: calc(var(--header-height) + 20px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.activity-list-page-card {
|
.activity-list-page-card {
|
||||||
|
|||||||
@@ -211,8 +211,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
@@ -131,8 +131,7 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
}
|
}
|
||||||
.forgot-content {
|
.forgot-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="homePage" class="home-page" @scroll="handleScroll">
|
<div class="home-page">
|
||||||
<div v-if="!isMobile" class="search-container">
|
<div v-if="!isMobile" class="search-container">
|
||||||
<div class="search-title">一切可能,从此刻启航</div>
|
<div class="search-title">一切可能,从此刻启航</div>
|
||||||
<div class="search-subtitle">愿你在此遇见灵感与共鸣。若有疑惑,欢迎发问,亦可在知识的海洋中搜寻答案。</div>
|
<div class="search-subtitle">愿你在此遇见灵感与共鸣。若有疑惑,欢迎发问,亦可在知识的海洋中搜寻答案。</div>
|
||||||
@@ -107,8 +107,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted, watch, onActivated, nextTick } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useScrollLoadMore } from '../utils/loadMore'
|
||||||
import { stripMarkdown } from '../utils/markdown'
|
import { stripMarkdown } from '../utils/markdown'
|
||||||
import { API_BASE_URL } from '../main'
|
import { API_BASE_URL } from '../main'
|
||||||
import { getToken } from '../utils/auth'
|
import { getToken } from '../utils/auth'
|
||||||
@@ -134,8 +135,6 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const homePage = ref(null)
|
|
||||||
const savedScrollTop = ref(0)
|
|
||||||
const selectedCategory = ref('')
|
const selectedCategory = ref('')
|
||||||
if (route.query.category) {
|
if (route.query.category) {
|
||||||
const c = decodeURIComponent(route.query.category)
|
const c = decodeURIComponent(route.query.category)
|
||||||
@@ -167,14 +166,6 @@ export default {
|
|||||||
const pageSize = 10
|
const pageSize = 10
|
||||||
const allLoaded = ref(false)
|
const allLoaded = ref(false)
|
||||||
|
|
||||||
onActivated(() => {
|
|
||||||
nextTick(() => {
|
|
||||||
if (homePage.value) {
|
|
||||||
homePage.value.scrollTop = savedScrollTop.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Backend now returns comment counts directly
|
// Backend now returns comment counts directly
|
||||||
|
|
||||||
const loadOptions = async () => {
|
const loadOptions = async () => {
|
||||||
@@ -366,13 +357,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleScroll = (e) => {
|
useScrollLoadMore(fetchContent)
|
||||||
const el = e.target
|
|
||||||
savedScrollTop.value = el.scrollTop
|
|
||||||
if (el.scrollHeight - el.scrollTop <= el.clientHeight + 50) {
|
|
||||||
fetchContent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
fetchContent()
|
fetchContent()
|
||||||
@@ -389,7 +374,7 @@ export default {
|
|||||||
|
|
||||||
const sanitizeDescription = (text) => stripMarkdown(text)
|
const sanitizeDescription = (text) => stripMarkdown(text)
|
||||||
|
|
||||||
return { topics, selectedTopic, articles, sanitizeDescription, isLoadingPosts, handleScroll, selectedCategory, selectedTags, tagOptions, categoryOptions, isMobile, homePage }
|
return { topics, selectedTopic, articles, sanitizeDescription, isLoadingPosts, selectedCategory, selectedTags, tagOptions, categoryOptions, isMobile }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -397,13 +382,9 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.home-page {
|
.home-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
|
||||||
padding-top: var(--header-height);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
overflow-y: auto;
|
|
||||||
/* enable container queries */
|
|
||||||
container-type: inline-size;
|
container-type: inline-size;
|
||||||
container-name: home-page;
|
container-name: home-page;
|
||||||
}
|
}
|
||||||
@@ -455,7 +436,6 @@ export default {
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -736,6 +716,10 @@ export default {
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topic-container {
|
||||||
|
position: initial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -125,8 +125,7 @@ export default {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -626,12 +626,8 @@ export default {
|
|||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.message-page {
|
.message-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
|
||||||
padding-top: var(--header-height);
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-page-header {
|
.message-page-header {
|
||||||
@@ -639,7 +635,6 @@ export default {
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@@ -261,8 +261,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div v-if="isWaitingFetchingPost" class="loading-container">
|
<div v-if="isWaitingFetchingPost" class="loading-container">
|
||||||
<l-hatch size="28" stroke="4" speed="3.5" color="var(--primary-color)"></l-hatch>
|
<l-hatch size="28" stroke="4" speed="3.5" color="var(--primary-color)"></l-hatch>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="post-page-main-container" ref="mainContainer" @scroll="onScroll">
|
<div v-else class="post-page-main-container" ref="mainContainer">
|
||||||
<div class="article-title-container">
|
<div class="article-title-container">
|
||||||
<div class="article-title-container-left">
|
<div class="article-title-container-left">
|
||||||
<div class="article-title">{{ title }}</div>
|
<div class="article-title">{{ title }}</div>
|
||||||
@@ -156,6 +156,7 @@ export default {
|
|||||||
const defaultTitle = document.title
|
const defaultTitle = document.title
|
||||||
const metaDescriptionEl = document.querySelector('meta[name="description"]')
|
const metaDescriptionEl = document.querySelector('meta[name="description"]')
|
||||||
const defaultDescription = metaDescriptionEl ? metaDescriptionEl.getAttribute('content') : ''
|
const defaultDescription = metaDescriptionEl ? metaDescriptionEl.getAttribute('content') : ''
|
||||||
|
const headerHeight = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0
|
||||||
|
|
||||||
watch(title, t => {
|
watch(title, t => {
|
||||||
document.title = `OpenIsle - ${t}`
|
document.title = `OpenIsle - ${t}`
|
||||||
@@ -170,6 +171,7 @@ export default {
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
document.title = defaultTitle
|
document.title = defaultTitle
|
||||||
if (metaDescriptionEl) metaDescriptionEl.setAttribute('content', defaultDescription)
|
if (metaDescriptionEl) metaDescriptionEl.setAttribute('content', defaultDescription)
|
||||||
|
window.removeEventListener('scroll', updateCurrentIndex)
|
||||||
})
|
})
|
||||||
|
|
||||||
const lightboxVisible = ref(false)
|
const lightboxVisible = ref(false)
|
||||||
@@ -202,12 +204,12 @@ export default {
|
|||||||
const items = []
|
const items = []
|
||||||
if (mainContainer.value) {
|
if (mainContainer.value) {
|
||||||
const main = mainContainer.value.querySelector('.info-content-container')
|
const main = mainContainer.value.querySelector('.info-content-container')
|
||||||
if (main) items.push({ el: main, top: 0 })
|
if (main) items.push({ el: main, top: getTop(main) })
|
||||||
|
|
||||||
for (const c of comments.value) {
|
for (const c of comments.value) {
|
||||||
const el = document.getElementById('comment-' + c.id)
|
const el = document.getElementById('comment-' + c.id)
|
||||||
if (el) {
|
if (el) {
|
||||||
items.push({ el, top: getTopRelativeTo(el, mainContainer.value) })
|
items.push({ el, top: getTop(el) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 根据 top 排序,防止评论异步插入后顺序错乱
|
// 根据 top 排序,防止评论异步插入后顺序错乱
|
||||||
@@ -230,11 +232,8 @@ export default {
|
|||||||
parentUserName: parentUserName
|
parentUserName: parentUserName
|
||||||
})
|
})
|
||||||
|
|
||||||
const getTopRelativeTo = (el, container) => {
|
const getTop = (el) => {
|
||||||
const elRect = el.getBoundingClientRect()
|
return el.getBoundingClientRect().top + window.scrollY
|
||||||
const parentRect = container.getBoundingClientRect()
|
|
||||||
// 加上 scrollTop,得到相对于 container 内部顶部的距离
|
|
||||||
return elRect.top - parentRect.top + container.scrollTop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const findCommentPath = (id, list) => {
|
const findCommentPath = (id, list) => {
|
||||||
@@ -336,19 +335,16 @@ export default {
|
|||||||
async () => {
|
async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
|
updateCurrentIndex()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateCurrentIndex = () => {
|
const updateCurrentIndex = () => {
|
||||||
const container = mainContainer.value
|
const scrollTop = window.scrollY
|
||||||
if (!container) return
|
|
||||||
|
|
||||||
const scrollTop = container.scrollTop
|
|
||||||
|
|
||||||
for (let i = 0; i < postItems.value.length; i++) {
|
for (let i = 0; i < postItems.value.length; i++) {
|
||||||
const el = postItems.value[i]
|
const el = postItems.value[i]
|
||||||
// 计算元素相对 container 顶部的 top
|
const top = getTop(el)
|
||||||
const top = getTopRelativeTo(el, container)
|
|
||||||
const bottom = top + el.offsetHeight
|
const bottom = top + el.offsetHeight
|
||||||
|
|
||||||
if (bottom > scrollTop) {
|
if (bottom > scrollTop) {
|
||||||
@@ -362,9 +358,9 @@ export default {
|
|||||||
const index = Number(e.target.value)
|
const index = Number(e.target.value)
|
||||||
currentIndex.value = index
|
currentIndex.value = index
|
||||||
const target = postItems.value[index - 1]
|
const target = postItems.value[index - 1]
|
||||||
if (target && mainContainer.value) {
|
if (target) {
|
||||||
const top = getTopRelativeTo(target, mainContainer.value)
|
const top = getTop(target) - headerHeight - 20 // 20 for beauty
|
||||||
mainContainer.value.scrollTo({ top, behavior: 'instant' })
|
window.scrollTo({ top, behavior: 'auto' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,12 +566,14 @@ export default {
|
|||||||
const hash = location.hash
|
const hash = location.hash
|
||||||
if (hash.startsWith('#comment-')) {
|
if (hash.startsWith('#comment-')) {
|
||||||
const id = hash.substring('#comment-'.length)
|
const id = hash.substring('#comment-'.length)
|
||||||
await nextTick()
|
// 不清楚啥原因,先wait一下子不然会定不准 😅
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
const el = document.getElementById('comment-' + id)
|
const el = document.getElementById('comment-' + id)
|
||||||
if (el && mainContainer.value) {
|
if (el) {
|
||||||
mainContainer.value.scrollTo({ top: getTopRelativeTo(el, mainContainer.value), behavior: 'instant' })
|
const top = el.getBoundingClientRect().top + window.scrollY - headerHeight - 20 // 20 for beauty
|
||||||
|
window.scrollTo({ top, behavior: 'smooth' })
|
||||||
el.classList.add('comment-highlight')
|
el.classList.add('comment-highlight')
|
||||||
setTimeout(() => el.classList.remove('comment-highlight'), 2000)
|
setTimeout(() => el.classList.remove('comment-highlight'), 4000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,6 +588,7 @@ export default {
|
|||||||
await fetchPost()
|
await fetchPost()
|
||||||
if (id) expandCommentPath(id)
|
if (id) expandCommentPath(id)
|
||||||
updateCurrentIndex()
|
updateCurrentIndex()
|
||||||
|
window.addEventListener('scroll', updateCurrentIndex)
|
||||||
await jumpToHashComment()
|
await jumpToHashComment()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -612,7 +611,6 @@ export default {
|
|||||||
postId,
|
postId,
|
||||||
postComment,
|
postComment,
|
||||||
onSliderInput,
|
onSliderInput,
|
||||||
onScroll: updateCurrentIndex,
|
|
||||||
copyPostLink,
|
copyPostLink,
|
||||||
subscribePost,
|
subscribePost,
|
||||||
unsubscribePost,
|
unsubscribePost,
|
||||||
@@ -650,7 +648,7 @@ export default {
|
|||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-container {
|
.loading-container {
|
||||||
@@ -662,19 +660,18 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.post-page-main-container {
|
.post-page-main-container {
|
||||||
overflow-y: auto;
|
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
height: calc(100% - 40px - var(--header-height));
|
|
||||||
width: calc(85% - 40px);
|
width: calc(85% - 40px);
|
||||||
padding-top: calc(var(--header-height) + 20px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-page-scroller-container {
|
.post-page-scroller-container {
|
||||||
padding-top: var(--header-height);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 15%;
|
width: 15%;
|
||||||
|
position: sticky;
|
||||||
|
top: var(--header-height);
|
||||||
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-config-container {
|
.comment-config-container {
|
||||||
@@ -974,7 +971,6 @@ export default {
|
|||||||
.post-page-main-container {
|
.post-page-main-container {
|
||||||
width: calc(100% - 20px);
|
width: calc(100% - 20px);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-top: calc(var(--header-height) + 10px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-title {
|
.article-title {
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ export default {
|
|||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const tabLoading = ref(false)
|
const tabLoading = ref(false)
|
||||||
const selectedTab = ref('summary')
|
const selectedTab = ref('summary')
|
||||||
const followTab = ref('followers')
|
const followTab = ref('followers')
|
||||||
|
|
||||||
const levelInfo = computed(() => {
|
const levelInfo = computed(() => {
|
||||||
const exp = user.value.experience || 0
|
const exp = user.value.experience || 0
|
||||||
@@ -336,9 +336,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchTimeline = async () => {
|
const fetchTimeline = async () => {
|
||||||
const postsRes = await fetch(`${API_BASE_URL}/api/users/${username}/posts?limit=50`)
|
const [postsRes, repliesRes, tagsRes] = await Promise.all([
|
||||||
const repliesRes = await fetch(`${API_BASE_URL}/api/users/${username}/replies?limit=50`)
|
fetch(`${API_BASE_URL}/api/users/${username}/posts?limit=50`),
|
||||||
const tagsRes = await fetch(`${API_BASE_URL}/api/users/${username}/tags?limit=50`)
|
fetch(`${API_BASE_URL}/api/users/${username}/replies?limit=50`),
|
||||||
|
fetch(`${API_BASE_URL}/api/users/${username}/tags?limit=50`)
|
||||||
|
])
|
||||||
const posts = postsRes.ok ? await postsRes.json() : []
|
const posts = postsRes.ok ? await postsRes.json() : []
|
||||||
const replies = repliesRes.ok ? await repliesRes.json() : []
|
const replies = repliesRes.ok ? await repliesRes.json() : []
|
||||||
const tags = tagsRes.ok ? await tagsRes.json() : []
|
const tags = tagsRes.ok ? await tagsRes.json() : []
|
||||||
@@ -367,8 +369,10 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchFollowUsers = async () => {
|
const fetchFollowUsers = async () => {
|
||||||
const followerRes = await fetch(`${API_BASE_URL}/api/users/${username}/followers`)
|
const [followerRes, followingRes] = await Promise.all([
|
||||||
const followingRes = await fetch(`${API_BASE_URL}/api/users/${username}/following`)
|
fetch(`${API_BASE_URL}/api/users/${username}/followers`),
|
||||||
|
fetch(`${API_BASE_URL}/api/users/${username}/following`)
|
||||||
|
])
|
||||||
followers.value = followerRes.ok ? await followerRes.json() : []
|
followers.value = followerRes.ok ? await followerRes.json() : []
|
||||||
followings.value = followingRes.ok ? await followingRes.json() : []
|
followings.value = followingRes.ok ? await followingRes.json() : []
|
||||||
}
|
}
|
||||||
@@ -493,8 +497,7 @@ export default {
|
|||||||
|
|
||||||
.profile-page {
|
.profile-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
@@ -639,7 +642,6 @@ export default {
|
|||||||
top: 1px;
|
top: 1px;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
background-color: var(--background-color-blur);
|
background-color: var(--background-color-blur);
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
|||||||
@@ -239,8 +239,7 @@ export default {
|
|||||||
.settings-page {
|
.settings-page {
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
height: calc(100vh - var(--header-height) - 80px);
|
height: calc(100% - 80px);
|
||||||
padding-top: calc(var(--header-height) + 40px);
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -222,8 +222,7 @@ export default {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ export default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
height: calc(100vh - var(--header-height));
|
height: 100%;
|
||||||
padding-top: var(--header-height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.reason-title {
|
.reason-title {
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ onMounted(loadData)
|
|||||||
max-width: var(--page-max-width);
|
max-width: var(--page-max-width);
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: calc(100vh - var(--header-height) - 40px);
|
height: 100%;
|
||||||
padding-top: calc(var(--header-height) + 20px);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user