feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="page-header">
|
||||
<Typography variant="h3" weight="bold" class="page-title">黄历搜索</Typography>
|
||||
</view>
|
||||
|
||||
<view class="page-content">
|
||||
<TemplatePanel @select="selectTemplate" />
|
||||
|
||||
<SearchConditionPanel
|
||||
:conditions="searchRequest.conditions"
|
||||
:days="searchRequest.days"
|
||||
@update:conditions="updateConditions"
|
||||
@update:days="updateDays"
|
||||
@search="performSearch"
|
||||
/>
|
||||
|
||||
<view v-if="loading" class="loading-container">
|
||||
<LoadingIndicator text="搜索中..." />
|
||||
</view>
|
||||
|
||||
<view v-else-if="error" class="error-container">
|
||||
<Typography variant="body" class="error-text">{{ error }}</Typography>
|
||||
<Button @click="performSearch">重试</Button>
|
||||
</view>
|
||||
|
||||
<SearchResultList
|
||||
v-else-if="searchResults.length > 0"
|
||||
:results="searchResults"
|
||||
:sortBy="searchRequest.sortBy"
|
||||
:sortOrder="searchRequest.sortOrder"
|
||||
@update:sortBy="updateSortBy"
|
||||
@update:sortOrder="updateSortOrder"
|
||||
@loadMore="loadMoreResults"
|
||||
/>
|
||||
|
||||
<EmptyState
|
||||
v-else
|
||||
:showAction="hasSearched"
|
||||
actionText="重新搜索"
|
||||
@action="performSearch"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<BottomNavigation currentTab="almanac" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import type { SearchCondition, SearchRequest, SearchResult } from '../../types/search'
|
||||
import BottomNavigation from '../../components/BottomNavigation/BottomNavigation.vue'
|
||||
import TemplatePanel from '../../components/TemplatePanel/index.vue'
|
||||
import SearchConditionPanel from '../../components/SearchConditionPanel/index.vue'
|
||||
import SearchResultList from '../../components/SearchResultList/index.vue'
|
||||
import LoadingIndicator from '../../components/LoadingIndicator/index.vue'
|
||||
import EmptyState from '../../components/EmptyState/index.vue'
|
||||
import Typography from '../../components/Typography/Typography.vue'
|
||||
import Button from '../../components/Button/Button.vue'
|
||||
import searchService from '../../services/searchService'
|
||||
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const hasSearched = ref(false)
|
||||
const searchResults = ref<SearchResult[]>([])
|
||||
const searchMode = ref<'keyword' | 'advanced'>('advanced')
|
||||
|
||||
const searchRequest = ref<SearchRequest>({
|
||||
conditions: [],
|
||||
days: 30,
|
||||
sortBy: 'date',
|
||||
sortOrder: 'asc'
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1] as any
|
||||
const options = currentPage?.options || {}
|
||||
|
||||
if (options.keyword) {
|
||||
searchMode.value = 'keyword'
|
||||
handleKeywordSearch(decodeURIComponent(options.keyword))
|
||||
} else if (options.conditions) {
|
||||
try {
|
||||
searchRequest.value.conditions = JSON.parse(decodeURIComponent(options.conditions))
|
||||
if (options.days) {
|
||||
searchRequest.value.days = parseInt(options.days)
|
||||
}
|
||||
performSearch()
|
||||
} catch (err) {
|
||||
console.error('解析搜索条件失败:', err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleKeywordSearch = async (keyword: string) => {
|
||||
if (!keyword.trim()) {
|
||||
error.value = '请输入搜索关键词'
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
hasSearched.value = true
|
||||
|
||||
try {
|
||||
const results = await searchService.searchByKeyword(keyword)
|
||||
searchResults.value = results
|
||||
|
||||
if (results.length === 0) {
|
||||
error.value = '没有找到符合条件的结果'
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('搜索失败:', err)
|
||||
error.value = '搜索失败,请稍后重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const updateConditions = (conditions: SearchCondition[]) => {
|
||||
searchRequest.value.conditions = conditions
|
||||
}
|
||||
|
||||
const updateDays = (days: number) => {
|
||||
searchRequest.value.days = days
|
||||
}
|
||||
|
||||
const updateSortBy = (sortBy: 'date' | 'matchCount') => {
|
||||
searchRequest.value.sortBy = sortBy
|
||||
sortResults()
|
||||
}
|
||||
|
||||
const updateSortOrder = (sortOrder: 'asc' | 'desc') => {
|
||||
searchRequest.value.sortOrder = sortOrder
|
||||
sortResults()
|
||||
}
|
||||
|
||||
const sortResults = () => {
|
||||
const sorted = [...searchResults.value].sort((a, b) => {
|
||||
let comparison = 0
|
||||
|
||||
if (searchRequest.value.sortBy === 'date') {
|
||||
comparison = new Date(a.date).getTime() - new Date(b.date).getTime()
|
||||
} else if (searchRequest.value.sortBy === 'matchCount') {
|
||||
comparison = b.matchCount - a.matchCount
|
||||
}
|
||||
|
||||
return searchRequest.value.sortOrder === 'desc' ? -comparison : comparison
|
||||
})
|
||||
|
||||
searchResults.value = sorted
|
||||
}
|
||||
|
||||
const performSearch = async () => {
|
||||
if (searchRequest.value.conditions.length === 0) {
|
||||
error.value = '请至少添加一个搜索条件'
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
hasSearched.value = true
|
||||
|
||||
try {
|
||||
const results = await searchService.search(searchRequest.value)
|
||||
searchResults.value = results
|
||||
|
||||
if (results.length === 0) {
|
||||
error.value = '没有找到符合条件的结果'
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('搜索失败:', err)
|
||||
error.value = '搜索失败,请稍后重试'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadMoreResults = () => {
|
||||
console.log('加载更多结果')
|
||||
}
|
||||
|
||||
const selectTemplate = (condition: SearchRequest) => {
|
||||
searchRequest.value = { ...condition }
|
||||
performSearch()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background-color: rgba(244, 244, 245, 0.3);
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background-color: rgba(255, 255, 255, 255);
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: rgba(44, 24, 16, 255);
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: rgba(196, 30, 58, 255);
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 375px) {
|
||||
.page-content {
|
||||
padding: 12px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 16px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.loading-container,
|
||||
.error-container {
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user