Files
everything-is-suitable/everything-is-suitable-uniapp/src/components/SearchHistoryPanel/index.vue
T
zhangxiang 810081ac0a feat: 小程序端 UI 优化与纯客户端化改造
- 移除微信订阅推送功能(云函数/订阅管理页),回归纯客户端零后端架构
- 新增今日黄历默认展示卡片 TodayAlmanacCard(9 语言翻译)
- i18n 修复:启用 globalInjection 修复 $t 未注入,插值消息全部改为
  Messages Functions 适配小程序端(63 处)
- 修复底部导航切换失效与运势页数据不刷新
- 统一设计令牌(深褐主色 + 印红强调色),组件 UI 规范重构
- 图标 iconfont 化(FontAwesome 子集化,修复 Android 豆腐块)
- 新增 postcss-px2rpx 机型自适应(仅 mp-weixin 构建生效)
- 新增 5 个用户旅程 E2E 测试(31 用例)与验收报告
- 补充微信小程序项目配置(appid)
2026-08-28 07:51:02 +08:00

184 lines
4.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
已实现但当前未接入任何页面预留组件)。
若后续要在搜索结果区展示"搜索历史"在页面中引入并监听 select 事件即可
-->
<template>
<Card class="search-history-panel">
<view class="panel-header">
<Typography variant="h4" weight="semibold">{{ $t('search.searchHistory') }}</Typography>
<Button size="small" type="text" @click="handleClearHistory">
<Typography variant="caption" color="rgba(196, 30, 58, 255)">{{ $t('search.clearHistory') }}</Typography>
</Button>
</view>
<view v-if="history.length === 0" class="empty-history">
<EmptyState
icon="empty"
:title="$t('search.noHistoryTitle')"
:description="$t('search.noHistoryDesc')"
size="small"
/>
</view>
<view v-else class="history-list">
<view
v-for="item in history"
:key="item.id"
class="history-item"
@click="handleSelectHistory(item)"
>
<view class="history-content">
<Typography variant="body" class="history-title">
{{ formatHistoryTitle(item.condition) }}
</Typography>
<Typography variant="caption" class="history-time">
{{ formatTime(item.createdAt) }}
</Typography>
</view>
<Icon name="right" size="16rpx" color="rgba(113, 113, 122, 255)" />
</view>
</view>
</Card>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import type { SearchRequest } from '../../types/search'
import searchService from '../../services/searchService'
import Card from '../Card/Card.vue'
import Button from '../Button/Button.vue'
import Typography from '../Typography/Typography.vue'
import Icon from '../Icon/Icon.vue'
import EmptyState from '../EmptyState/index.vue'
interface HistoryItem {
id: string
condition: SearchRequest
createdAt: string
}
const { t } = useI18n()
const emit = defineEmits<{
select: [condition: SearchRequest]
}>()
const history = ref<HistoryItem[]>([])
onMounted(() => {
loadHistory()
})
function loadHistory() {
history.value = searchService.getSearchHistory()
}
function handleSelectHistory(item: HistoryItem) {
emit('select', item.condition)
}
function handleClearHistory() {
uni.showModal({
title: t('search.confirmClear'),
content: t('search.confirmClearContent'),
success: (res) => {
if (res.confirm) {
searchService.clearSearchHistory()
history.value = []
}
}
})
}
function formatHistoryTitle(condition: SearchRequest): string {
const conditionCount = condition.conditions.length
const days = condition.days
let title = t('search.historyConditionDays', { count: conditionCount, days })
if (conditionCount > 0) {
const firstCondition = condition.conditions[0]
const type = firstCondition.type === 'suitable' ? t('search.suitable') : t('search.unsuitable')
const items = firstCondition.items.slice(0, 2).join('、')
title += `${type}${items}${firstCondition.items.length > 2 ? '...' : ''}`
}
return title
}
function formatTime(timeStr: string): string {
const date = new Date(timeStr)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
if (diffHours < 1) {
return t('search.justNow')
} else if (diffHours < 24) {
return t('search.hoursAgo', { count: diffHours })
} else {
const diffDays = Math.floor(diffHours / 24)
if (diffDays < 7) {
return t('search.daysAgo', { count: diffDays })
} else {
return date.toLocaleDateString()
}
}
}
</script>
<style scoped lang="scss">
.search-history-panel {
margin-bottom: 16px;
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.empty-history {
padding: 24px 0;
}
.history-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.history-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: rgba(244, 244, 245, 255);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--transition-fast);
&:active {
background: rgba(230, 225, 220, 255);
}
}
.history-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.history-title {
color: var(--color-text);
}
.history-time {
color: var(--color-text-secondary);
}
</style>