删除不再使用的文件和配置,优化项目结构以提升可维护性;新增环境变量配置示例,更新 Docker 和部署相关文件以支持灵活的端口设置;重构数据库连接逻辑,增强错误处理和配置管理,确保更好的兼容性和稳定性。
This commit is contained in:
@@ -1,109 +1,69 @@
|
||||
/**
|
||||
* Soul创业派对 - 章节搜索页
|
||||
* 搜索章节标题和内容
|
||||
*/
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
keyword: '',
|
||||
navBarHeight: 88,
|
||||
query: '',
|
||||
results: [],
|
||||
loading: false,
|
||||
searched: false,
|
||||
total: 0,
|
||||
// 热门搜索关键词
|
||||
hotKeywords: ['私域', '电商', '流量', '赚钱', '创业', 'Soul', '抖音', '变现'],
|
||||
// 热门章节推荐
|
||||
hotChapters: [
|
||||
{ id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', part: '真实的人' },
|
||||
{ id: '9.12', title: '美业整合:一个人的公司如何月入十万', tag: '热门', part: '真实的赚钱' },
|
||||
{ id: '3.1', title: '3000万流水如何跑出来', tag: '热门', part: '真实的行业' },
|
||||
{ id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', part: '真实的赚钱' },
|
||||
{ id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', tag: '最新', part: '真实的赚钱' }
|
||||
]
|
||||
keywords: [],
|
||||
isLoading: false,
|
||||
hotKeywords: ['私域', '流量', '赚钱', '电商', 'AI', '社群']
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.setData({
|
||||
statusBarHeight: app.globalData.statusBarHeight || 44
|
||||
})
|
||||
// 加载热门章节
|
||||
this.loadHotChapters()
|
||||
},
|
||||
|
||||
// 加载热门章节(从服务器获取点击量高的章节)
|
||||
async loadHotChapters() {
|
||||
try {
|
||||
const res = await app.request('/api/book/hot')
|
||||
if (res && res.success && res.chapters?.length > 0) {
|
||||
this.setData({ hotChapters: res.chapters })
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('加载热门章节失败,使用默认数据')
|
||||
}
|
||||
const statusBarHeight = app.globalData.statusBarHeight || 44
|
||||
const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
|
||||
this.setData({ statusBarHeight, navBarHeight })
|
||||
},
|
||||
|
||||
// 输入关键词
|
||||
onInput(e) {
|
||||
this.setData({ keyword: e.detail.value })
|
||||
},
|
||||
|
||||
// 清空搜索
|
||||
clearSearch() {
|
||||
this.setData({
|
||||
keyword: '',
|
||||
results: [],
|
||||
searched: false,
|
||||
total: 0
|
||||
})
|
||||
},
|
||||
|
||||
// 点击热门关键词
|
||||
onHotKeyword(e) {
|
||||
const keyword = e.currentTarget.dataset.keyword
|
||||
this.setData({ keyword })
|
||||
this.doSearch()
|
||||
},
|
||||
|
||||
// 执行搜索
|
||||
async doSearch() {
|
||||
const { keyword } = this.data
|
||||
if (!keyword || keyword.trim().length < 1) {
|
||||
wx.showToast({ title: '请输入搜索关键词', icon: 'none' })
|
||||
const query = (e.detail && e.detail.value) || ''
|
||||
this.setData({ query })
|
||||
if (!query.trim()) {
|
||||
this.setData({ results: [], keywords: [] })
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ loading: true, searched: true })
|
||||
|
||||
try {
|
||||
const res = await app.request(`/api/book/search?q=${encodeURIComponent(keyword.trim())}`)
|
||||
|
||||
if (res && res.success) {
|
||||
this.setData({
|
||||
results: res.results || [],
|
||||
total: res.total || 0
|
||||
})
|
||||
} else {
|
||||
this.setData({ results: [], total: 0 })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('搜索失败:', e)
|
||||
wx.showToast({ title: '搜索失败', icon: 'none' })
|
||||
this.setData({ results: [], total: 0 })
|
||||
} finally {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
this.debounceSearch(query)
|
||||
},
|
||||
|
||||
_searchTimer: null,
|
||||
debounceSearch(query) {
|
||||
if (this._searchTimer) clearTimeout(this._searchTimer)
|
||||
this._searchTimer = setTimeout(() => this.doSearch(query), 300)
|
||||
},
|
||||
|
||||
doSearch(q) {
|
||||
if (!q || !q.trim()) return
|
||||
this.setData({ isLoading: true })
|
||||
app.request('/api/search?q=' + encodeURIComponent(q.trim()) + '&type=all')
|
||||
.then((res) => {
|
||||
const results = (res && res.data && res.data.results) ? res.data.results : []
|
||||
const keywords = (res && res.data && res.data.keywords) ? res.data.keywords : []
|
||||
this.setData({ results, keywords, isLoading: false })
|
||||
})
|
||||
.catch(() => {
|
||||
this.setData({ results: [], keywords: [], isLoading: false })
|
||||
})
|
||||
},
|
||||
|
||||
onKeywordTap(e) {
|
||||
const keyword = e.currentTarget.dataset.keyword
|
||||
this.setData({ query: keyword })
|
||||
this.doSearch(keyword)
|
||||
},
|
||||
|
||||
clearQuery() {
|
||||
this.setData({ query: '', results: [], keywords: [] })
|
||||
},
|
||||
|
||||
// 跳转阅读
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
wx.navigateTo({ url: `/pages/read/read?id=${id}` })
|
||||
if (!id) return
|
||||
wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
|
||||
},
|
||||
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
wx.navigateBack()
|
||||
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "搜索"
|
||||
"navigationBarTitleText": "搜索",
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,67 @@
|
||||
<!--pages/search/search.wxml-->
|
||||
<!--章节搜索页-->
|
||||
<view class="page">
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-content">
|
||||
<view class="back-btn" bindtap="goBack">
|
||||
<text class="back-icon">←</text>
|
||||
</view>
|
||||
<view class="search-input-wrap">
|
||||
<view class="search-icon-small">🔍</view>
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索章节标题或内容..."
|
||||
value="{{keyword}}"
|
||||
bindinput="onInput"
|
||||
bindconfirm="doSearch"
|
||||
confirm-type="search"
|
||||
focus="{{true}}"
|
||||
/>
|
||||
<view class="clear-btn" wx:if="{{keyword}}" bindtap="clearSearch">×</view>
|
||||
</view>
|
||||
<view class="search-btn" bindtap="doSearch">搜索</view>
|
||||
<view class="nav-bar" style="height: {{navBarHeight || (statusBarHeight + 44)}}px; padding-top: {{statusBarHeight || 44}}px; box-sizing: border-box;">
|
||||
<view class="nav-inner safe-header-right">
|
||||
<view class="nav-back" bindtap="goBack">← 返回</view>
|
||||
<text class="nav-title">搜索</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="search-box">
|
||||
<view class="search-input-wrap">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input class="search-input" placeholder="搜索章节标题或内容..." value="{{query}}" bindinput="onInput" focus="{{true}}" />
|
||||
<view class="search-clear" wx:if="{{query}}" bindtap="clearQuery">×</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<view class="main-content" style="padding-top: {{statusBarHeight + 56}}px;">
|
||||
|
||||
<!-- 热门搜索(未搜索时显示) -->
|
||||
<view class="hot-section" wx:if="{{!searched}}">
|
||||
<text class="section-title">热门搜索</text>
|
||||
<view class="hot-tags">
|
||||
<view
|
||||
class="hot-tag"
|
||||
wx:for="{{hotKeywords}}"
|
||||
wx:key="*this"
|
||||
bindtap="onHotKeyword"
|
||||
data-keyword="{{item}}"
|
||||
>{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门章节推荐(未搜索时显示) -->
|
||||
<view class="hot-chapters" wx:if="{{!searched && hotChapters.length > 0}}">
|
||||
<text class="section-title">热门章节</text>
|
||||
<view class="chapter-list">
|
||||
<view
|
||||
class="chapter-item"
|
||||
wx:for="{{hotChapters}}"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<view class="chapter-rank">{{index + 1}}</view>
|
||||
<view class="chapter-info">
|
||||
<text class="chapter-title">{{item.title}}</text>
|
||||
<text class="chapter-part">{{item.part}}</text>
|
||||
</view>
|
||||
<view class="chapter-tag {{item.tag === '免费' ? 'tag-free' : item.tag === '热门' ? 'tag-hot' : 'tag-new'}}">{{item.tag}}</view>
|
||||
<scroll-view class="result-area" scroll-y>
|
||||
<block wx:if="{{!query}}">
|
||||
<view class="hot-section">
|
||||
<text class="hot-label">热门搜索</text>
|
||||
<view class="hot-tags">
|
||||
<view class="hot-tag" wx:for="{{hotKeywords}}" wx:key="*this" data-keyword="{{item}}" bindtap="onKeywordTap">{{item}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<view class="results-section" wx:if="{{searched}}">
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-wrap" wx:if="{{loading}}">
|
||||
<block wx:elif="{{isLoading}}">
|
||||
<view class="loading-wrap">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">搜索中...</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 结果列表 -->
|
||||
<block wx:elif="{{results.length > 0}}">
|
||||
<view class="results-header">
|
||||
<text class="results-count">找到 {{total}} 个结果</text>
|
||||
</view>
|
||||
|
||||
<view class="results-list">
|
||||
<view
|
||||
class="result-item"
|
||||
wx:for="{{results}}"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<view class="result-header">
|
||||
<text class="result-chapter">{{item.chapterLabel}}</text>
|
||||
<view class="result-tags">
|
||||
<text class="tag tag-match" wx:if="{{item.matchType === 'title'}}">标题匹配</text>
|
||||
<text class="tag tag-match" wx:elif="{{item.matchType === 'content'}}">内容匹配</text>
|
||||
<text class="tag tag-free" wx:if="{{item.isFree}}">免费</text>
|
||||
</view>
|
||||
<block wx:elif="{{results.length > 0}}">
|
||||
<view class="result-header">找到 {{results.length}} 个结果</view>
|
||||
<view class="result-list">
|
||||
<view class="result-item" wx:for="{{results}}" wx:key="id" data-id="{{item.id}}" bindtap="goToRead">
|
||||
<view class="result-icon">📄</view>
|
||||
<view class="result-body">
|
||||
<view class="result-meta">
|
||||
<text class="result-id">{{item.id}}</text>
|
||||
<text class="tag-free" wx:if="{{item.isFree}}">免费</text>
|
||||
<text class="tag-content" wx:if="{{item.matchType === 'content'}}">内容匹配</text>
|
||||
</view>
|
||||
<text class="result-title">{{item.title}}</text>
|
||||
<text class="result-part">{{item.part}}</text>
|
||||
<view class="result-content" wx:if="{{item.matchedContent}}">
|
||||
<text class="content-preview">{{item.matchedContent}}</text>
|
||||
</view>
|
||||
<view class="result-arrow">→</view>
|
||||
<text class="result-snippet" wx:if="{{item.snippet}}">{{item.snippet}}</text>
|
||||
<text class="result-path" wx:if="{{item.partTitle}}">{{item.partTitle}} · {{item.chapterTitle}}</text>
|
||||
</view>
|
||||
<text class="result-arrow">›</text>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 无结果 -->
|
||||
<view class="empty-wrap" wx:elif="{{!loading}}">
|
||||
<text class="empty-icon">🔍</text>
|
||||
<text class="empty-text">未找到相关章节</text>
|
||||
<text class="empty-hint">换个关键词试试</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="keywords-section" wx:if="{{keywords.length > 0}}">
|
||||
<text class="keywords-label">相关标签</text>
|
||||
<view class="keywords-tags">
|
||||
<view class="keyword-tag" wx:for="{{keywords}}" wx:for-item="kw" wx:key="*this" wx:if="{{kw}}" data-keyword="{{kw}}" bindtap="onKeywordTap">#{{kw}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<block wx:elif="{{query && !isLoading}}">
|
||||
<view class="empty-wrap">
|
||||
<text class="empty-icon">🔍</text>
|
||||
<text class="empty-text">未找到相关内容</text>
|
||||
<text class="empty-hint">试试其他关键词</text>
|
||||
</view>
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
@@ -1,335 +1,40 @@
|
||||
/* 章节搜索页样式 */
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #0a0a0a 0%, #111111 100%);
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: rgba(10, 10, 10, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.nav-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 24rpx;
|
||||
height: 88rpx;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
font-size: 40rpx;
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border-radius: 40rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 64rpx;
|
||||
margin: 0 16rpx;
|
||||
}
|
||||
|
||||
.search-icon-small {
|
||||
font-size: 28rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
font-size: 28rpx;
|
||||
color: #00CED1;
|
||||
padding: 0 16rpx;
|
||||
}
|
||||
|
||||
/* 主内容 */
|
||||
.main-content {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
/* 热门搜索 */
|
||||
.hot-section {
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255,255,255,0.6);
|
||||
margin-bottom: 24rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hot-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.hot-tag {
|
||||
background: rgba(0, 206, 209, 0.15);
|
||||
color: #00CED1;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 28rpx;
|
||||
border: 1rpx solid rgba(0, 206, 209, 0.3);
|
||||
}
|
||||
|
||||
/* 热门章节 */
|
||||
.hot-chapters {
|
||||
padding: 32rpx 0;
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.chapter-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.chapter-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.chapter-rank {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chapter-item:nth-child(1) .chapter-rank { background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); }
|
||||
.chapter-item:nth-child(2) .chapter-rank { background: linear-gradient(135deg, #C0C0C0 0%, #A9A9A9 100%); }
|
||||
.chapter-item:nth-child(3) .chapter-rank { background: linear-gradient(135deg, #CD7F32 0%, #8B4513 100%); }
|
||||
|
||||
.chapter-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chapter-title {
|
||||
font-size: 28rpx;
|
||||
color: #fff;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.chapter-part {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255,255,255,0.4);
|
||||
margin-top: 6rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chapter-tag {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chapter-tag.tag-free { background: rgba(76, 175, 80, 0.2); color: #4CAF50; }
|
||||
.chapter-tag.tag-hot { background: rgba(255, 87, 34, 0.2); color: #FF5722; }
|
||||
.chapter-tag.tag-new { background: rgba(233, 30, 99, 0.2); color: #E91E63; }
|
||||
|
||||
/* 搜索结果 */
|
||||
.results-section {
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.results-count {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
position: relative;
|
||||
border: 1rpx solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.result-chapter {
|
||||
font-size: 24rpx;
|
||||
color: #00CED1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.result-tags {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
font-size: 20rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.tag-match {
|
||||
background: rgba(147, 112, 219, 0.2);
|
||||
color: #9370DB;
|
||||
}
|
||||
|
||||
.tag-free {
|
||||
background: rgba(76, 175, 80, 0.2);
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.result-title {
|
||||
font-size: 30rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.result-part {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
margin-top: 16rpx;
|
||||
padding-top: 16rpx;
|
||||
border-top: 1rpx solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.content-preview {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255,255,255,0.6);
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.result-arrow {
|
||||
position: absolute;
|
||||
right: 28rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 32rpx;
|
||||
color: rgba(255,255,255,0.3);
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 4rpx solid rgba(0, 206, 209, 0.3);
|
||||
border-top-color: #00CED1;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255,255,255,0.6);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.empty-hint {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
.page { min-height: 100vh; background: #000; display: flex; flex-direction: column; }
|
||||
.nav-bar { background: #1c1c1e; border-bottom: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; display: flex; flex-direction: column; justify-content: flex-end; }
|
||||
.nav-inner { display: flex; align-items: center; padding: 0 24rpx; height: 88rpx; min-height: 44px; flex-shrink: 0; }
|
||||
.nav-back { font-size: 32rpx; color: #00CED1; padding: 16rpx 0; }
|
||||
.nav-title { flex: 1; text-align: center; font-size: 34rpx; color: #fff; }
|
||||
.search-box { padding: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.search-input-wrap { display: flex; align-items: center; padding: 24rpx 32rpx; background: #2c2c2e; border-radius: 24rpx; border: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.search-icon { font-size: 32rpx; margin-right: 16rpx; opacity: 0.6; }
|
||||
.search-input { flex: 1; font-size: 28rpx; color: #fff; }
|
||||
.search-clear { font-size: 40rpx; color: rgba(255,255,255,0.5); padding: 0 16rpx; }
|
||||
.result-area { flex: 1; height: 0; }
|
||||
.hot-section { padding: 32rpx; }
|
||||
.hot-label { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 24rpx; }
|
||||
.hot-tags { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.hot-tag { padding: 16rpx 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); background: #2c2c2e; border-radius: 32rpx; }
|
||||
.loading-wrap { padding: 80rpx; text-align: center; }
|
||||
.loading-spinner { width: 48rpx; height: 48rpx; border: 4rpx solid rgba(0,206,209,0.3); border-top-color: #00CED1; border-radius: 50%; animation: spin 0.8s linear infinite; margin: 0 auto 24rpx; }
|
||||
.loading-text { font-size: 28rpx; color: rgba(255,255,255,0.5); }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.result-header { padding: 24rpx 32rpx; font-size: 24rpx; color: rgba(255,255,255,0.4); border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.result-list { }
|
||||
.result-item { display: flex; align-items: flex-start; padding: 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.result-icon { width: 64rpx; height: 64rpx; border-radius: 16rpx; background: rgba(0,206,209,0.1); display: flex; align-items: center; justify-content: center; font-size: 32rpx; margin-right: 24rpx; flex-shrink: 0; }
|
||||
.result-body { flex: 1; min-width: 0; }
|
||||
.result-meta { display: flex; align-items: center; gap: 16rpx; margin-bottom: 8rpx; }
|
||||
.result-id { font-size: 24rpx; color: #00CED1; font-family: monospace; }
|
||||
.tag-free { font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 6rpx; background: rgba(0,206,209,0.1); color: #00CED1; }
|
||||
.tag-content { font-size: 20rpx; padding: 4rpx 12rpx; border-radius: 6rpx; background: rgba(123,97,255,0.1); color: #7B61FF; }
|
||||
.result-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; margin-bottom: 4rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.result-snippet { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 4rpx; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||||
.result-path { font-size: 22rpx; color: rgba(255,255,255,0.3); }
|
||||
.result-arrow { font-size: 32rpx; color: rgba(255,255,255,0.3); margin-left: 16rpx; }
|
||||
.keywords-section { padding: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.05); }
|
||||
.keywords-label { font-size: 24rpx; color: rgba(255,255,255,0.4); display: block; margin-bottom: 16rpx; }
|
||||
.keywords-tags { display: flex; flex-wrap: wrap; gap: 16rpx; }
|
||||
.keyword-tag { font-size: 24rpx; color: rgba(255,255,255,0.5); padding: 8rpx 16rpx; background: #2c2c2e; border-radius: 8rpx; }
|
||||
.empty-wrap { padding: 80rpx; text-align: center; }
|
||||
.empty-icon { font-size: 80rpx; display: block; margin-bottom: 24rpx; opacity: 0.5; }
|
||||
.empty-text { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; }
|
||||
.empty-hint { font-size: 24rpx; color: rgba(255,255,255,0.3); margin-top: 8rpx; display: block; }
|
||||
|
||||
Reference in New Issue
Block a user