mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-05-11 21:27:31 +08:00
Merge pull request #572 from CH-122/refactor/ui
refactor: 在 header 组件中添加发帖功能,移动端添加发帖悬浮按钮,优化首页搜索标题样式 ,
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<HeaderComponent
|
<HeaderComponent
|
||||||
ref="header"
|
ref="header"
|
||||||
@@ -9,12 +10,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
|
|
||||||
<div class="menu-container" v-click-outside="handleMenuOutside">
|
<div class="menu-container" v-click-outside="handleMenuOutside">
|
||||||
<MenuComponent :visible="!hideMenu && menuVisible" @item-click="menuVisible = false" />
|
<MenuComponent :visible="!hideMenu && menuVisible" @item-click="menuVisible = false" />
|
||||||
</div>
|
</div>
|
||||||
<div class="content" :class="{ 'menu-open': menuVisible && !hideMenu }">
|
<div class="content" :class="{ 'menu-open': menuVisible && !hideMenu }">
|
||||||
<NuxtPage keepalive />
|
<NuxtPage keepalive />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if='!menuVisible && route.path !== "/new-post"' class="new-post-icon" @click="goToNewPost">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<GlobalPopups />
|
<GlobalPopups />
|
||||||
</div>
|
</div>
|
||||||
@@ -23,8 +29,8 @@
|
|||||||
<script>
|
<script>
|
||||||
import HeaderComponent from '~/components/HeaderComponent.vue'
|
import HeaderComponent from '~/components/HeaderComponent.vue'
|
||||||
import MenuComponent from '~/components/MenuComponent.vue'
|
import MenuComponent from '~/components/MenuComponent.vue'
|
||||||
|
|
||||||
import GlobalPopups from '~/components/GlobalPopups.vue'
|
import GlobalPopups from '~/components/GlobalPopups.vue'
|
||||||
|
import { useIsMobile } from '~/utils/screen'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -32,6 +38,8 @@ export default {
|
|||||||
setup() {
|
setup() {
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
const menuVisible = ref(!isMobile.value)
|
const menuVisible = ref(!isMobile.value)
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const hideMenu = computed(() => {
|
const hideMenu = computed(() => {
|
||||||
return [
|
return [
|
||||||
'/login',
|
'/login',
|
||||||
@@ -65,12 +73,16 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { menuVisible, hideMenu, handleMenuOutside, header }
|
const goToNewPost = () => {
|
||||||
|
navigateTo('/new-post', { replace: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
return { menuVisible, hideMenu, handleMenuOutside, header, route, goToNewPost }
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style src="~/assets/global.css"></style>
|
<style src="~/assets/global.css"></style>
|
||||||
<style>
|
<style scoped>
|
||||||
.header-container {
|
.header-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -103,6 +115,22 @@ export default {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.new-post-icon {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 40px;
|
||||||
|
right: 20px;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.content,
|
.content,
|
||||||
.content.menu-open {
|
.content.menu-open {
|
||||||
|
|||||||
@@ -24,6 +24,17 @@
|
|||||||
<div v-if="isMobile" class="search-icon" @click="search">
|
<div v-if="isMobile" class="search-icon" @click="search">
|
||||||
<i class="fas fa-search"></i>
|
<i class="fas fa-search"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ToolTip
|
||||||
|
v-if="!isMobile"
|
||||||
|
content="发帖"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<div class="new-post-icon" @click="goToNewPost">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</div>
|
||||||
|
</ToolTip>
|
||||||
|
|
||||||
<DropdownMenu ref="userMenu" :items="headerMenuItems">
|
<DropdownMenu ref="userMenu" :items="headerMenuItems">
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
@@ -52,6 +63,7 @@
|
|||||||
import { ClientOnly } from '#components'
|
import { ClientOnly } from '#components'
|
||||||
import { computed, nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import DropdownMenu from '~/components/DropdownMenu.vue'
|
import DropdownMenu from '~/components/DropdownMenu.vue'
|
||||||
|
import ToolTip from '~/components/ToolTip.vue'
|
||||||
import SearchDropdown from '~/components/SearchDropdown.vue'
|
import SearchDropdown from '~/components/SearchDropdown.vue'
|
||||||
import { authState, clearToken, loadCurrentUser } from '~/utils/auth'
|
import { authState, clearToken, loadCurrentUser } from '~/utils/auth'
|
||||||
import { fetchUnreadCount, notificationState } from '~/utils/notification'
|
import { fetchUnreadCount, notificationState } from '~/utils/notification'
|
||||||
@@ -113,6 +125,10 @@ const goToLogout = () => {
|
|||||||
navigateTo('/login', { replace: true })
|
navigateTo('/login', { replace: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const goToNewPost = () => {
|
||||||
|
navigateTo('/new-post', { replace: false })
|
||||||
|
}
|
||||||
|
|
||||||
const headerMenuItems = computed(() => [
|
const headerMenuItems = computed(() => [
|
||||||
{ text: '设置', onClick: goToSettings },
|
{ text: '设置', onClick: goToSettings },
|
||||||
{ text: '个人主页', onClick: goToProfile },
|
{ text: '个人主页', onClick: goToProfile },
|
||||||
@@ -275,6 +291,11 @@ onMounted(async () => {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.new-post-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.header-content {
|
.header-content {
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
<i class="menu-item-icon fas fa-hashtag"></i>
|
<i class="menu-item-icon fas fa-hashtag"></i>
|
||||||
<span class="menu-item-text">话题</span>
|
<span class="menu-item-text">话题</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
<NuxtLink
|
||||||
|
class="menu-item"
|
||||||
|
exact-active-class="selected"
|
||||||
|
to="/new-post"
|
||||||
|
@click="handleItemClick"
|
||||||
|
>
|
||||||
|
<i class="menu-item-icon fas fa-edit"></i>
|
||||||
|
<span class="menu-item-text">发帖</span>
|
||||||
|
</NuxtLink>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
exact-active-class="selected"
|
exact-active-class="selected"
|
||||||
@@ -47,15 +56,6 @@
|
|||||||
<i class="menu-item-icon fas fa-chart-line"></i>
|
<i class="menu-item-icon fas fa-chart-line"></i>
|
||||||
<span class="menu-item-text">站点统计</span>
|
<span class="menu-item-text">站点统计</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink
|
|
||||||
class="menu-item"
|
|
||||||
exact-active-class="selected"
|
|
||||||
to="/new-post"
|
|
||||||
@click="handleItemClick"
|
|
||||||
>
|
|
||||||
<i class="menu-item-icon fas fa-edit"></i>
|
|
||||||
<span class="menu-item-text">发帖</span>
|
|
||||||
</NuxtLink>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-section">
|
<div class="menu-section">
|
||||||
|
|||||||
488
frontend_nuxt/components/ToolTip.vue
Normal file
488
frontend_nuxt/components/ToolTip.vue
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tooltip-wrapper" ref="wrapperRef">
|
||||||
|
<!-- 触发器 -->
|
||||||
|
<div
|
||||||
|
class="tooltip-trigger"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
@click="handleClick"
|
||||||
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
|
:tabindex="focusable ? 0 : -1"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 提示内容 -->
|
||||||
|
<Transition name="tooltip-fade">
|
||||||
|
<div
|
||||||
|
v-if="visible"
|
||||||
|
ref="tooltipRef"
|
||||||
|
class="tooltip-content"
|
||||||
|
:class="[
|
||||||
|
`tooltip-${placement}`,
|
||||||
|
{ 'tooltip-dark': dark },
|
||||||
|
{ 'tooltip-light': !dark }
|
||||||
|
]"
|
||||||
|
:style="tooltipStyle"
|
||||||
|
role="tooltip"
|
||||||
|
:aria-describedby="ariaId"
|
||||||
|
>
|
||||||
|
<div class="tooltip-inner">
|
||||||
|
<slot name="content">
|
||||||
|
{{ content }}
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" :class="`tooltip-arrow-${placement}`"></div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, computed, onMounted, onBeforeUnmount, nextTick, useId, watch } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ToolTip',
|
||||||
|
props: {
|
||||||
|
// 提示内容
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 触发方式:hover、click、focus
|
||||||
|
trigger: {
|
||||||
|
type: String,
|
||||||
|
default: 'hover',
|
||||||
|
validator: (value) => ['hover', 'click', 'focus', 'manual'].includes(value)
|
||||||
|
},
|
||||||
|
// 位置:top、bottom、left、right
|
||||||
|
placement: {
|
||||||
|
type: String,
|
||||||
|
default: 'top',
|
||||||
|
validator: (value) => ['top', 'bottom', 'left', 'right'].includes(value)
|
||||||
|
},
|
||||||
|
// 是否启用暗色主题
|
||||||
|
dark: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 延迟显示时间(毫秒)
|
||||||
|
delay: {
|
||||||
|
type: Number,
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
// 是否禁用
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 是否可通过Tab键聚焦
|
||||||
|
focusable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 偏移距离
|
||||||
|
offset: {
|
||||||
|
type: Number,
|
||||||
|
default: 8
|
||||||
|
},
|
||||||
|
// 最大宽度
|
||||||
|
maxWidth: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '200px'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['show', 'hide'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const wrapperRef = ref(null)
|
||||||
|
const tooltipRef = ref(null)
|
||||||
|
const visible = ref(false)
|
||||||
|
const ariaId = ref(`tooltip-${useId()}`)
|
||||||
|
|
||||||
|
let showTimer = null
|
||||||
|
let hideTimer = null
|
||||||
|
|
||||||
|
// 计算tooltip样式
|
||||||
|
const tooltipStyle = computed(() => {
|
||||||
|
const maxWidth = typeof props.maxWidth === 'number'
|
||||||
|
? `${props.maxWidth}px`
|
||||||
|
: props.maxWidth
|
||||||
|
|
||||||
|
return {
|
||||||
|
maxWidth,
|
||||||
|
zIndex: 2000
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 显示tooltip
|
||||||
|
const show = () => {
|
||||||
|
if (props.disabled) return
|
||||||
|
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
showTimer = setTimeout(() => {
|
||||||
|
visible.value = true
|
||||||
|
emit('show')
|
||||||
|
nextTick(() => {
|
||||||
|
updatePosition()
|
||||||
|
})
|
||||||
|
}, props.delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏tooltip
|
||||||
|
const hide = () => {
|
||||||
|
clearTimeout(showTimer)
|
||||||
|
hideTimer = setTimeout(() => {
|
||||||
|
visible.value = false
|
||||||
|
emit('hide')
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即显示(用于manual模式)
|
||||||
|
const showImmediately = () => {
|
||||||
|
if (props.disabled) return
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
clearTimeout(showTimer)
|
||||||
|
visible.value = true
|
||||||
|
emit('show')
|
||||||
|
nextTick(() => {
|
||||||
|
updatePosition()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即隐藏(用于manual模式)
|
||||||
|
const hideImmediately = () => {
|
||||||
|
clearTimeout(showTimer)
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
visible.value = false
|
||||||
|
emit('hide')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新位置
|
||||||
|
const updatePosition = () => {
|
||||||
|
if (!wrapperRef.value || !tooltipRef.value) return
|
||||||
|
|
||||||
|
const trigger = wrapperRef.value.querySelector('.tooltip-trigger')
|
||||||
|
const tooltip = tooltipRef.value
|
||||||
|
|
||||||
|
if (!trigger) return
|
||||||
|
|
||||||
|
const triggerRect = trigger.getBoundingClientRect()
|
||||||
|
const tooltipRect = tooltip.getBoundingClientRect()
|
||||||
|
|
||||||
|
let top = 0
|
||||||
|
let left = 0
|
||||||
|
|
||||||
|
switch (props.placement) {
|
||||||
|
case 'top':
|
||||||
|
top = triggerRect.top - tooltipRect.height - props.offset
|
||||||
|
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2
|
||||||
|
break
|
||||||
|
case 'bottom':
|
||||||
|
top = triggerRect.bottom + props.offset
|
||||||
|
left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2
|
||||||
|
left = triggerRect.left - tooltipRect.width - props.offset
|
||||||
|
break
|
||||||
|
case 'right':
|
||||||
|
top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2
|
||||||
|
left = triggerRect.right + props.offset
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 边界检测
|
||||||
|
const padding = 8
|
||||||
|
const viewportWidth = window.innerWidth
|
||||||
|
const viewportHeight = window.innerHeight
|
||||||
|
|
||||||
|
if (left < padding) {
|
||||||
|
left = padding
|
||||||
|
} else if (left + tooltipRect.width > viewportWidth - padding) {
|
||||||
|
left = viewportWidth - tooltipRect.width - padding
|
||||||
|
}
|
||||||
|
|
||||||
|
if (top < padding) {
|
||||||
|
top = padding
|
||||||
|
} else if (top + tooltipRect.height > viewportHeight - padding) {
|
||||||
|
top = viewportHeight - tooltipRect.height - padding
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip.style.position = 'fixed'
|
||||||
|
tooltip.style.top = `${top}px`
|
||||||
|
tooltip.style.left = `${left}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件处理
|
||||||
|
const handleMouseEnter = () => {
|
||||||
|
if (props.trigger === 'hover') {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
if (props.trigger === 'hover') {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (props.trigger === 'click') {
|
||||||
|
if (visible.value) {
|
||||||
|
hide()
|
||||||
|
} else {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (props.trigger === 'focus') {
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
if (props.trigger === 'focus') {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击外部隐藏
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (props.trigger === 'click' && wrapperRef.value && !wrapperRef.value.contains(event.target)) {
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 窗口大小改变时重新计算位置
|
||||||
|
const handleResize = () => {
|
||||||
|
if (visible.value) {
|
||||||
|
updatePosition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听禁用状态变化
|
||||||
|
watch(() => props.disabled, (newVal) => {
|
||||||
|
if (newVal && visible.value) {
|
||||||
|
hideImmediately()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('click', handleClickOutside)
|
||||||
|
window.addEventListener('resize', handleResize)
|
||||||
|
window.addEventListener('scroll', handleResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearTimeout(showTimer)
|
||||||
|
clearTimeout(hideTimer)
|
||||||
|
document.removeEventListener('click', handleClickOutside)
|
||||||
|
window.removeEventListener('resize', handleResize)
|
||||||
|
window.removeEventListener('scroll', handleResize)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
wrapperRef,
|
||||||
|
tooltipRef,
|
||||||
|
visible,
|
||||||
|
ariaId,
|
||||||
|
tooltipStyle,
|
||||||
|
handleMouseEnter,
|
||||||
|
handleMouseLeave,
|
||||||
|
handleClick,
|
||||||
|
handleFocus,
|
||||||
|
handleBlur,
|
||||||
|
// 暴露给父组件的方法
|
||||||
|
show: showImmediately,
|
||||||
|
hide: hideImmediately
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tooltip-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-trigger {
|
||||||
|
display: inline-block;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-content {
|
||||||
|
position: fixed;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-inner {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-wrap: break-word;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 亮色主题 */
|
||||||
|
.tooltip-light .tooltip-inner {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid var(--normal-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色主题 */
|
||||||
|
.tooltip-dark .tooltip-inner {
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 箭头基础样式 */
|
||||||
|
.tooltip-arrow {
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部箭头 */
|
||||||
|
.tooltip-top .tooltip-arrow-top {
|
||||||
|
bottom: -6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border-width: 6px 6px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-top .tooltip-arrow-top {
|
||||||
|
border-color: var(--normal-border-color) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-top .tooltip-arrow-top::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -7px;
|
||||||
|
left: -6px;
|
||||||
|
border-width: 6px 6px 0 6px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(--background-color) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-dark.tooltip-top .tooltip-arrow-top {
|
||||||
|
border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部箭头 */
|
||||||
|
.tooltip-bottom .tooltip-arrow-bottom {
|
||||||
|
top: -6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border-width: 0 6px 6px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-bottom .tooltip-arrow-bottom {
|
||||||
|
border-color: transparent transparent var(--normal-border-color) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-bottom .tooltip-arrow-bottom::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: -6px;
|
||||||
|
border-width: 0 6px 6px 6px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent transparent var(--background-color) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-dark.tooltip-bottom .tooltip-arrow-bottom {
|
||||||
|
border-color: transparent transparent rgba(0, 0, 0, 0.9) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧箭头 */
|
||||||
|
.tooltip-left .tooltip-arrow-left {
|
||||||
|
right: -6px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-width: 6px 0 6px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-left .tooltip-arrow-left {
|
||||||
|
border-color: transparent transparent transparent var(--normal-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-left .tooltip-arrow-left::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: -7px;
|
||||||
|
border-width: 6px 0 6px 6px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent transparent transparent var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-dark.tooltip-left .tooltip-arrow-left {
|
||||||
|
border-color: transparent transparent transparent rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧箭头 */
|
||||||
|
.tooltip-right .tooltip-arrow-right {
|
||||||
|
left: -6px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
border-width: 6px 6px 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-right .tooltip-arrow-right {
|
||||||
|
border-color: transparent var(--normal-border-color) transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-light.tooltip-right .tooltip-arrow-right::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -6px;
|
||||||
|
left: 1px;
|
||||||
|
border-width: 6px 6px 6px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent var(--background-color) transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-dark.tooltip-right .tooltip-arrow-right {
|
||||||
|
border-color: transparent rgba(0, 0, 0, 0.9) transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 过渡动画 */
|
||||||
|
.tooltip-fade-enter-active,
|
||||||
|
.tooltip-fade-leave-active {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-fade-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式调整 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.tooltip-inner {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 键盘导航样式 */
|
||||||
|
.tooltip-trigger:focus-visible {
|
||||||
|
outline: 2px solid var(--primary-color);
|
||||||
|
outline-offset: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-page">
|
<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>
|
|
||||||
<SearchDropdown />
|
<SearchDropdown />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -371,8 +368,8 @@ const sanitizeDescription = (text) => stripMarkdown(text)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-container {
|
.search-container {
|
||||||
margin-top: 100px;
|
margin-top: 32px;
|
||||||
padding: 20px;
|
padding: 20px 20px 32px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -384,9 +381,6 @@ const sanitizeDescription = (text) => stripMarkdown(text)
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-subtitle {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container {
|
.loading-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user