添加行政区选择
This commit is contained in:
265
src/components/DistanceQuickFilterV2/index.scss
Normal file
265
src/components/DistanceQuickFilterV2/index.scss
Normal file
@@ -0,0 +1,265 @@
|
||||
.distanceQuickFilterWrap {
|
||||
width: 100%;
|
||||
font-family: "PingFang SC";
|
||||
position: relative;
|
||||
|
||||
|
||||
|
||||
|
||||
// 全局覆盖 NutUI Menu 容器的 overflow 样式
|
||||
|
||||
|
||||
.nut-menu-container-wrap {
|
||||
position: fixed !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: auto !important;
|
||||
border-bottom-left-radius: 30px;
|
||||
border-bottom-right-radius: 30px;
|
||||
background-color: #fafafa !important;
|
||||
z-index: 1100 !important;
|
||||
box-sizing: border-box !important;
|
||||
max-height: auto !important;
|
||||
height: 380px !important;
|
||||
|
||||
}
|
||||
|
||||
.nut-menu-container-content {
|
||||
overflow: visible !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
background-color: transparent !important;
|
||||
max-height: auto !important;
|
||||
|
||||
}
|
||||
|
||||
.nut-menu-bar {
|
||||
--nutui-menu-bar-line-height: 30px;
|
||||
background-color: #fafafa !important; // 明确设置背景色,避免组件默认样式覆盖
|
||||
box-shadow: unset;
|
||||
// padding: 0 15px;
|
||||
gap: 5px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.nut-menu-title {
|
||||
flex: unset;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
height: 28px;
|
||||
padding: 4px 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 999px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.06); // 根据设计稿添加边框
|
||||
background: #ffffff;
|
||||
font-family: "PingFang SC"; // 根据设计稿设置字体
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px; // 1.4285714285714286em ≈ 20px
|
||||
letter-spacing: -0.23px; // -1.6428571726594652% of 14px
|
||||
}
|
||||
|
||||
.nut-menu-title.active {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.positionWrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
padding-left: 20px ;
|
||||
padding-right: 20px ;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cityName {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #3c3c43;
|
||||
}
|
||||
|
||||
.distanceWrap {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.distanceBubbleItem {
|
||||
display: flex;
|
||||
width: 80px;
|
||||
height: 28px;
|
||||
padding: 4px 10px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 999px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.itemIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
// 新增:行政区选择区域样式
|
||||
.districtWrap2 {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.districtContent {
|
||||
display: flex;
|
||||
align-items: stretch; // 让子元素高度一致
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
flex: 1; // 占满父容器剩余空间
|
||||
overflow: hidden; // 确保子元素不会超出圆角
|
||||
|
||||
.districtTitleBox {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
height: 100%; // 占满父容器高度
|
||||
width: 82px;
|
||||
|
||||
.districtTitle {
|
||||
flex-shrink: 0;
|
||||
padding: 10px 0 10px 20px; // 左侧标题的 padding
|
||||
z-index: 1;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4285714285714286em; // 20px
|
||||
letter-spacing: -0.23px;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.districtTitleBg {
|
||||
width: 100%; // 自适应宽度
|
||||
height: 100%; // 自适应高度
|
||||
background-color: #f2f2f7; // 灰色背景
|
||||
border-top-right-radius: 20px; // 右上角圆角
|
||||
z-index: 0; // 在文字下方
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.districtOptionsWrapper {
|
||||
flex: 1;
|
||||
max-height: 80vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 290px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
|
||||
.districtOptionsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
|
||||
.districtItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 10px 20px 8px 20px;
|
||||
height: 38px;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
font-family: "PingFang SC";
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2857142857142858em; // 18px
|
||||
letter-spacing: -0.2px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.districtItemText {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quickOptionsWrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
|
||||
.quickItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
|
||||
&.active {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.distanceQuickFilterWrap_0 .nut-menu-title-0 {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.distanceQuickFilterWrap_1 .nut-menu-title-1 {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
176
src/components/DistanceQuickFilterV2/index.tsx
Normal file
176
src/components/DistanceQuickFilterV2/index.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { Menu } from "@nutui/nutui-react-taro";
|
||||
import { Image, View, ScrollView } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import Bubble from "../Bubble";
|
||||
import "./index.scss";
|
||||
|
||||
const DistanceQuickFilterV2 = (props) => {
|
||||
const {
|
||||
cityOptions,
|
||||
quickOptions,
|
||||
districtOptions = [], // 新增:行政区选项
|
||||
onChange,
|
||||
cityName,
|
||||
quickName,
|
||||
districtName = "district", // 新增:行政区字段名
|
||||
cityValue,
|
||||
quickValue,
|
||||
districtValue, // 新增:行政区选中值
|
||||
onMenuVisibleChange, // 菜单展开/收起回调
|
||||
} = props;
|
||||
const cityRef = useRef(null);
|
||||
const quickRef = useRef(null);
|
||||
const [changePosition, setChangePosition] = useState<number[]>([]);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
// 全城筛选显示的标题 - 如果选择了行政区,显示行政区名称
|
||||
const getCityTitle = () => {
|
||||
if (districtValue) {
|
||||
const selectedDistrict = districtOptions.find((item) => item.value === districtValue);
|
||||
if (selectedDistrict) {
|
||||
return selectedDistrict.label;
|
||||
}
|
||||
}
|
||||
return cityOptions.find((item) => item.value === cityValue)?.label;
|
||||
};
|
||||
const cityTitle = getCityTitle();
|
||||
|
||||
// 快捷筛选显示的标题
|
||||
const quickTitle = quickOptions.find(
|
||||
(item) => item.value === quickValue
|
||||
)?.label;
|
||||
|
||||
// className
|
||||
const filterWrapperClassName = changePosition.reduce((pre, cur) => {
|
||||
return `${pre} distanceQuickFilterWrap_${cur}`;
|
||||
}, "");
|
||||
|
||||
// 处理选择变化
|
||||
const handleChange = (
|
||||
name: string,
|
||||
value: string | number,
|
||||
index: number
|
||||
) => {
|
||||
setChangePosition((preState) => {
|
||||
const newData = new Set([...preState, index]);
|
||||
return Array.from(newData);
|
||||
});
|
||||
onChange && onChange(name, value);
|
||||
|
||||
// 控制隐藏
|
||||
index === 0 && (cityRef.current as any)?.toggle(false);
|
||||
index === 1 && (quickRef.current as any)?.toggle(false);
|
||||
};
|
||||
|
||||
// 监听菜单状态变化,通知父组件
|
||||
useEffect(() => {
|
||||
onMenuVisibleChange?.(isMenuOpen);
|
||||
}, [isMenuOpen, onMenuVisibleChange]);
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Menu
|
||||
className={`distanceQuickFilterWrap ${filterWrapperClassName}`}
|
||||
onOpen={() => setIsMenuOpen(true)}
|
||||
onClose={() => setIsMenuOpen(false)}
|
||||
>
|
||||
<Menu.Item
|
||||
title={cityTitle}
|
||||
ref={cityRef}
|
||||
icon={<Image src={img.ICON_MENU_ITEM_SELECTED} />}
|
||||
>
|
||||
<div className="positionWrap">
|
||||
<p className="title">当前位置</p>
|
||||
<p className="cityName">上海市</p>
|
||||
</div>
|
||||
<div className="distanceWrap">
|
||||
<Bubble
|
||||
options={cityOptions}
|
||||
value={cityValue}
|
||||
onChange={(name, value) => {
|
||||
const singleValue = Array.isArray(value) ? value[0] : value;
|
||||
handleChange(name, singleValue, 0);
|
||||
}}
|
||||
layout="grid"
|
||||
size="small"
|
||||
columns={4}
|
||||
itemClassName="distanceBubbleItem"
|
||||
name={cityName}
|
||||
/>
|
||||
</div>
|
||||
{/* 新增:行政区选择区域 */}
|
||||
{districtOptions.length > 0 && (
|
||||
<div className="districtWrap2">
|
||||
<div className="districtContent">
|
||||
<div className="districtTitleBox">
|
||||
<p className="districtTitle">行政区</p>
|
||||
<div className="districtTitleBg"></div>
|
||||
</div>
|
||||
<ScrollView
|
||||
className="districtOptionsWrapper"
|
||||
scrollY
|
||||
enhanced
|
||||
showScrollbar={true}
|
||||
>
|
||||
<View className="districtOptionsList">
|
||||
{districtOptions.map((item) => {
|
||||
const active = districtValue === item?.value;
|
||||
return (
|
||||
<View
|
||||
key={item.value}
|
||||
className={`districtItem ${active && "active"}`}
|
||||
onClick={() => handleChange(districtName, item.value, 0)}
|
||||
>
|
||||
<View className="districtItemText">{item?.label}</View>
|
||||
{active && (
|
||||
<View>
|
||||
<Image
|
||||
className="itemIcon"
|
||||
src={img.ICON_MENU_ITEM_SELECTED}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</ScrollView>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
title={quickTitle}
|
||||
ref={quickRef}
|
||||
defaultValue={quickValue}
|
||||
>
|
||||
<View className="quickOptionsWrapper">
|
||||
{quickOptions.map((item) => {
|
||||
const active = quickValue === item?.value;
|
||||
return (
|
||||
<View
|
||||
key={item.value}
|
||||
className={`quickItem ${active && "active"}`}
|
||||
onClick={() => handleChange(quickName, item.value, 1)}
|
||||
>
|
||||
<View>{item?.label}</View>
|
||||
{active && (
|
||||
<View>
|
||||
<Image
|
||||
className="itemIcon"
|
||||
src={img.ICON_MENU_ITEM_SELECTED}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default DistanceQuickFilterV2;
|
||||
|
||||
Reference in New Issue
Block a user