mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-03-01 17:41:03 +08:00
Compare commits
6 Commits
feature/ui
...
feature/se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
232f40151b | ||
|
|
3b3f99754d | ||
|
|
e14566ee66 | ||
|
|
892312c6d4 | ||
|
|
dfb31771ff | ||
|
|
bf7df629cc |
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user