add searchable
This commit is contained in:
@ -5,6 +5,8 @@
|
||||
"t-icon": "tdesign-miniprogram/icon/icon",
|
||||
"t-button": "tdesign-miniprogram/button/button",
|
||||
"t-loading": "tdesign-miniprogram/loading/loading",
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group"
|
||||
"t-cell-group": "tdesign-miniprogram/cell-group/cell-group",
|
||||
"t-search": "tdesign-miniprogram/search/search",
|
||||
"t-empty": "tdesign-miniprogram/empty/empty"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,29 +2,52 @@
|
||||
.journal-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.item {
|
||||
|
||||
&.selected {
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
object-fit: cover;
|
||||
background: var(--td-bg-color-component);
|
||||
flex-shrink: 0;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
.search-bar {
|
||||
padding: 8rpx 16rpx;
|
||||
flex-shrink: 0;
|
||||
background: var(--td-bg-color-container);
|
||||
box-shadow: 0 4rpx 8rpx var(--theme-shadow-light);
|
||||
}
|
||||
|
||||
.loading,
|
||||
.finished,
|
||||
.empty {
|
||||
color: var(--td-text-color-placeholder);
|
||||
padding: 24rpx 0;
|
||||
text-align: center;
|
||||
.scroll-content {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
|
||||
.item {
|
||||
|
||||
&.selected {
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
flex-shrink: 0;
|
||||
object-fit: cover;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--td-bg-color-component);
|
||||
}
|
||||
|
||||
.date {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.location {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.loading,
|
||||
.finished {
|
||||
color: var(--td-text-color-placeholder);
|
||||
padding: 32rpx 0;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
import config from "../../config/index";
|
||||
import { JournalPage, JournalPageType } from "../../types/Journal";
|
||||
import Time from "../../utils/Time";
|
||||
import Toolkit from "../../utils/Toolkit";
|
||||
|
||||
export type JournalListItem = {
|
||||
id: number;
|
||||
@ -16,9 +17,15 @@ interface JournalListData {
|
||||
isFetching: boolean;
|
||||
isFinished: boolean;
|
||||
page: JournalPage;
|
||||
searchValue: string;
|
||||
}
|
||||
|
||||
Component({
|
||||
// 组件实例类型扩展
|
||||
interface ComponentInstance {
|
||||
debouncedSearch?: ((keyword: string) => void) & { cancel(): void };
|
||||
}
|
||||
|
||||
Component<JournalListData, {}, {}, ComponentInstance>({
|
||||
options: {
|
||||
styleIsolation: 'apply-shared'
|
||||
},
|
||||
@ -34,6 +41,14 @@ Component({
|
||||
selectedId: {
|
||||
type: Number,
|
||||
value: undefined
|
||||
},
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
value: false // 是否显示搜索框
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
value: 'select' // 'select' 选择模式 或 'navigate' 跳转模式
|
||||
}
|
||||
},
|
||||
data: <JournalListData>{
|
||||
@ -44,27 +59,63 @@ Component({
|
||||
index: 0,
|
||||
size: 16,
|
||||
type: JournalPageType.PREVIEW
|
||||
},
|
||||
searchValue: ""
|
||||
},
|
||||
lifetimes: {
|
||||
ready() {
|
||||
// 创建防抖搜索函数
|
||||
this.debouncedSearch = Toolkit.debounce(
|
||||
(keyword: string) => {
|
||||
this.resetAndSearch(keyword);
|
||||
},
|
||||
false, // 不立即执行,等待输入停止
|
||||
400 // 400ms 延迟
|
||||
);
|
||||
// 组件加载时就获取数据
|
||||
this.fetch();
|
||||
},
|
||||
detached() {
|
||||
// 组件销毁时取消防抖
|
||||
if (this.debouncedSearch) {
|
||||
this.debouncedSearch.cancel();
|
||||
}
|
||||
}
|
||||
},
|
||||
observers: {
|
||||
'visible': function(visible: boolean) {
|
||||
// visible 从 false 变为 true 时,如果还没有数据则加载
|
||||
if (visible && this.data.list.length === 0) {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
'type': function() {
|
||||
this.resetPage();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetPage() {
|
||||
const likeExample = this.data.searchValue ? {
|
||||
idea: this.data.searchValue,
|
||||
location: this.data.searchValue
|
||||
} : undefined;
|
||||
|
||||
const equalsExample = this.properties.type ? {
|
||||
type: this.properties.type
|
||||
} : undefined;
|
||||
|
||||
this.setData({
|
||||
page: {
|
||||
index: 0,
|
||||
size: 16,
|
||||
type: JournalPageType.PREVIEW
|
||||
type: JournalPageType.PREVIEW,
|
||||
equalsExample,
|
||||
likeExample
|
||||
},
|
||||
list: [],
|
||||
isFinished: false
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
fetch() {
|
||||
if (this.data.isFetching || this.data.isFinished) {
|
||||
return;
|
||||
@ -88,7 +139,7 @@ Component({
|
||||
const firstThumb = journal.items.find((item: any) => item.attachType === "THUMB");
|
||||
return {
|
||||
id: journal.id,
|
||||
date: Time.toPassedDateTime(journal.createdAt),
|
||||
date: Time.toPassedDate(journal.createdAt),
|
||||
idea: journal.idea,
|
||||
location: journal.location,
|
||||
thumbUrl: firstThumb ? `${config.url}/attachment/read/${firstThumb.mongoId}` : undefined
|
||||
@ -108,9 +159,70 @@ Component({
|
||||
}
|
||||
});
|
||||
},
|
||||
onSearchChange(e: WechatMiniprogram.CustomEvent) {
|
||||
const value = e.detail.value.trim();
|
||||
this.setData({ searchValue: value });
|
||||
// 如果是清空操作,不使用防抖(clear 事件会处理)
|
||||
if (value === "" && this.debouncedSearch) {
|
||||
this.debouncedSearch.cancel();
|
||||
return;
|
||||
}
|
||||
// 使用防抖自动搜索
|
||||
if (this.debouncedSearch) {
|
||||
this.debouncedSearch(value);
|
||||
}
|
||||
},
|
||||
onSearchSubmit(e: WechatMiniprogram.CustomEvent) {
|
||||
const value = e.detail.value.trim();
|
||||
// 立即搜索,取消防抖
|
||||
if (this.debouncedSearch) {
|
||||
this.debouncedSearch.cancel();
|
||||
}
|
||||
this.resetAndSearch(value);
|
||||
},
|
||||
onSearchClear() {
|
||||
// 取消防抖,立即搜索
|
||||
if (this.debouncedSearch) {
|
||||
this.debouncedSearch.cancel();
|
||||
}
|
||||
this.resetAndSearch("");
|
||||
},
|
||||
resetAndSearch(keyword: string) {
|
||||
const likeExample = keyword ? {
|
||||
idea: keyword,
|
||||
location: keyword
|
||||
} : undefined;
|
||||
|
||||
const equalsExample = this.properties.type ? {
|
||||
type: this.properties.type
|
||||
} : undefined;
|
||||
|
||||
this.setData({
|
||||
searchValue: keyword,
|
||||
list: [],
|
||||
page: {
|
||||
index: 0,
|
||||
size: 16,
|
||||
type: JournalPageType.PREVIEW,
|
||||
equalsExample,
|
||||
likeExample
|
||||
},
|
||||
isFetching: false,
|
||||
isFinished: false
|
||||
}, () => {
|
||||
// 无论是否有搜索词,都重新获取列表
|
||||
this.fetch();
|
||||
});
|
||||
},
|
||||
onSelectItem(e: WechatMiniprogram.BaseEvent) {
|
||||
const { id } = e.currentTarget.dataset;
|
||||
this.triggerEvent('select', { id });
|
||||
if (this.properties.mode === 'navigate') {
|
||||
// 跳转模式:触发 navigate 事件
|
||||
this.triggerEvent('navigate', { id });
|
||||
} else {
|
||||
// 选择模式:触发 select 事件
|
||||
this.triggerEvent('select', { id });
|
||||
}
|
||||
},
|
||||
onScrollToLower() {
|
||||
this.fetch();
|
||||
|
||||
@ -1,40 +1,77 @@
|
||||
<!--components/journal-list/index.wxml-->
|
||||
<scroll-view
|
||||
class="journal-list"
|
||||
scroll-y
|
||||
bindscrolltolower="onScrollToLower"
|
||||
>
|
||||
<t-cell-group>
|
||||
<t-cell
|
||||
class="item {{selectedId === item.id ? 'selected' : ''}}"
|
||||
wx:for="{{list}}"
|
||||
wx:key="id"
|
||||
title="{{item.date}}"
|
||||
description="{{item.idea}}"
|
||||
bind:tap="onSelectItem"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<image
|
||||
slot="left-icon"
|
||||
wx:if="{{item.thumbUrl}}"
|
||||
class="thumb"
|
||||
src="{{item.thumbUrl}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<t-icon wx:if="{{!item.thumbUrl}}" slot="left-icon" name="image-off" color="gray" size="96rpx" />
|
||||
<t-icon
|
||||
wx:if="{{selectedId === item.id}}"
|
||||
slot="note"
|
||||
class="check"
|
||||
name="check"
|
||||
size="48rpx"
|
||||
color="var(--theme-wx)"
|
||||
/>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
<view wx:if="{{isFetching}}" class="loading">
|
||||
<t-loading theme="dots" size="40rpx" />
|
||||
<view class="journal-list">
|
||||
<view wx:if="{{searchable}}" class="search-bar">
|
||||
<t-search
|
||||
value="{{searchValue}}"
|
||||
placeholder="搜索日记内容或地点"
|
||||
bind:change="onSearchChange"
|
||||
bind:submit="onSearchSubmit"
|
||||
bind:clear="onSearchClear"
|
||||
/>
|
||||
</view>
|
||||
<view wx:if="{{isFinished && list.length > 0}}" class="finished">没有更多了</view>
|
||||
<view wx:if="{{!isFetching && list.length === 0}}" class="empty">暂无记录</view>
|
||||
</scroll-view>
|
||||
|
||||
<scroll-view
|
||||
class="scroll-content"
|
||||
scroll-y
|
||||
bindscrolltolower="onScrollToLower"
|
||||
>
|
||||
<block wx:if="{{list.length > 0}}">
|
||||
<t-cell-group>
|
||||
<t-cell
|
||||
class="item {{selectedId === item.id ? 'selected' : ''}}"
|
||||
wx:for="{{list}}"
|
||||
wx:key="id"
|
||||
hover
|
||||
bind:tap="onSelectItem"
|
||||
data-id="{{item.id}}"
|
||||
>
|
||||
<image
|
||||
slot="left-icon"
|
||||
wx:if="{{item.thumbUrl}}"
|
||||
class="thumb"
|
||||
src="{{item.thumbUrl}}"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<t-icon wx:else slot="left-icon" name="image" color="gray" size="96rpx" />
|
||||
<view slot="title">
|
||||
<text class="date">{{item.date}}</text>
|
||||
<text wx:if="{{item.idea}}"> · {{item.idea}}</text>
|
||||
</view>
|
||||
<text wx:if="{{item.location}}" class="location" slot="description">{{item.location}}</text>
|
||||
<t-icon
|
||||
wx:if="{{mode === 'select' && selectedId === item.id}}"
|
||||
slot="right-icon"
|
||||
class="check"
|
||||
name="check"
|
||||
size="48rpx"
|
||||
color="var(--theme-wx)"
|
||||
/>
|
||||
<t-icon
|
||||
wx:elif="{{mode === 'navigate'}}"
|
||||
slot="right-icon"
|
||||
name="chevron-right"
|
||||
/>
|
||||
</t-cell>
|
||||
</t-cell-group>
|
||||
|
||||
<view wx:if="{{isFetching}}" class="loading">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{isFinished}}" class="finished">
|
||||
{{searchable && searchValue ? '已显示全部结果' : '没有更多了'}}
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{isFetching}}">
|
||||
<view class="loading">
|
||||
<t-loading theme="circular" size="40rpx" text="加载中..." />
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{searchable && searchValue}}">
|
||||
<t-empty icon="search" description="未找到相关日记" />
|
||||
</block>
|
||||
<block wx:else>
|
||||
<t-empty icon="image" description="暂无记录" />
|
||||
</block>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
@ -118,7 +118,7 @@
|
||||
border-top-right-radius: 16rpx;
|
||||
|
||||
&.select-journal {
|
||||
max-height: 80vh;
|
||||
height: 80vh;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
|
||||
@ -184,6 +184,7 @@
|
||||
<view class="content">
|
||||
<journal-list
|
||||
visible="{{archiveStep === 'select-journal'}}"
|
||||
searchable="{{true}}"
|
||||
type="{{type}}"
|
||||
selectedId="{{selectedJournalId}}"
|
||||
bind:select="onJournalListSelect"
|
||||
|
||||
Reference in New Issue
Block a user