232 lines
5.3 KiB
TypeScript
232 lines
5.3 KiB
TypeScript
// components/journal-list/index.ts
|
||
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;
|
||
date: string;
|
||
location?: string;
|
||
idea?: string;
|
||
thumbUrl?: string;
|
||
}
|
||
|
||
interface JournalListData {
|
||
list: JournalListItem[];
|
||
isFetching: boolean;
|
||
isFinished: boolean;
|
||
page: JournalPage;
|
||
searchValue: string;
|
||
}
|
||
|
||
// 组件实例类型扩展
|
||
interface ComponentInstance {
|
||
debouncedSearch?: ((keyword: string) => void) & { cancel(): void };
|
||
}
|
||
|
||
Component<JournalListData, {}, {}, ComponentInstance>({
|
||
options: {
|
||
styleIsolation: 'apply-shared'
|
||
},
|
||
properties: {
|
||
visible: {
|
||
type: Boolean,
|
||
value: false
|
||
},
|
||
type: {
|
||
type: String,
|
||
value: undefined // NORMAL 或 PORTFOLIO,不传则获取所有类型
|
||
},
|
||
selectedId: {
|
||
type: Number,
|
||
value: undefined
|
||
},
|
||
searchable: {
|
||
type: Boolean,
|
||
value: false // 是否显示搜索框
|
||
},
|
||
mode: {
|
||
type: String,
|
||
value: 'select' // 'select' 选择模式 或 'navigate' 跳转模式
|
||
}
|
||
},
|
||
data: <JournalListData>{
|
||
list: [],
|
||
isFetching: false,
|
||
isFinished: false,
|
||
page: {
|
||
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,
|
||
equalsExample,
|
||
likeExample
|
||
},
|
||
list: [],
|
||
isFinished: false
|
||
});
|
||
},
|
||
fetch() {
|
||
if (this.data.isFetching || this.data.isFinished) {
|
||
return;
|
||
}
|
||
this.setData({ isFetching: true });
|
||
wx.request({
|
||
url: `${config.url}/journal/list`,
|
||
method: "POST",
|
||
header: {
|
||
Key: wx.getStorageSync("key")
|
||
},
|
||
data: this.data.page,
|
||
success: (resp: any) => {
|
||
const list = resp.data.data.list;
|
||
if (!list || list.length === 0) {
|
||
this.setData({ isFinished: true });
|
||
return;
|
||
}
|
||
const result = list.map((journal: any) => {
|
||
// 获取第一张缩略图
|
||
const firstThumb = journal.items.find((item: any) => item.attachType === "THUMB");
|
||
return {
|
||
id: journal.id,
|
||
date: Time.toPassedDate(journal.createdAt),
|
||
idea: journal.idea,
|
||
location: journal.location,
|
||
thumbUrl: firstThumb ? `${config.url}/attachment/read/${firstThumb.mongoId}` : undefined
|
||
} as JournalListItem;
|
||
});
|
||
this.setData({
|
||
page: {
|
||
...this.data.page,
|
||
index: this.data.page.index + 1
|
||
},
|
||
list: this.data.list.concat(result),
|
||
isFinished: list.length < this.data.page.size
|
||
});
|
||
},
|
||
complete: () => {
|
||
this.setData({ isFetching: false });
|
||
}
|
||
});
|
||
},
|
||
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;
|
||
if (this.properties.mode === 'navigate') {
|
||
// 跳转模式:触发 navigate 事件
|
||
this.triggerEvent('navigate', { id });
|
||
} else {
|
||
// 选择模式:触发 select 事件
|
||
this.triggerEvent('select', { id });
|
||
}
|
||
},
|
||
onScrollToLower() {
|
||
this.fetch();
|
||
}
|
||
}
|
||
});
|