一、場景實現
1、左側為查詢條件,查詢條件為樹和多選。點擊查詢條件在右上方顯示搜索條件的內容,右上方查詢條件 tag 刪除后,左側條件也對應刪除。
2、樹結構:默認第一層下所有節點都展開。
1、頁面效果圖
2、查詢效果圖
二、前端代碼
1、Tree 組件
(1)實現方法一
import React, { useEffect, useState } from "react";
import { Tree, TreeProps } from "antd";interface FilterTreeProps {treeData: TreeNode[];value: string[];onChange: (values: string[]) => void;defaultExpandFirst?: boolean; // 是否默認展開第一條數據及其子級
}interface TreeNode {productLineCode: string;productLineName: string;children?: TreeNode[];
}interface SelectedNode {code: string;label: string;path: string;
}const { TreeNode } = Tree;const FilterTree = ({treeData,value,onChange,defaultExpandFirst = true,
}: FilterTreeProps) => {const [expandedKeys, setExpandedKeys] = useState<string[]>([]);/*** 遞歸獲取節點及其所有子節點的key*/const getAllKeys = (node: TreeNode): string[] => {if (!node) return [];const keys = [node.productLineCode]; // 當前節點// 遞歸添加子節點if (node.children && node.children.length > 0) {node.children.forEach((child: TreeNode) => {keys.push(...getAllKeys(child));});}return keys;};useEffect(() => {if (defaultExpandFirst && treeData.length > 0) {setExpandedKeys(getAllKeys(treeData[0]));}}, [treeData, defaultExpandFirst]);/** 收縮 */const onExpand = (expandedKeys: string[]) => {setExpandedKeys(expandedKeys as string[]);};/** 勾選 */const onCheck: TreeProps["onCheck"] = (selectKeys) => {const findParentNodes = (data: TreeNode[] = [],path: SelectedNode[] = []): SelectedNode[] => {return data.flatMap((item) => {const currentPath = [...path];const isParent = item.children && item.children.length > 0;if (isParent) {// 檢查是否所有子節點都被選中const allChildrenSelected = item.children?.every((child) =>(selectKeys as string[]).includes(child.productLineCode));if (allChildrenSelected) {return [...currentPath,{code: item.productLineCode,label: item.productLineName,path: item.path,},];}// 遞歸處理子節點return findParentNodes(item.children, currentPath);}// 葉子節點處理return (selectKeys as string[]).includes(item.productLineCode)? [...currentPath,{code: item.productLineCode,label: item.productLineName,path: item.path,},]: [];});};const selectedNodes = findParentNodes(treeData);// console.log("勾選數據", selectedNodes);const params = {tags: selectedNodes, // 要顯示的tags標簽checkedValues: selectKeys, // 勾選的數據};onChange(params);};// 渲染樹節點const renderTreeNodes = (data: TreeNode[]) => {return data.map((item) => {if (item.children && item.children.length > 0) {return (<TreeNodetitle={item.productLineName}key={item.productLineCode}style={{ lineHeight: "36px" }}>{renderTreeNodes(item.children)}</TreeNode>);}return (<TreeNode title={item.productLineName} key={item.productLineCode} />);});};return (<TreecheckablecheckedKeys={value}expandedKeys={expandedKeys}onCheck={onCheck}onExpand={onExpand}>{renderTreeNodes(treeData)}</Tree>);
};export default FilterTree;
(2)實現方法二
2、CheckBox 組件
import React from "react";
import { Checkbox } from "antd";interface FilterCheckboxProps {options: any[];value: string[];onChange: (values: string[]) => void;className?: string;itemClassName?: string;
}const FilterCheckbox = ({options,value,onChange,className,itemClassName,
}: FilterCheckboxProps) => {return (<Checkbox.Group className={className} value={value} onChange={onChange}>{options?.map((val: any) => (<Checkbox key={val.value} value={val.value} className={itemClassName}>{val.meaning}</Checkbox>))}</Checkbox.Group>);
};export default FilterCheckbox;
3、Search 組件
import React, { useEffect, useMemo, useState } from "react";
import Image from "next/image";
import { Input } from "antd";
import SearchIcon from "@/public/course/search.svg";
import styles from "../../index.module.scss";
import { getModelConfig } from "@/utils";
import { getPortalConfig } from "@/api/home";interface BannerProps {onSearch?: (goodsName: string) => void;
}const Banner: React.FC<BannerProps> = ({ onSearch }) => {const [searchValue, setSearchValue] = useState(""); // 搜索值const [configData, setConfigData] = useState([]); // 配置數據詳情// banner圖const bgLogo = useMemo(() => {// 獲取頂級const father = getModelConfig("courseBackgroundImage", configData);// 獲取子級const data = getModelConfig("courseBackgroundImage.picture",father?.childrenList);return data?.componentPicture || "";}, [configData]);/*** @description 獲取配置列表*/const fetchConfig = async () => {const res = await getPortalConfig("zh_CN");if (res?.code === 200) {// 獲取配置列表,過濾出'門戶配置'下的組件const result = (res.data || []).filter((item: { componentPlate: string }) => item.componentPlate === "LESSONS");setConfigData(result);}};useEffect(() => {const fetchDataSequence = async () => {await fetchConfig();};fetchDataSequence();}, []);const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {const value = e.target.value;setSearchValue(value);// 當輸入被清空時立即觸發搜索if (value === "") {handleSearch();}};const handlePressEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {if (e.key === "Enter") {handleSearch();}};const handleSearch = () => {if (onSearch) {onSearch(searchValue.trim());}};return (<div className={styles.courseDetail_banner}>{/* 背景圖 */}<Imagesrc={bgLogo}alt=""width={1920}height={280}style={{width: "100%",height: "280px",objectFit: "cover",}}unoptimizedloading="lazy"className={styles.courseDetail_banner_pic}/>{/* 內容 */}<div className={styles.courseDetail_banner_box}><div className={styles.courseDetail_banner_box_title}>匯川技術學堂</div>{/* 搜索 */}<div className={styles.courseDetail_banner_box_search}><Inputvariant="underlined"placeholder="你想了解..."value={searchValue}onChange={handleChange}onPressEnter={handlePressEnter}className={styles.courseDetail_banner_box_search_input}/><Imagesrc={SearchIcon}alt=""width={27}height={27}unoptimizedloading="lazy"onClick={handleSearch}/></div></div></div>);
};export default Banner;
4、Card組件
import React, { useEffect, useMemo, useState } from "react";
import Image from "next/image";
import styles from "../../index.module.scss";
import TypeIcon from "@/public/course/typeIcon.png";
import { getQueryValue } from "@/api/home";
import { objectToArr } from "@/utils";
import { filter } from "../../store";
import { DetailCardProps, OptionType } from "@/type/course";const Card: React.FC<{ list: DetailCardProps[] }> = ({ list = [] }) => {const [stages, setStages] = useState<OptionType[]>([]);useEffect(() => {// 階段:值集const fetchStagesType = async () => {try {const res = await getQueryValue("INO_TAI_COURSE_STAGE");const result = objectToArr(res.data || []);setStages(result);} catch (error) {console.error("Error fetching title:", error);}};fetchStagesType();}, []);const stagesType = useMemo(() => stages, [stages]); // 階段return (<>{list.map((item: DetailCardProps, index: number) => {return (<divkey={index}className={styles.courseDetail_content_right_card_item}>{/* 封面 */}<div className={styles.courseDetail_content_right_card_item_pic}><Imagesrc={item?.ossImgUrlCompressed}alt=""width={292}height={164}unoptimizedloading="lazy"className={styles.courseDetail_content_right_card_item_pic}/></div><div className={styles.courseDetail_content_right_card_item_box}><divclassName={styles.courseDetail_content_right_card_item_box_title}>{item.goodsName}</div><divclassName={styles.courseDetail_content_right_card_item_box_info}>{/* 階段 */}<divclassName={styles.courseDetail_content_right_card_item_box_info_left}><Imagesrc={TypeIcon}alt=""width={13}height={13}unoptimizedloading="lazy"/>{filter(stagesType, item.stage)}</div>{/* <div>iFA開發者</div> */}</div>{/* 價格:如果為0的話顯示'免費' */}<divclassName={styles.courseDetail_content_right_card_item_box_type}style={{ marginTop: "28px" }}>{item.priceLow === 0 ? "免費" : `¥${item.priceLow}`}</div></div></div>);})}</>);
};
export default Card;
5、完整代碼(非最新版,待更新)
"use client";import React, { useEffect, useState } from "react";
import Image from "next/image";
import { CloseOutlined } from "@ant-design/icons";
import Layout from "@/components/Layout/index";
import Card from "./components/Card/main";
import Search from "./components/Search/main";
import FilterCheckbox from "./components/CheckBox/main";
import FilterTree from "./components/Tree/main";
import styles from "./index.module.scss";
import { objectToArr } from "@/utils";
import { getQueryValue } from "@/api/home";
import DeleteIcon from "@/public/course/delete.png";
import { Pagination } from "antd";
import { getBusinessConfig, postCourseList } from "@/api/course";
import {ChooseItem,CourseItem,DetailCardProps,OptionType,PaginationState,SearchItem,
} from "@/type/course";
import { searchParams } from "./store";
import EmptyIcon from "@/public/empty.png";const CourseDetail: React.FC = () => {const [table, setTable] = useState<DetailCardProps[]>([]); // table列表const [goodsName, setGoodsName] = useState<string>(""); // 課程名稱const [chooseList, setChooseList] = useState<ChooseItem[]>([]); // 新增:已選條件列表// 存儲各類型的選中值const [selectedValues, setSelectedValues] = useState<Record<string, string[]>>({});// 分頁狀態const [pagination, setPagination] = useState<PaginationState>({currentPage: 1,pageSize: 12,total: 0,});// 搜索條件const [searchList, setSearchList] = useState<SearchItem[]>([{ name: "業務模塊", value: "modules", query: "businessModel", list: [] },{ name: "階段", value: "stages", query: "INO_TAI_COURSE_STAGE", list: [] },{name: "內容類型",value: "contentTypes",query: "INO_TAI_COURSE_TYPE",list: [],},{name: "價格",value: "priceRanges",query: "INO_TAI_COURSE_PRICE",list: [],},{name: "課程類型",value: "courseTypes",query: "INO_TAI_COURSE_SUBJECT_TYPE",list: [],},
]);/*** 獲取課程列表* @param page - 當前頁碼(1-based)* @param pageSize - 每頁大小*/const fetchData = async (page = 1, pageSize = 12) => {// 必傳項const requireParams = {sellType: "independently", // 付費類型(C端只顯示:獨立售賣)isStopSell: "N", // 是否停售(C端顯示0)isForbid: "N", // 商品是否被封禁(C端顯示0)};// 可選項const params: CourseItem = {page: page - 1, // 轉換為0-basedsize: pageSize,...(goodsName !== undefined ? { goodsName } : {}), // 課程名稱};// 添加已選條件到查詢參數const otherParams = {...Object.entries(selectedValues).reduce((acc, [type, values]) => {if (values && values.length > 0) {acc[type] = values; // 多個值用數組}return acc;}, {} as Record<string, string[]>),};// console.log("otherParams", otherParams);const resultParams = {// ...requireParams,...params,...otherParams,};// console.log("查詢條件", resultParams);try {const res = await postCourseList(resultParams);if (res.data) {setTable(res.data.content || []);// 更新分頁狀態(總記錄數、當前頁、每頁大小)setPagination({total: res.data.totalElements || 0, // 總記錄數currentPage: page, // 前端顯示的當前頁(1-based)pageSize: res.data.size || 12, // 后端返回的每頁大小});}} catch (error) {console.error("獲取課程列表失敗:", error);}};const handleTreeChange = (type: string, checkedValues: any) => {console.log("樹結構處理", type, checkedValues);};/*** 處理多選框變化* @param type - 條件類型* @param checkedValues - 選中的值數組*/const handleCheckboxChange = (type: string, checkedValues: any[]) => {// 更新選中值狀態setSelectedValues((prev) => ({...prev,[type]: checkedValues,}));// 獲取當前條件的配置const currentCondition = searchList.find((item) => item.value === type);if (!currentCondition) return;// 更新已選條件列表setChooseList((prev) => {// 先移除當前類型的所有已選條件const filtered = prev.filter((item) => item.type !== type);// 如果有選中的值,則添加新的條件if (checkedValues.length > 0) {const newItems = checkedValues.map((value) => {const option = currentCondition.list.find((item: any) => item.value === value);return {type,value,meaning: option?.meaning || value,};});return [...filtered, ...newItems];}return filtered;});};/*** 移除單個已選條件* @param type - 條件類型* @param value - 要移除的值*/const removeCondition = (type: string, value: string) => {setSelectedValues((prev) => ({...prev,[type]: prev[type]?.filter((v) => v !== value) || [],}));setChooseList((prev) =>prev.filter((item) => !(item.type === type && item.value === value)));};/*** 清空所有已選條件*/const clearAllConditions = () => {setSelectedValues({});setChooseList([]);};/*** 加載篩選條件數據*/useEffect(() => {const fetchOptions = async () => {// 1. 處理常規接口請求const normalItems = searchList.filter((item) => item.query && item.query !== "businessModel");const normalPromises = normalItems.map((item: any) =>getQueryValue(item.query).then((res) => ({key: item.value,data: objectToArr(res.data || []).sort((a, b) => a.orderSeq - b.orderSeq),})));// 2. 處理特殊接口請求const businessItem = searchList.find((item) => item.query === "businessModel");const businessPromise = businessItem? getBusinessConfig().then((res) => ({key: businessItem.value,data: objectToArr(res.data || []).sort((a, b) => a.orderSeq - b.orderSeq),})): Promise.resolve({ key: "", data: [] }); // 若無businessModel,返回空結果try {const [normalResults, businessResult] = await Promise.all([Promise.all(normalPromises),businessPromise,]);// 合并結果const resultMap = [...normalResults, businessResult].reduce((map, result) => {if (result.key) map[result.key] = result.data;return map;},{} as Record<string, OptionType[]>);setSearchList((prev) =>prev.map((item) => ({...item,list: resultMap[item.value] || [],})));} catch (error) {console.error("加載篩選條件失敗:", error);}};fetchOptions();}, []);// 初始數據加載useEffect(() => {(async function () {await fetchData();})();}, [goodsName, chooseList]);return (<><Layout><div className={styles.courseDetail}><SearchonSearch={(searchText) => {setGoodsName(searchText);}}/><div className={styles.courseDetail_content}>{/* 左側篩選欄 */}<div className={styles.courseDetail_content_left}><div className={styles.courseDetail_content_left_title}>篩選</div>{/* 查詢條件 */}{searchList.map((item) => (<React.Fragment key={item.value}><divclassName={`${styles.courseDetail_content_left_title} ${styles.courseDetail_content_left_subTitle}`}>{item.name}</div>{/* 業務模塊使用Tree組件,其他使用Checkbox組件 */}{item.query === "businessModel" ? (<><FilterTreetreeData={item.list || []}value={selectedValues[item.value] || []}onChange={(values) =>handleCheckboxChange(item.value, values)}/></>) : (<><FilterCheckboxoptions={item.list || []}value={selectedValues[item.value] || []}onChange={(values) =>handleCheckboxChange(item.value, values)}className={styles.courseDetail_content_left_checkbox}itemClassName={styles.courseDetail_content_left_checkbox_item}/></>)}</React.Fragment>))}</div>{/* 右側 */}<div className={styles.courseDetail_content_right}><div className={styles.courseDetail_content_right_main}>{/* 查詢條件:標簽 */}<div className={styles.courseDetail_content_right_search}><divclassName={styles.courseDetail_content_right_search_left}>{/* 展示已選條件 */}{chooseList.map((item) => (<divkey={`${item.type}-${item.value}`}className={styles.courseDetail_content_right_search_left_tags}>{item.meaning}<CloseOutlinedstyle={{fontSize: "10px",color: "#8F8F8F",marginLeft: 4,}}onClick={() => removeCondition(item.type, item.value)}/></div>))}{/* 清空所有按鈕 */}{chooseList.length > 0 && (<divclassName={styles.courseDetail_content_right_search_left_tags}onClick={clearAllConditions}><Imagesrc={DeleteIcon}alt="清空"width={10}height={11}unoptimizedloading="lazy"/><span style={{ marginLeft: 4 }}>清空</span></div>)}</div><divclassName={styles.courseDetail_content_right_search_right}>找到全部相關內容:{pagination.total}</div></div>{/* 課程卡片區域 */}{table?.length > 0 ? (<div className={styles.courseDetail_content_right_card}><Card list={table} /></div>) : (<div className={styles.courseDetail_empty}><Imagesrc={EmptyIcon}alt="empty"width={165}height={115}unoptimizedloading="lazy"/><div className={styles.courseDetail_empty_desc}>暫無課程,看看其他的吧~</div></div>)}</div>{/* 分頁 */}<div className={styles.courseDetail_content_right_page}><Paginationtotal={pagination.total || 0}current={pagination.currentPage} // 前端顯示的當前頁(1-based)pageSize={pagination.pageSize}showSizeChangershowQuickJumpershowTotal={(total) => `共 ${total} 條記錄`}onChange={(page) => fetchData(page)} // 頁碼變更時(1-based)onShowSizeChange={(currentPage, pageSize) => fetchData(currentPage, pageSize) // 切換每頁大小后從第一頁開始}/></div></div></div></div></Layout></>);
};
export default CourseDetail;
5、css樣式
// 內容一行,超出顯示省略號
@mixin ellipsis-one-lines {overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 1;-webkit-box-orient: vertical;
}// 內容兩行,超出顯示省略號
@mixin ellipsis-two-lines {overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;
}.courseDetail {font-family: "阿里巴巴普惠體";width: 100%;background: #ffffff;&_banner {width: 100%;height: 280px;position: relative;display: flex;&_pic {position: absolute;inset: 0;}&_box {width: 1226px;margin: 0 auto;position: relative;display: flex;flex-direction: column;align-items: center;justify-content: center;&_title {color: #0c1a34;font-size: 48px;line-height: 70px;font-weight: 700;margin-bottom: 24px;}// 搜索&_search {width: 960px;height: 56px;border-radius: 4px;box-shadow: 0px 5px 12px 4px rgba(0, 0, 0, 0.05),0px 3px 6px 0px rgba(0, 0, 0, 0.08),0px 1px 2px -2px rgba(0, 0, 0, 0.12);background: white;padding: 12px 24px 12px 12px;box-sizing: border-box;display: flex;gap: 8px;&_input {flex: 1;border: none;}img {&:hover {cursor: pointer;}}}}}&_content {width: 1226px;margin: 0 auto;padding: 84px 0 114px 0;display: flex;flex-direction: row;gap: 24px;&_left {width: 295px;&_title {font-size: 18px;font-weight: bold;color: #222222;border-bottom: 1px solid #dee3e8;line-height: 48px;padding: 0 16px;letter-spacing: 0px;}&_subTitle {font-size: 16px;}// 樹// 多選框&_checkbox {display: flex;flex-direction: column;padding: 0 16px;box-sizing: border-box;// gap: 8px;&_item {line-height: 40px;}}}&_right {flex: 1;// 查詢條件:標簽&_search {display: flex;justify-content: space-between;gap: 16px;&_left {display: flex;flex-wrap: wrap;gap: 8px;&_tags {color: #222222;font-size: 12px;line-height: 24px;padding: 0 8px;background: #f5f5f5;display: flex;align-items: center;gap: 4px;}}&_right {color: #9099ac;font-weight: 400;font-size: 14px;line-height: 20px;}}// 卡片&_card {width: 100%;display: flex;flex-wrap: wrap;gap: 16px;padding-top: 15px;margin-top: 15px;border-top: 1px solid #dee3e8;&_item {width: calc((100% - 32px) / 3);flex-basis: calc((100% - 32px) / 3);border-radius: 15px;background: white;box-shadow: 0px 1.67px 16.7px 0px rgba(0, 0, 0, 0.1);&:hover {cursor: pointer;box-shadow: 0px 18px 32px 16px rgba(0, 0, 0, 0.03),0px 12px 24px 0px rgba(0, 0, 0, 0.05),0px 8px 16px -8px rgba(0, 0, 0, 0.06);}&_pic {width: 292px;height: 164px;border-radius: 11.01px 11.01px 0px 0px;}&_box {padding: 12px;&_title {color: #222222;font-size: 16px;font-weight: bold;line-height: 24px;height: 48px;@include ellipsis-two-lines;}&_info {display: flex;align-items: center;justify-content: space-between;font-size: 12px;color: #8c8c8c;line-height: 20px;margin-top: 4px;&_left {display: flex;align-items: center;gap: 4px;}}&_type {color: #222222;font-size: 16px;font-weight: bold;line-height: 24px;margin-bottom: 8px;}}}}// 分頁&_page {display: flex;justify-content: flex-end;margin-top: 32px;}}}// 空數據&_empty {width: 100%;padding: 90px 0;box-sizing: border-box;display: flex;flex-direction: column;align-items: center;&_desc {font-size: 14px;color: #788295;line-height: 19px;margin-top: 27px;}}
}
三、后端返回JSON
[{"creationDate": "2025-05-19 16:18:49","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:26:17","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGHZSWroRqcOPk1MTPLSV5VE=","id": 199,"productLineCode": "068607","productLineName": "karla","productLineLevel": 1,"productLineSort": 1,"path": "068607","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:18:56","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:29","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqEtpLwGY7lxCOrle/21VpcI=","id": 200,"productLineCode": "788763","productLineName": "demo","productLineLevel": 2,"productLineSort": 1,"parentCode": "068607","path": "068607/788763","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:19:08","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:20","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqJgDxrQ9Pf5/U+hdOUL/DYQ=","id": 202,"productLineCode": "185799","productLineName": "小朋友","productLineLevel": 3,"productLineSort": 1,"parentCode": "788763","path": "068607/788763/185799","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:19:45","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:19:45","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqNISAJXY9gOeNcmlflkR5po=","id": 205,"productLineCode": "220944","productLineName": "小寶寶","productLineLevel": 4,"productLineSort": 1,"parentCode": "185799","path": "068607/788763/185799/220944","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-19 16:19:22","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:35","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqPJvYv6jT8jvuZuYoqlaew4=","id": 203,"productLineCode": "385121","productLineName": "家庭成員","productLineLevel": 3,"productLineSort": 2,"parentCode": "788763","path": "068607/788763/385121","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:19:55","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:19:55","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqKcdVFMAFlWjlvwB2owvxf8=","id": 206,"productLineCode": "507563","productLineName": "爸","productLineLevel": 4,"productLineSort": 1,"parentCode": "385121","path": "068607/788763/385121/507563","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:20:02","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:02","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqB0JnEiN7gP29YaDjqF2sys=","id": 207,"productLineCode": "079729","productLineName": "媽","productLineLevel": 4,"productLineSort": 1,"parentCode": "385121","path": "068607/788763/385121/079729","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:20:13","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:13","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqHCRKHEIQQwrjFTkTwNdkJw=","id": 208,"productLineCode": "082499","productLineName": "爺爺","productLineLevel": 4,"productLineSort": 3,"parentCode": "385121","path": "068607/788763/385121/082499","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:20:22","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:22","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqHZSWroRqcOPk1MTPLSV5VE=","id": 209,"productLineCode": "334232","productLineName": "奶奶","productLineLevel": 4,"productLineSort": 4,"parentCode": "385121","path": "068607/788763/385121/334232","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-19 16:19:33","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:51","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqH862sHidVIsCLoA5mqI2Zc=","id": 204,"productLineCode": "410177","productLineName": "水果","productLineLevel": 3,"productLineSort": 3,"parentCode": "788763","path": "068607/788763/410177","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:20:44","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:44","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhUtpLwGY7lxCOrle/21VpcI=","id": 210,"productLineCode": "514006","productLineName": "蘋果","productLineLevel": 4,"productLineSort": 1,"parentCode": "410177","path": "068607/788763/410177/514006","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:20:59","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:20:59","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhXXzRoERUkkc2i2OTyqu/QI=","id": 211,"productLineCode": "350970","productLineName": "菠蘿","productLineLevel": 4,"productLineSort": 2,"parentCode": "410177","path": "068607/788763/410177/350970","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:21:08","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:08","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhZgDxrQ9Pf5/U+hdOUL/DYQ=","id": 212,"productLineCode": "367594","productLineName": "香蕉","productLineLevel": 4,"productLineSort": 5,"parentCode": "410177","path": "068607/788763/410177/367594","tenantId": 0,"matched": false,"children": []}]}]},{"creationDate": "2025-05-19 16:19:02","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:38","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQM8g/tDo7jW7Ku6QInBMZqHXzRoERUkkc2i2OTyqu/QI=","id": 201,"productLineCode": "115960","productLineName": "汽車","productLineLevel": 2,"productLineSort": 1,"parentCode": "068607","path": "068607/115960","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:21:45","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:45","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhfJvYv6jT8jvuZuYoqlaew4=","id": 213,"productLineCode": "284494","productLineName": "國產","productLineLevel": 3,"productLineSort": 1,"parentCode": "115960","path": "068607/115960/284494","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:22:19","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:19","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhR0JnEiN7gP29YaDjqF2sys=","id": 217,"productLineCode": "569856","productLineName": "長城","productLineLevel": 4,"productLineSort": 1,"parentCode": "284494","path": "068607/115960/284494/569856","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-19 16:21:52","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:21:52","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhX862sHidVIsCLoA5mqI2Zc=","id": 214,"productLineCode": "564860","productLineName": "國外","productLineLevel": 3,"productLineSort": 2,"parentCode": "115960","path": "068607/115960/564860","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:22:05","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:05","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhdISAJXY9gOeNcmlflkR5po=","id": 215,"productLineCode": "326208","productLineName": "沃爾沃","productLineLevel": 4,"productLineSort": 1,"parentCode": "564860","path": "068607/115960/564860/326208","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:22:12","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:12","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhacdVFMAFlWjlvwB2owvxf8=","id": 216,"productLineCode": "617395","productLineName": "寶馬","productLineLevel": 4,"productLineSort": 3,"parentCode": "564860","path": "068607/115960/564860/617395","tenantId": 0,"matched": false,"children": []}]}]},{"creationDate": "2025-05-19 16:22:27","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:27","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhXCRKHEIQQwrjFTkTwNdkJw=","id": 218,"productLineCode": "153878","productLineName": "你好呀","productLineLevel": 2,"productLineSort": 2,"parentCode": "068607","path": "068607/153878","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-09 18:40:11","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:58","lastUpdatedBy": 13937,"objectVersionNumber": 3,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQwHGwJ4ahmn50+LyPk0m4FH862sHidVIsCLoA5mqI2Zc=","id": 184,"productLineCode": "395902","productLineName": "課程","productLineLevel": 1,"productLineSort": 2,"path": "395902","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:23:14","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:23:14","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQQltZlT7MnufgSfHOIXwHZ5gDxrQ9Pf5/U+hdOUL/DYQ=","id": 222,"productLineCode": "604957","productLineName": "數學","productLineLevel": 2,"productLineSort": 2,"parentCode": "395902","path": "395902/604957","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:23:09","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:23:09","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQQltZlT7MnufgSfHOIXwHZ3XzRoERUkkc2i2OTyqu/QI=","id": 221,"productLineCode": "104885","productLineName": "語文","productLineLevel": 2,"productLineSort": 2,"parentCode": "395902","path": "395902/104885","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-13 14:24:05","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:38","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGB0JnEiN7gP29YaDjqF2sys=","id": 197,"productLineCode": "422225","productLineName": "國家","productLineLevel": 1,"productLineSort": 3,"path": "422225","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-19 16:22:43","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:43","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQpUihnaRAYlwAQ/WFvF1xhXZSWroRqcOPk1MTPLSV5VE=","id": 219,"productLineCode": "170090","productLineName": "中國","productLineLevel": 2,"productLineSort": 1,"parentCode": "422225","path": "422225/170090","tenantId": 0,"matched": false,"children": []},{"creationDate": "2025-05-19 16:22:49","createdBy": 13937,"lastUpdateDate": "2025-05-19 16:22:49","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQQltZlT7MnufgSfHOIXwHZ0tpLwGY7lxCOrle/21VpcI=","id": 220,"productLineCode": "833410","productLineName": "美國","productLineLevel": 2,"productLineSort": 2,"parentCode": "422225","path": "422225/833410","tenantId": 0,"matched": false,"children": []}]},{"creationDate": "2025-05-09 18:48:32","createdBy": 13937,"lastUpdateDate": "2025-05-13 14:24:09","lastUpdatedBy": 13937,"objectVersionNumber": 2,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGEtpLwGY7lxCOrle/21VpcI=","id": 190,"productLineCode": "028284","productLineName": "4","productLineLevel": 1,"productLineSort": 4,"path": "028284","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-09 18:48:38","createdBy": 13937,"lastUpdateDate": "2025-05-09 18:48:38","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGHXzRoERUkkc2i2OTyqu/QI=","id": 191,"productLineCode": "033804","productLineName": "3","productLineLevel": 2,"productLineSort": 3,"parentCode": "028284","path": "028284/033804","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-09 18:48:46","createdBy": 13937,"lastUpdateDate": "2025-05-09 18:48:46","lastUpdatedBy": 13937,"objectVersionNumber": 1,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGJgDxrQ9Pf5/U+hdOUL/DYQ=","id": 192,"productLineCode": "062078","productLineName": "2","productLineLevel": 3,"productLineSort": 2,"parentCode": "033804","path": "028284/033804/062078","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-09 18:48:51","createdBy": 13937,"lastUpdateDate": "2025-05-09 18:52:35","lastUpdatedBy": 13937,"objectVersionNumber": 3,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGPJvYv6jT8jvuZuYoqlaew4=","id": 193,"productLineCode": "065263","productLineName": "綁定課程四級","productLineLevel": 4,"productLineSort": 1,"parentCode": "062078","path": "028284/033804/062078/065263","tenantId": 0,"matched": false,"children": [{"creationDate": "2025-05-09 18:49:01","createdBy": 13937,"lastUpdateDate": "2025-05-09 18:52:49","lastUpdatedBy": 13937,"objectVersionNumber": 3,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQTEBt6f+NtV7DQiMXR9/fGH862sHidVIsCLoA5mqI2Zc=","id": 194,"productLineCode": "708130","productLineName": "綁定課程五級","productLineLevel": 5,"productLineSort": 1,"parentCode": "065263","path": "028284/033804/062078/065263/708130","tenantId": 0,"matched": false,"children": []}]}]}]}]},{"creationDate": "2025-05-09 14:36:42","createdBy": 41344,"lastUpdateDate": "2025-05-19 16:26:21","lastUpdatedBy": 13937,"objectVersionNumber": 5,"_token": "vF3QEsJKNjXBOytbocJJJ029kVE6+U/Z7BkBLEAPhEwFuzOtisxAqcHVn8vKcuFQjdq6zFI0rq1LFywvSox31tISAJXY9gOeNcmlflkR5po=","id": 175,"productLineCode": "876514","productLineName": "公司","productLineLevel": 1,"productLineSort": 6,"path": "876514","tenantId": 0,"matched": false,"children": []}
]
要實現:默認第一條數據下所有的子集都展開。
目前只實現了:
應實現效果:
默認第一條數據的所有子級都顯示代碼:
import React, { useState } from "react";
import { Tree, TreeProps } from "antd";interface FilterTreeProps {treeData: any[];value: string[];onChange: (values: string[]) => void;defaultExpandFirst?: boolean; // 是否默認展開第一條數據及其子級
}const { TreeNode } = Tree;const FilterTree = ({treeData,value,onChange,defaultExpandFirst = true,
}: FilterTreeProps) => {const [expandedKeys, setExpandedKeys] = useState<string[]>([]);/** 遞歸獲取節點及其所有子節點的key */const getAllKeys = (node: any): string[] => {if (!node) return [];const keys = [node.productLineCode]; // 當前節點// 遞歸添加子節點if (node.children && node.children.length > 0) {node.children.forEach((child: any) => {keys.push(...getAllKeys(child));});}return keys;};React.useEffect(() => {if (defaultExpandFirst && treeData.length > 0) {setExpandedKeys(getAllKeys(treeData[0]));}}, [treeData, defaultExpandFirst]);const onCheck: TreeProps["onCheck"] = (checkedKeys, info) => {// console.log("onCheck", checkedKeys, info);// 這里的數據,勾選的數據都添加checkStatus: true的狀態,打印出treeDataconsole.log("勾選的數據", info.checkedNodes);onChange(checkedKeys);};const onExpand = (expandedKeys: string[]) => {setExpandedKeys(expandedKeys);};// 渲染樹節點const renderTreeNodes = (data: any[]) => {return data.map((item) => {if (item.children && item.children.length > 0) {return (<TreeNodetitle={item.productLineName}key={item.productLineCode}style={{ lineHeight: "36px" }}>{renderTreeNodes(item.children)}</TreeNode>);}return (<TreeNode title={item.productLineName} key={item.productLineCode} />);});};return (<TreecheckablecheckedKeys={value}expandedKeys={expandedKeys}onCheck={onCheck}onExpand={onExpand}>{renderTreeNodes(treeData)}</Tree>);
};export default FilterTree;
打印出來沒問題,所有代碼:
"use client";import React, { useEffect, useState } from "react";
import Image from "next/image";
import { CloseOutlined } from "@ant-design/icons";
import Layout from "@/components/Layout/index";
import Card from "./components/Card/main";
import Search from "./components/Search/main";
import FilterCheckbox from "./components/CheckBox/main";
import FilterTree from "./components/Tree/main";
import styles from "./index.module.scss";
import { objectToArr } from "@/utils";
import { getQueryValue } from "@/api/home";
import DeleteIcon from "@/public/course/delete.png";
import { Pagination } from "antd";
import { getBusinessConfig, postCourseList } from "@/api/course";
import {ChooseItem,CourseItem,DetailCardProps,OptionType,PaginationState,SearchItem,
} from "@/type/course";const CourseDetail: React.FC = () => {const [table, setTable] = useState<DetailCardProps[]>([]); // table列表const [goodsName, setGoodsName] = useState<string>(""); // 課程名稱const [chooseList, setChooseList] = useState<ChooseItem[]>([]); // 新增:已選條件列表const [selectedValues, setSelectedValues] = useState<Record<string, string[]>>({}); // 存儲各類型的選中值// 分頁狀態const [pagination, setPagination] = useState<PaginationState>({currentPage: 1,pageSize: 12,total: 0,});// 搜索條件const [searchList, setSearchList] = useState<SearchItem[]>([{ name: "業務模塊", value: "modules", query: "businessModel", list: [] },{ name: "階段", value: "stages", query: "INO_TAI_COURSE_STAGE", list: [] },{name: "內容類型",value: "contentTypes",query: "INO_TAI_COURSE_TYPE",list: [],},{name: "價格",value: "priceRanges",query: "INO_TAI_COURSE_PRICE",list: [],},{name: "課程類型",value: "courseTypes",query: "INO_TAI_COURSE_SUBJECT_TYPE",list: [],},]);/*** 獲取課程列表* @param page - 當前頁碼(1-based)* @param pageSize - 每頁大小*/const fetchData = async (page = 1, pageSize = 12) => {// 必傳項const requireParams = {sellType: "independently", // 付費類型(C端只顯示:獨立售賣)isStopSell: "N", // 是否停售(C端顯示0)isForbid: "N", // 商品是否被封禁(C端顯示0)};// 可選項const params: CourseItem = {page: page - 1, // 轉換為0-basedsize: pageSize,...(goodsName !== undefined ? { goodsName } : {}), // 課程名稱};// 添加已選條件到查詢參數const otherParams = {...Object.entries(selectedValues).reduce((acc, [type, values]) => {if (values && values.length > 0) {acc[type] = values; // 多個值用數組}return acc;}, {} as Record<string, string[]>),};console.log("otherParams", otherParams);// console.log("查詢條件", params);try {const res = await postCourseList({// ...requireParams,...params,// ...otherParams,});// console.log("????", res.data);if (res.data) {setTable(res.data.content || []);// 更新分頁狀態(總記錄數、當前頁、每頁大小)setPagination({total: res.data.totalElements || 0, // 總記錄數currentPage: page, // 前端顯示的當前頁(1-based)pageSize: res.data.size || 12, // 后端返回的每頁大小});}} catch (error) {console.error("獲取課程列表失敗:", error);}};/*** 處理多選框變化* @param type - 條件類型* @param checkedValues - 選中的值數組*/const handleCheckboxChange = (type: string, checkedValues: any[]) => {// 更新選中值狀態setSelectedValues((prev) => ({...prev,[type]: checkedValues,}));// 獲取當前條件的配置const currentCondition = searchList.find((item) => item.value === type);if (!currentCondition) return;// 更新已選條件列表setChooseList((prev) => {// 先移除當前類型的所有已選條件const filtered = prev.filter((item) => item.type !== type);// 如果有選中的值,則添加新的條件if (checkedValues.length > 0) {const newItems = checkedValues.map((value) => {const option = currentCondition.list.find((item: any) => item.value === value);return {type,value,meaning: option?.meaning || value,};});return [...filtered, ...newItems];}return filtered;});};/*** 移除單個已選條件* @param type - 條件類型* @param value - 要移除的值*/const removeCondition = (type: string, value: string) => {setSelectedValues((prev) => ({...prev,[type]: prev[type]?.filter((v) => v !== value) || [],}));setChooseList((prev) =>prev.filter((item) => !(item.type === type && item.value === value)));};/*** 清空所有已選條件*/const clearAllConditions = () => {setSelectedValues({});setChooseList([]);};/*** 加載篩選條件數據*/useEffect(() => {const fetchOptions = async () => {// 1. 處理常規接口請求const normalItems = searchList.filter((item) => item.query && item.query !== "businessModel");const normalPromises = normalItems.map((item: any) =>getQueryValue(item.query).then((res) => ({key: item.value,data: objectToArr(res.data || []).sort((a, b) => a.orderSeq - b.orderSeq),})));// 2. 處理特殊接口請求const businessItem = searchList.find((item) => item.query === "businessModel");const businessPromise = businessItem? getBusinessConfig().then((res) => ({key: businessItem.value,data: objectToArr(res.data || []).sort((a, b) => a.orderSeq - b.orderSeq),})): Promise.resolve({ key: "", data: [] }); // 若無businessModel,返回空結果try {const [normalResults, businessResult] = await Promise.all([Promise.all(normalPromises),businessPromise,]);// 合并結果const resultMap = [...normalResults, businessResult].reduce((map, result) => {if (result.key) map[result.key] = result.data;return map;},{} as Record<string, OptionType[]>);setSearchList((prev) =>prev.map((item) => ({...item,list: resultMap[item.value] || [],})));} catch (error) {console.error("加載篩選條件失敗:", error);}};fetchOptions();}, []);// 初始數據加載useEffect(() => {(async function () {await fetchData();})();}, [goodsName, chooseList]);return (<><Layout><div className={styles.courseDetail}><SearchonSearch={(searchText) => {// console.log("Searching for:", searchText);setGoodsName(searchText);}}/><div className={styles.courseDetail_content}>{/* 左側篩選欄 */}<div className={styles.courseDetail_content_left}><div className={styles.courseDetail_content_left_title}>篩選</div>{/* 查詢條件 */}{searchList.map((item) => (<React.Fragment key={item.value}><divclassName={`${styles.courseDetail_content_left_title} ${styles.courseDetail_content_left_subTitle}`}>{item.name}</div>{/* 業務模塊使用Tree組件,其他使用Checkbox組件 */}{item.query === "businessModel" ? (<><FilterTreetreeData={item.list || []}value={selectedValues[item.value] || []}onChange={(values) =>handleCheckboxChange(item.value, values)}className={styles.courseDetail_content_left_tree}/></>) : (<><FilterCheckboxoptions={item.list || []}value={selectedValues[item.value] || []}onChange={(values) =>handleCheckboxChange(item.value, values)}className={styles.courseDetail_content_left_checkbox}itemClassName={styles.courseDetail_content_left_checkbox_item}/></>)}</React.Fragment>))}</div>{/* 右側 */}<div className={styles.courseDetail_content_right}>{/* 查詢條件:標簽 */}<div className={styles.courseDetail_content_right_search}><div className={styles.courseDetail_content_right_search_left}>{/* 展示已選條件 */}{chooseList.map((item) => (<divkey={`${item.type}-${item.value}`}className={styles.courseDetail_content_right_search_left_tags}>{item.meaning}<CloseOutlinedstyle={{fontSize: "10px",color: "#8F8F8F",marginLeft: 4,}}onClick={() => removeCondition(item.type, item.value)}/></div>))}{/* 清空所有按鈕 */}{chooseList.length > 0 && (<divclassName={styles.courseDetail_content_right_search_left_tags}onClick={clearAllConditions}><Imagesrc={DeleteIcon}alt="清空"width={10}height={11}unoptimizedloading="lazy"/><span style={{ marginLeft: 4 }}>清空</span></div>)}</div><div className={styles.courseDetail_content_right_search_right}>找到全部相關內容:{pagination.total}</div></div>{/* 課程卡片區域 */}<div className={styles.courseDetail_content_right_card}><Card list={table} /></div>{/* 分頁 */}<div className={styles.courseDetail_content_right_page}><Paginationtotal={pagination.total || 0}current={pagination.currentPage} // 前端顯示的當前頁(1-based)pageSize={pagination.pageSize}showSizeChangershowQuickJumpershowTotal={(total) => `共 ${total} 條記錄`}onChange={(page) => fetchData(page)} // 頁碼變更時(1-based)onShowSizeChange={(currentPage, pageSize) => fetchData(currentPage, pageSize) // 切換每頁大小后從第一頁開始}/></div></div></div></div></Layout></>);
};
export default CourseDetail;