列表
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
.menuWrap {
|
.menuWrap {
|
||||||
padding: 5px 20px 10px;
|
padding: 5px 20px 10px;
|
||||||
.menuItem {
|
.menuItem {
|
||||||
|
width: 100vw;
|
||||||
left: 0;
|
left: 0;
|
||||||
border-bottom-left-radius: 30px;
|
border-bottom-left-radius: 30px;
|
||||||
border-bottom-right-radius: 30px;
|
border-bottom-right-radius: 30px;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MenuComponent = (props: IProps) => {
|
const MenuComponent = (props: IProps) => {
|
||||||
const { value, onChange, wrapperClassName, itemClassName } = props;
|
const { value, onChange, wrapperClassName, itemClassName, options } = props;
|
||||||
const [isChange, setIsChange] = useState(false);
|
const [isChange, setIsChange] = useState(false);
|
||||||
const itemRef = useRef(null);
|
const itemRef = useRef(null);
|
||||||
|
|
||||||
@@ -22,12 +22,7 @@ const MenuComponent = (props: IProps) => {
|
|||||||
onChange && onChange(value);
|
onChange && onChange(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: BubbleOption[] = [
|
|
||||||
{ id: 0, label: "全城", value: "0" },
|
|
||||||
{ id: 1, label: "3km", value: "3" },
|
|
||||||
{ id: 2, label: "5km", value: "5" },
|
|
||||||
{ id: 3, label: "10km", value: "10" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@@ -38,7 +33,7 @@ const MenuComponent = (props: IProps) => {
|
|||||||
>
|
>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
title="全城"
|
title="全城"
|
||||||
className={`${styles.menuItem} ${itemClassName}`}
|
className={`${styles.menuItem} ${itemClassName ? itemClassName : ''}`}
|
||||||
ref={itemRef}
|
ref={itemRef}
|
||||||
>
|
>
|
||||||
<div className={styles.positionWrap}>
|
<div className={styles.positionWrap}>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
.menuWrap {
|
.menuWrap {
|
||||||
|
position: static;
|
||||||
padding: 5px 20px 10px;
|
padding: 5px 20px 10px;
|
||||||
.menuItem {
|
.menuItem {
|
||||||
|
width: 100vw;
|
||||||
left: 0;
|
left: 0;
|
||||||
border-bottom-left-radius: 30px;
|
border-bottom-left-radius: 30px;
|
||||||
border-bottom-right-radius: 30px;
|
border-bottom-right-radius: 30px;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
--nutui-searchbar-input-text-color: #000000;
|
--nutui-searchbar-input-text-color: #000000;
|
||||||
--nutui-searchbar-input-padding: 0 0 0 10px;
|
--nutui-searchbar-input-padding: 0 0 0 10px;
|
||||||
--nutui-searchbar-padding:0 15px;
|
--nutui-searchbar-padding:0 15px;
|
||||||
// --nutui-searchbar-background: #ffffff;
|
|
||||||
:global(.nut-searchbar-content) {
|
:global(.nut-searchbar-content) {
|
||||||
box-shadow: 0 4px 48px #00000014;
|
box-shadow: 0 4px 48px #00000014;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import { SearchBar } from "@nutui/nutui-react-taro";
|
||||||
|
// import {ICON_FILTER} from '../../config/images.js'
|
||||||
|
import styles from "./index.module.scss";
|
||||||
|
|
||||||
import { SearchBar } from '@nutui/nutui-react-taro'
|
const SearchBarComponent = (props: IProps) => {
|
||||||
import styles from './index.module.scss'
|
// console.log('===', ICON_FILTER)
|
||||||
|
const { handleFilterIcon } = props;
|
||||||
const SearchBarComponent = () => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
@@ -10,13 +12,15 @@ const SearchBarComponent = () => {
|
|||||||
// <div>123</div>
|
// <div>123</div>
|
||||||
// }
|
// }
|
||||||
right={
|
right={
|
||||||
<div className={styles.searchBarRight}>筛</div>
|
<div className={styles.searchBarRight} onClick={handleFilterIcon}>
|
||||||
|
筛
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
className={styles.searchBar}
|
className={styles.searchBar}
|
||||||
placeholder='搜索上海的球局和场地'
|
placeholder="搜索上海的球局和场地"
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default SearchBarComponent
|
export default SearchBarComponent;
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ export default {
|
|||||||
ICON_COST: require('@/static/publishBall/icon-cost.svg'),
|
ICON_COST: require('@/static/publishBall/icon-cost.svg'),
|
||||||
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'),
|
ICON_TIPS: require('@/static/publishBall/icon-tips.svg'),
|
||||||
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'),
|
ICON_ARROW_RIGHT: require('@/static/publishBall/icon-arrow-right.svg'),
|
||||||
|
ICON_FILTER: require('@/static/list/icon-filter.svg'),
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,8 @@ import Range from "../../components/Range";
|
|||||||
import Bubble, { BubbleOption } from "../../components/Bubble";
|
import Bubble, { BubbleOption } from "../../components/Bubble";
|
||||||
import styles from "./filterPopup.module.scss";
|
import styles from "./filterPopup.module.scss";
|
||||||
import TitleComponent from "src/components/Title";
|
import TitleComponent from "src/components/Title";
|
||||||
|
import { Button } from "@nutui/nutui-react-taro";
|
||||||
|
import { useListStore } from "../../store/listStore";
|
||||||
|
|
||||||
const timeOptions: BubbleOption[] = [
|
const timeOptions: BubbleOption[] = [
|
||||||
{ id: 1, label: "晨间 6:00-10:00", value: "morning" },
|
{ id: 1, label: "晨间 6:00-10:00", value: "morning" },
|
||||||
@@ -19,7 +21,14 @@ const locationOptions: BubbleOption[] = [
|
|||||||
{ id: 3, label: "半室外", value: "3" },
|
{ id: 3, label: "半室外", value: "3" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const FilterPopup = () => {
|
interface IProps {
|
||||||
|
onCancel: () => void;
|
||||||
|
onConfirm: () => void;
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FilterPopup = (props: IProps) => {
|
||||||
|
const { loading, onCancel, onConfirm } = props;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Popup
|
<Popup
|
||||||
@@ -28,9 +37,6 @@ const FilterPopup = () => {
|
|||||||
position="top"
|
position="top"
|
||||||
round
|
round
|
||||||
closeOnOverlayClick={false}
|
closeOnOverlayClick={false}
|
||||||
onClose={() => {
|
|
||||||
// setShowTop(false)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className={styles.filterPopupWrapper}>
|
<div className={styles.filterPopupWrapper}>
|
||||||
{/* 时间气泡选项 */}
|
{/* 时间气泡选项 */}
|
||||||
@@ -63,6 +69,15 @@ const FilterPopup = () => {
|
|||||||
columns={3}
|
columns={3}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/* 按钮 */}
|
||||||
|
<div className={styles.filterPopupBtnWrapper}>
|
||||||
|
<Button className={styles.btn} type="default" onClick={onCancel}>
|
||||||
|
清空全部
|
||||||
|
</Button>
|
||||||
|
<Button className={`${styles.btn} ${styles.confirm}`} type="default" loading={loading} onClick={onConfirm}>
|
||||||
|
显示 332 场球局
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,8 +1,35 @@
|
|||||||
.filterPopupWrapper {
|
.filterPopupWrapper {
|
||||||
|
position: relative;
|
||||||
$m18: 18px;
|
$m18: 18px;
|
||||||
padding: $m18;
|
padding: $m18;
|
||||||
|
|
||||||
.filterPopupRange {
|
.filterPopupRange {
|
||||||
margin-top: $m18;
|
margin-top: $m18;
|
||||||
margin-bottom: $m18;
|
margin-bottom: $m18;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filterPopupBtnWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
--nutui-button-border-width: 0.5px;
|
||||||
|
--nutui-button-default-border-color: #0000000F;
|
||||||
|
--nutui-button-border-radius: 16px;
|
||||||
|
--nutui-button-default-height: 44px;
|
||||||
|
--nutui-button-default-color: #000000;
|
||||||
|
.confirm {
|
||||||
|
--nutui-button-default-color: #ffffff;
|
||||||
|
--nutui-button-default-background-color: #000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
15
src/pages/list/index.module.scss
Normal file
15
src/pages/list/index.module.scss
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.listPage {
|
||||||
|
background-color: #fafafa;
|
||||||
|
padding: 0 15px;
|
||||||
|
|
||||||
|
.listTopFilterWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 5px 0 10px;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuFilter {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,26 @@
|
|||||||
import ListItem from "../../components/ListItem";
|
import ListItem from "../../components/ListItem";
|
||||||
import List from "../../components/List";
|
import List from "../../components/List";
|
||||||
import Bubble from "../../components/Bubble/example";
|
import Menu from "../../components/Menu";
|
||||||
import Range from "../../components/Range/example";
|
import CityFilter from "../../components/CityFilter";
|
||||||
import Menu from "../../components/Menu/example";
|
|
||||||
import CityFilter from "../../components/CityFilter/example";
|
|
||||||
import SearchBar from "../../components/SearchBar";
|
import SearchBar from "../../components/SearchBar";
|
||||||
import FilterPopup from "./FilterPopup";
|
import FilterPopup from "./FilterPopup";
|
||||||
import "./index.scss";
|
import styles from "./index.module.scss";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
import {
|
import { useListStore } from "../../store/listStore";
|
||||||
useTennisMatches,
|
|
||||||
useTennisLoading,
|
|
||||||
useTennisError,
|
|
||||||
useTennisLastRefresh,
|
|
||||||
useTennisActions,
|
|
||||||
} from "../../store/listStore";
|
|
||||||
|
|
||||||
const ListPage = () => {
|
const ListPage = () => {
|
||||||
// 从 store 获取数据和方法
|
// 从 store 获取数据和方法
|
||||||
const matches = useTennisMatches();
|
const {
|
||||||
const loading = useTennisLoading();
|
isShowFilterPopup,
|
||||||
const error = useTennisError();
|
error,
|
||||||
const lastRefreshTime = useTennisLastRefresh();
|
matches,
|
||||||
const { fetchMatches, refreshMatches, clearError } = useTennisActions();
|
loading,
|
||||||
|
fetchMatches,
|
||||||
|
refreshMatches,
|
||||||
|
clearError,
|
||||||
|
updateState,
|
||||||
|
} = useListStore() || {};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 页面加载时获取数据
|
// 页面加载时获取数据
|
||||||
@@ -146,23 +143,44 @@ const ListPage = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleShowPopup = () => {
|
||||||
|
updateState({ isShowFilterPopup: !isShowFilterPopup });
|
||||||
|
};
|
||||||
|
|
||||||
|
const cityOptions: BubbleOption[] = [
|
||||||
|
{ id: 0, label: "全城", value: "0" },
|
||||||
|
{ id: 1, label: "3km", value: "3" },
|
||||||
|
{ id: 2, label: "5km", value: "5" },
|
||||||
|
{ id: 3, label: "10km", value: "10" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{ text: "默认排序", value: "a" },
|
||||||
|
{ text: "好评排序", value: "b" },
|
||||||
|
{ text: "销量排序", value: "c" },
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.listPage}>
|
||||||
<SearchBar />
|
<SearchBar handleFilterIcon={toggleShowPopup} />
|
||||||
{/* 综合筛选 */}
|
{/* 综合筛选 */}
|
||||||
|
{isShowFilterPopup && (
|
||||||
<div>
|
<div>
|
||||||
<FilterPopup />
|
<FilterPopup
|
||||||
|
loading={loading}
|
||||||
|
onCancel={toggleShowPopup}
|
||||||
|
onConfirm={toggleShowPopup}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{/* 筛选 */}
|
{/* 筛选 */}
|
||||||
<div>
|
<div className={ styles.listTopFilterWrapper}>
|
||||||
{/* 全城筛选 */}
|
{/* 全城筛选 */}
|
||||||
<CityFilter />
|
<CityFilter options={cityOptions} value="1" onChange={() => { }} wrapperClassName={styles.menuFilter} />
|
||||||
{/* 智能排序 */}
|
{/* 智能排序 */}
|
||||||
<Menu />
|
<Menu options={options} value="a" onChange={() => { }} wrapperClassName={styles.menuFilter} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* 列表内容 */}
|
{/* 列表内容 */}
|
||||||
<List>
|
<List>
|
||||||
{matches.map((match, index) => (
|
{matches.map((match, index) => (
|
||||||
|
|||||||
11
src/static/list/icon-filter.svg
Normal file
11
src/static/list/icon-filter.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.2917 4.16666H14.7917" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.4583 2.5V5.83333" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.4584 4.16666H2.29175" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M5.62508 10H2.29175" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M8.95825 8.33333V11.6667" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M18.1249 10H8.95825" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M17.2917 15.8333H14.7917" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.4583 14.1667V17.5" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.4584 15.8333H2.29175" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -16,15 +16,16 @@ export interface TennisMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store 状态接口
|
// Store 状态接口
|
||||||
interface TennisState {
|
interface ListState {
|
||||||
matches: TennisMatch[]
|
matches: TennisMatch[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
error: string | null
|
error: string | null
|
||||||
lastRefreshTime: string | null
|
lastRefreshTime: string | null
|
||||||
|
isShowFilterPopup: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store Actions 接口
|
// Store Actions 接口
|
||||||
interface TennisActions {
|
interface ListActions {
|
||||||
fetchMatches: (params?: {
|
fetchMatches: (params?: {
|
||||||
page?: number
|
page?: number
|
||||||
pageSize?: number
|
pageSize?: number
|
||||||
@@ -33,18 +34,21 @@ interface TennisActions {
|
|||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
refreshMatches: () => Promise<void>
|
refreshMatches: () => Promise<void>
|
||||||
clearError: () => void
|
clearError: () => void
|
||||||
|
updateState: (payload: Record<string, any>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
// 完整的 Store 类型
|
// 完整的 Store 类型
|
||||||
type TennisStore = TennisState & TennisActions
|
type TennisStore = ListState & ListActions
|
||||||
|
|
||||||
// 创建 store
|
// 创建 store
|
||||||
export const useTennisStore = create<TennisStore>()((set, get) => ({
|
export const useListStore = create<TennisStore>()((set, get) => ({
|
||||||
// 初始状态
|
// 初始状态
|
||||||
matches: [],
|
matches: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
lastRefreshTime: null,
|
lastRefreshTime: null,
|
||||||
|
// 是否展示综合筛选弹窗
|
||||||
|
isShowFilterPopup: false,
|
||||||
|
|
||||||
// 获取比赛数据
|
// 获取比赛数据
|
||||||
fetchMatches: async (params) => {
|
fetchMatches: async (params) => {
|
||||||
@@ -93,16 +97,14 @@ export const useTennisStore = create<TennisStore>()((set, get) => ({
|
|||||||
// 清除错误信息
|
// 清除错误信息
|
||||||
clearError: () => {
|
clearError: () => {
|
||||||
set({ error: null })
|
set({ error: null })
|
||||||
|
},
|
||||||
|
// 更新store数据
|
||||||
|
updateState: (payload: Record<string, any>) => {
|
||||||
|
set({
|
||||||
|
...(payload || {})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 导出便捷的 hooks
|
// 导出便捷的 hooks
|
||||||
export const useTennisMatches = () => useTennisStore((state) => state.matches)
|
export const useListState = () => useListStore((state) => state)
|
||||||
export const useTennisLoading = () => useTennisStore((state) => state.loading)
|
|
||||||
export const useTennisError = () => useTennisStore((state) => state.error)
|
|
||||||
export const useTennisLastRefresh = () => useTennisStore((state) => state.lastRefreshTime)
|
|
||||||
export const useTennisActions = () => useTennisStore((state) => ({
|
|
||||||
fetchMatches: state.fetchMatches,
|
|
||||||
refreshMatches: state.refreshMatches,
|
|
||||||
clearError: state.clearError
|
|
||||||
}))
|
|
||||||
|
|||||||
Reference in New Issue
Block a user