index.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { useCallback, useEffect, useState } from "react";
  2. import { FC } from "@tarojs/taro";
  3. import { AtFloatLayout, AtInput, AtTabs, AtTabsPane } from "taro-ui";
  4. import { AtFloatLayoutProps } from "taro-ui/types/float-layout";
  5. import { View } from "@tarojs/components";
  6. import { CITY_LIST, SIGHT_LIST } from "../../constants";
  7. import { debounce } from "../../../../utils";
  8. import { getSignListApi } from "../../../../api";
  9. import "./index.scss";
  10. export interface SearchLayoutProps extends AtFloatLayoutProps {
  11. openDetail(id: (typeof SIGHT_LIST)[0]): void;
  12. }
  13. export const SearchLayout: FC<SearchLayoutProps> = (props) => {
  14. const [curTab, setCurTab] = useState(0);
  15. const [keyword, setKeyword] = useState("");
  16. const [input, setInput] = useState("");
  17. const [list, setList] = useState<any>({});
  18. const [loading, setLoading] = useState(false);
  19. useEffect(() => {
  20. if (props.isOpened) {
  21. getSignList();
  22. }
  23. }, [props.isOpened]);
  24. const getSignList = async (idx?: number) => {
  25. const _idx = idx ?? curTab;
  26. if (list[_idx]) return;
  27. try {
  28. setLoading(true);
  29. const data = await getSignListApi({
  30. region: _idx === 0 ? "" : CITY_LIST[_idx].title,
  31. type: "scene",
  32. });
  33. setList({
  34. ...list,
  35. [_idx]: data,
  36. });
  37. } finally {
  38. setLoading(false);
  39. }
  40. };
  41. const handleTabClick = (idx: number) => {
  42. setCurTab(idx);
  43. getSignList(idx);
  44. };
  45. const debounceSearch = useCallback(
  46. debounce((v: string) => {
  47. setKeyword(v);
  48. }, 1000),
  49. []
  50. );
  51. const fakeSearch = (v: string) => {
  52. return v.match(keyword) !== null;
  53. };
  54. return (
  55. <AtFloatLayout className="search-layout" {...props}>
  56. {props.isOpened && (
  57. <>
  58. <View className="search-input">
  59. <AtInput
  60. clear
  61. className="search-input__input"
  62. name="keyword"
  63. value={input}
  64. confirm-type="search"
  65. placeholder="请输入要搜索的内容..."
  66. onChange={(v) => {
  67. setInput(v as string);
  68. debounceSearch(v as string);
  69. }}
  70. />
  71. <View className="search-input__cancel" onClick={props.onClose}>
  72. 取消
  73. </View>
  74. </View>
  75. <AtTabs
  76. current={curTab}
  77. scroll
  78. className="search-tab"
  79. tabList={CITY_LIST}
  80. onClick={handleTabClick}
  81. >
  82. {CITY_LIST.map((item, idx) => {
  83. const _list = (list[idx] || []).filter((i) =>
  84. keyword ? fakeSearch(i.name) : true
  85. );
  86. return (
  87. <AtTabsPane current={curTab} index={item.id}>
  88. {_list.length
  89. ? _list.map((subItem) => (
  90. <View
  91. key={subItem.id}
  92. className="search-tab__item"
  93. onClick={props.openDetail.bind(undefined, subItem)}
  94. >
  95. <View className="search-tab__item-inner">
  96. <View className="search-tab__item-inner__label">
  97. {subItem.name}
  98. </View>
  99. <View className="limit-line">
  100. {subItem.description.slice(0, 50)}
  101. </View>
  102. </View>
  103. <View className="search-tab__item__more" />
  104. </View>
  105. ))
  106. : !loading && (
  107. <View className="search-tab__nomore">暂无内容</View>
  108. )}
  109. </AtTabsPane>
  110. );
  111. })}
  112. </AtTabs>
  113. </>
  114. )}
  115. </AtFloatLayout>
  116. );
  117. };