Compare commits

..

6 Commits

Author SHA1 Message Date
Tim
232f40151b fix: 修改css冲突 2025-10-17 17:42:12 +08:00
Tim
3b3f99754d fix: 搜索focus 2025-10-17 17:33:34 +08:00
Tim
e14566ee66 fix: 搜索提示 2025-10-17 17:01:55 +08:00
Tim
892312c6d4 fix: 搜索框 2025-10-17 16:59:40 +08:00
Tim
dfb31771ff feat: searchbar集成到header 2025-10-17 16:54:03 +08:00
Tim
bf7df629cc Merge pull request #1073 from nagisa77/feature/ui_fix
fix: avatar 以及 auth 重构
2025-10-17 15:11:55 +08:00
5 changed files with 81 additions and 18 deletions

View File

@@ -168,9 +168,19 @@ export default {
const mobileMenuRef = ref(null) const mobileMenuRef = ref(null)
const isMobile = useIsMobile() const isMobile = useIsMobile()
const openMenu = () => {
if (!open.value) {
open.value = true
}
}
const toggle = () => { const toggle = () => {
open.value = !open.value if (open.value) {
if (!open.value) emit('close') open.value = false
emit('close')
} else {
open.value = true
}
} }
const close = () => { const close = () => {
@@ -275,7 +285,7 @@ export default {
return /^https?:\/\//.test(icon) || icon.startsWith('/') return /^https?:\/\//.test(icon) || icon.startsWith('/')
} }
expose({ toggle, close, reload, scrollToBottom }) expose({ toggle, close, reload, scrollToBottom, openMenu })
return { return {
open, open,
@@ -308,7 +318,6 @@ export default {
border: 1px solid var(--normal-border-color); border: 1px solid var(--normal-border-color);
border-radius: 5px; border-radius: 5px;
padding: 5px 10px; padding: 5px 10px;
margin-bottom: 4px;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -331,6 +340,7 @@ export default {
z-index: 10000; z-index: 10000;
max-height: 300px; max-height: 300px;
min-width: 350px; min-width: 350px;
margin-top: 4px;
overflow-y: auto; overflow-y: auto;
} }

View File

@@ -26,6 +26,11 @@
<ClientOnly> <ClientOnly>
<div class="header-content-right"> <div class="header-content-right">
<SearchDropdown
ref="searchDropdown"
v-if="!isMobile || showSearch"
@close="closeSearch"
/>
<!-- 搜索 --> <!-- 搜索 -->
<ToolTip v-if="isMobile" content="搜索" placement="bottom"> <ToolTip v-if="isMobile" content="搜索" placement="bottom">
<div class="header-icon-item" @click="search"> <div class="header-icon-item" @click="search">
@@ -106,7 +111,6 @@
</div> </div>
</div> </div>
</ClientOnly> </ClientOnly>
<SearchDropdown ref="searchDropdown" v-if="isMobile && showSearch" @close="closeSearch" />
</div> </div>
</header> </header>
</template> </template>

View File

@@ -17,7 +17,8 @@
<input <input
class="text-input" class="text-input"
v-model="keyword" v-model="keyword"
placeholder="Search" placeholder="键盘点击「/」以触发搜索"
ref="searchInput"
@input="setSearch(keyword)" @input="setSearch(keyword)"
/> />
</div> </div>
@@ -48,7 +49,7 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from 'vue' import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import Dropdown from '~/components/Dropdown.vue' import Dropdown from '~/components/Dropdown.vue'
import { stripMarkdown } from '~/utils/markdown' import { stripMarkdown } from '~/utils/markdown'
import { useIsMobile } from '~/utils/screen' import { useIsMobile } from '~/utils/screen'
@@ -61,8 +62,48 @@ const keyword = ref('')
const selected = ref(null) const selected = ref(null)
const results = ref([]) const results = ref([])
const dropdown = ref(null) const dropdown = ref(null)
const searchInput = ref(null)
const isMobile = useIsMobile() const isMobile = useIsMobile()
const isEditableElement = (el) => {
if (!el) return false
if (el.isContentEditable) return true
const tagName = el.tagName ? el.tagName.toLowerCase() : ''
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
return true
}
const role = el.getAttribute ? el.getAttribute('role') : null
return role === 'textbox'
}
const focusSearchInput = () => {
if (!searchInput.value) return
dropdown.value?.openMenu?.()
if (typeof searchInput.value.focus === 'function') {
try {
searchInput.value.focus({ preventScroll: true })
} catch (e) {
searchInput.value.focus()
}
}
}
const handleGlobalSlash = (event) => {
if (event.defaultPrevented) return
if (event.key !== '/' || event.ctrlKey || event.metaKey || event.altKey) return
if (isEditableElement(document.activeElement)) return
event.preventDefault()
focusSearchInput()
}
onMounted(() => {
window.addEventListener('keydown', handleGlobalSlash)
})
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleGlobalSlash)
})
const toggle = () => { const toggle = () => {
dropdown.value.toggle() dropdown.value.toggle()
} }
@@ -144,8 +185,7 @@ defineExpose({
<style scoped> <style scoped>
.search-dropdown { .search-dropdown {
margin-top: 20px; width: 300px;
width: 500px;
} }
.search-mobile-trigger { .search-mobile-trigger {
@@ -154,7 +194,7 @@ defineExpose({
} }
.search-input { .search-input {
padding: 10px; padding: 2px 10px;
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: 100%;
@@ -202,7 +242,7 @@ defineExpose({
} }
.result-body { .result-body {
line-height: 1; line-height: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
@@ -216,4 +256,14 @@ defineExpose({
font-size: 12px; font-size: 12px;
color: #666; color: #666;
} }
.search-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 10000;
}
</style> </style>

View File

@@ -1,9 +1,9 @@
<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>
<SearchDropdown /> <SearchDropdown />
</div> </div> -->
<div class="topic-container"> <div class="topic-container">
<div class="topic-item-container"> <div class="topic-item-container">
@@ -379,7 +379,6 @@ onBeforeUnmount(() => {
/** 供 InfiniteLoadMore 重建用的 key筛选/Tab 改变即重建内部状态 */ /** 供 InfiniteLoadMore 重建用的 key筛选/Tab 改变即重建内部状态 */
const ioKey = computed(() => asyncKey.value.join('::')) const ioKey = computed(() => asyncKey.value.join('::'))
// 页面选项同步到全局状态 // 页面选项同步到全局状态
watch([selectedCategory, selectedTags], ([newCategory, newTags]) => { watch([selectedCategory, selectedTags], ([newCategory, newTags]) => {
selectedCategoryGlobal.value = newCategory selectedCategoryGlobal.value = newCategory
@@ -544,14 +543,14 @@ watch([selectedCategory, selectedTags], ([newCategory, newTags]) => {
.header-item.views { .header-item.views {
width: 5%; width: 5%;
justify-content: flex-end; justify-content: flex-end;
text-align: right; text-align: right;
} }
.article-time, .article-time,
.header-item.activity { .header-item.activity {
width: 10%; width: 10%;
justify-content: flex-end; justify-content: flex-end;
text-align: left; text-align: left;
} }
.article-item-title { .article-item-title {

View File

@@ -92,7 +92,7 @@
></div> ></div>
<div class="article-footer-container"> <div class="article-footer-container">
<div class="option-container"> <div class="article-option-container">
<ReactionsGroup <ReactionsGroup
ref="postReactionsGroupRef" ref="postReactionsGroupRef"
v-model="postReactions" v-model="postReactions"
@@ -1286,7 +1286,7 @@ onMounted(async () => {
margin-bottom: 10px; margin-bottom: 10px;
} }
.option-container { .article-option-container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;