Преглед изворни кода

分类产品页部分调整

zhangzf пре 3 дана
родитељ
комит
2578890bd8

+ 4 - 4
src/app/(public)/category/[slug]/[id]/_components/CategoryDesc.tsx

@@ -41,18 +41,18 @@ const CategoryDesc: FC<CategoryDescProps> = ({ title, description }) => {
   );
   );
 
 
   return (
   return (
-    <div className="px-4">
-      <h2 className="text-ly-18 leading-ly-24 font-medium mt-8 mb-4">
+    <div className="">
+      <h2 className="text-ly-24 leading-ly-26 font-medium mt-8 mb-4">
         {title}
         {title}
       </h2>
       </h2>
 
 
       <p
       <p
         ref={textRef}
         ref={textRef}
-        className={`overflow-hidden text-ly-12 leading-ly-20 font-normal transition-[max-height] text-[#666] duration-300 ease-in-out ${
+        className={`overflow-hidden text-ly-18 leading-ly-26 font-normal transition-[max-height] text-[#666] duration-300 ease-in-out ${
           isExpand ? "" : "line-clamp-2"
           isExpand ? "" : "line-clamp-2"
         }`}
         }`}
       >
       >
-        {description}
+        <div dangerouslySetInnerHTML={{ __html: description }} />
       </p>
       </p>
 
 
       {showMore && (
       {showMore && (

+ 41 - 37
src/app/(public)/category/[slug]/[id]/_components/ProductListing.tsx

@@ -13,6 +13,7 @@ import { useRouter, usePathname } from "next/navigation";
 import {  useApolloClient } from "@apollo/client/react";
 import {  useApolloClient } from "@apollo/client/react";
 import type{ CategoryProductsResult,PageInfo,CategoryProductNode,CategoryAttrFilterItem } from "@components/catalog/type";
 import type{ CategoryProductsResult,PageInfo,CategoryProductNode,CategoryAttrFilterItem } from "@components/catalog/type";
 import {CATEGORY_PRODUCTS} from "@/graphql";
 import {CATEGORY_PRODUCTS} from "@/graphql";
+import {transformFiltersForApi} from "@utils/helper";
 interface QueryState {
 interface QueryState {
   // search: string;
   // search: string;
 
 
@@ -65,6 +66,8 @@ export default function ProductListing({
 
 
   const fetchProducts = async (currentQuery: QueryState) => {
   const fetchProducts = async (currentQuery: QueryState) => {
     try {
     try {
+        // 转换
+    const finalFilterObj = transformFiltersForApi(currentQuery.filters);
       const res = await apolloClient.query<CategoryProductsResult>({
       const res = await apolloClient.query<CategoryProductsResult>({
         query: CATEGORY_PRODUCTS,
         query: CATEGORY_PRODUCTS,
         variables: 
         variables: 
@@ -80,14 +83,14 @@ export default function ProductListing({
         
         
           sortKey: currentQuery.sortKey,
           sortKey: currentQuery.sortKey,
           reverse: currentQuery.reverse,
           reverse: currentQuery.reverse,
-          filter:JSON.stringify( currentQuery.filters),
+          filter:JSON.stringify( finalFilterObj),
 
 
           first: 15,
           first: 15,
 
 
           after: null,
           after: null,
         },
         },
       });
       });
-      console.log("res----------------------------------:",res,currentQuery);
+      console.log("res----------------------------------:",res,currentQuery,finalFilterObj);
       
       
       // const res = await fetch("/api/products", {
       // const res = await fetch("/api/products", {
       //   method: "POST",
       //   method: "POST",
@@ -217,8 +220,8 @@ export default function ProductListing({
       }
       }
   };
   };
   return (
   return (
-    <>
-      <div className="flex items-center justify-between gap-4 py-8 md:hidden w-full max-w-screen-2xl mx-auto px-4">
+    <> 
+      <div className="flex  justify-between  py-8  w-full mx-auto ">
         <NewMobileFilter
         <NewMobileFilter
           filterAttributes={filterAttributes}
           filterAttributes={filterAttributes}
           filters={query.filters}
           filters={query.filters}
@@ -232,40 +235,41 @@ export default function ProductListing({
             });
             });
           }}
           }}
         />
         />
-        <NewSortOrder
-          sortOrders={newSortByFields}
-          title="Sort by"
-          value={query.sortValue}
-          onChange={(sortValue,sortKey,reverse) => {
-            updateQuery({
-              ...query,
-              sortValue,
-              sortKey,
-              reverse:reverse,
-              cursor: undefined,
-            });
-          }}
-        />
-      </div>
-
-      {isArray(products) && products.length > 0 ? (
-        <Grid className="grid grid-flow-row grid-cols-2 gap-5 lg:gap-11.5 w-full max-w-screen-2xl mx-auto md:grid-cols-3 lg:grid-cols-4 px-4 xss:px-7.5">
-          <ProductGridItems products={products} />
-        </Grid>
-      ) : (
-        <div className="px-4">
-          <div className="flex h-40 items-center justify-center rounded-lg border border-dashed border-neutral-300">
-            <p className="text-neutral-500">
-              No products found in this category.
-            </p>
-          </div>
+        <div>
+              <NewSortOrder
+                sortOrders={newSortByFields}
+                title="Sort by"
+                value={query.sortValue}
+                onChange={(sortValue,sortKey,reverse) => {
+                  updateQuery({
+                    ...query,
+                    sortValue,
+                    sortKey,
+                    reverse:reverse,
+                    cursor: undefined,
+                  });
+                }}
+              />
+              {isArray(products) && products.length > 0 ? (
+                 <Grid className="grid grid-cols-4 gap-8">
+                   <ProductGridItems products={products} />
+                 </Grid>
+               ) : (
+                 <div className="px-4">
+                   <div className="flex h-40 items-center justify-center rounded-lg border border-dashed border-neutral-300">
+                     <p className="text-neutral-500">
+                       No products found in this category.
+                     </p>
+                   </div>
+                 </div>
+               )}
+               {pageInfo?.hasNextPage && (
+                 <div className="flex justify-center my-10">
+                   <button onClick={loadMore}>View More</button>
+                 </div>
+              )}
         </div>
         </div>
-      )}
-      {pageInfo?.hasNextPage && (
-        <div className="flex justify-center my-10">
-          <button onClick={loadMore}>View More</button>
-        </div>
-      )}
+      </div>
     </>
     </>
   );
   );
 }
 }

+ 16 - 5
src/app/(public)/category/[slug]/[id]/page.tsx

@@ -4,6 +4,7 @@ import { notFound } from "next/navigation";
 // import FilterList from "@components/theme/filters/FilterList";
 // import FilterList from "@components/theme/filters/FilterList";
 // import Pagination from "@components/catalog/Pagination";
 // import Pagination from "@components/catalog/Pagination";
 import ProductListing from "./_components/ProductListing";
 import ProductListing from "./_components/ProductListing";
+import Image from "next/image";
 import type{
 import type{
   // ProductsResponse,
   // ProductsResponse,
   GetCategoryAttrFiltersResult,
   GetCategoryAttrFiltersResult,
@@ -33,6 +34,7 @@ import {
   // extractNumericId,
   // extractNumericId,
   // findCategoryBySlug,
   // findCategoryBySlug,
   newBuildProductFilters,
   newBuildProductFilters,
+  transformFiltersForApi,
 } from "@utils/helper";
 } from "@utils/helper";
 import { serverGraphqlFetch } from "@/utils/bagisto/index";
 import { serverGraphqlFetch } from "@/utils/bagisto/index";
 import type {
 import type {
@@ -158,7 +160,7 @@ export default async function CategoryPage({
   const cursor = getSearchParam(resolvedParams?.cursor);
   const cursor = getSearchParam(resolvedParams?.cursor);
 
 
   const before = getSearchParam(resolvedParams?.before);
   const before = getSearchParam(resolvedParams?.before);
-  const itemsPerPage = 15;
+  const itemsPerPage = 8;
   // const currentPage = page ? parseInt(page) - 1 : 0;
   // const currentPage = page ? parseInt(page) - 1 : 0;
   const sortValue = getSearchParam(resolvedParams?.sort) ?? "name-asc";
   const sortValue = getSearchParam(resolvedParams?.sort) ?? "name-asc";
   const selectedSort =
   const selectedSort =
@@ -175,8 +177,8 @@ export default async function CategoryPage({
   if (numericId) {
   if (numericId) {
     filterObject.category_id = numericId;
     filterObject.category_id = numericId;
   }
   }
-
-  const filterInput = JSON.stringify(filterObject);
+  const finalFilterObj =transformFiltersForApi(filterObject);
+  const filterInput = JSON.stringify(finalFilterObj);
   // 默认获取产品数据接口:
   // 默认获取产品数据接口:
   const { data: categoryProductDatas } = await serverGraphqlFetch<
   const { data: categoryProductDatas } = await serverGraphqlFetch<
     CategoryProductsResult,
     CategoryProductsResult,
@@ -298,7 +300,7 @@ export default async function CategoryPage({
   return (
   return (
     <>
     <>
       {/* <MobileSearchBar /> */}
       {/* <MobileSearchBar /> */}
-      <section>
+      <section className="px-12">
         <Suspense fallback={<FilterListSkeleton />}>
         <Suspense fallback={<FilterListSkeleton />}>
           {/* <CategoryDetail
           {/* <CategoryDetail
             categoryItem={{
             categoryItem={{
@@ -306,10 +308,19 @@ export default async function CategoryPage({
               name: translation?.name ?? "",
               name: translation?.name ?? "",
             }}
             }}
           /> */}
           /> */}
+          <div className="felx justify-center">
+              <Image
+               src={categoryProductDatas? categoryProductDatas.categoryProducts.banner: ''}
+               alt={'categoryBanner'}
+               width={1824}
+               height={608}
+               
+              />
+          </div>
           <CategoryDesc
           <CategoryDesc
             title={translation?.name ?? ""}
             title={translation?.name ?? ""}
             description={
             description={
-              "Alipearl Hair provides many style best quality but cheap lace wigs,including lace front wigs, lace closure wigs, full lace human hair wigs, HD transparent lace wigs, 13x4 frontal wigs, 13x6 lace front wigs, 360 lace wigs, all textures available straight, body wave, deep wave, water wave, curly hair wigs, all lengths from 8 inch to 40 inch, different hair colors natural black, brown, blonde, highlight, ginger, burgundy etc."
+              categoryProductDatas? categoryProductDatas.categoryProducts.description: ''
             } //translation?.description ?? ""}
             } //translation?.description ?? ""}
           />
           />
         </Suspense>
         </Suspense>

+ 3 - 0
src/components/catalog/type.ts

@@ -176,6 +176,7 @@ export interface CategoryAttrFilterItem  {
         adminName: string;
         adminName: string;
         sortOrder: number;
         sortOrder: number;
         swatchValue: string | null;
         swatchValue: string | null;
+        productCount:number;
       };
       };
     }>;
     }>;
   };
   };
@@ -210,6 +211,8 @@ export type CategoryProductNode = {
 export type CategoryProductsResult = {
 export type CategoryProductsResult = {
   categoryProducts: {
   categoryProducts: {
     totalCount: number;
     totalCount: number;
+    description: string;
+    banner: string;
     pageInfo:{
     pageInfo:{
       endCursor:string,
       endCursor:string,
       hasNextPage: boolean
       hasNextPage: boolean

+ 90 - 165
src/components/theme/filters/NewMobileFilter.tsx

@@ -1,27 +1,11 @@
 "use client";
 "use client";
-
-import {
-  Drawer,
-  DrawerContent,
-  DrawerHeader,
-  DrawerBody,
-} from "@heroui/drawer";
-
 import { Button } from "@heroui/button";
 import { Button } from "@heroui/button";
-
-import { useDisclosure } from "@heroui/use-disclosure";
-import FilterPrice from "./FilterPrice";
-import {
-  AdjustmentsHorizontalIcon,
-  XMarkIcon,
-} from "@heroicons/react/24/outline";
 import { useEffect, useState, useRef } from "react";
 import { useEffect, useState, useRef } from "react";
+import FilterPrice from "./FilterPrice";
 
 
 interface MobileFilterProps {
 interface MobileFilterProps {
   filterAttributes: any[];
   filterAttributes: any[];
-
   filters: Record<string, string>;
   filters: Record<string, string>;
-
   onChange: (filters: Record<string, string>) => void;
   onChange: (filters: Record<string, string>) => void;
 }
 }
 export default function MobileFilter({
 export default function MobileFilter({
@@ -29,67 +13,38 @@ export default function MobileFilter({
   filters,
   filters,
   onChange,
   onChange,
 }: MobileFilterProps) {
 }: MobileFilterProps) {
-  const { isOpen, onOpen, onOpenChange } = useDisclosure();
-  // 记录上一次抽屉状态,用来捕获【关闭→打开】瞬间
-  const prevIsOpenRef = useRef(isOpen);
-
   /**
   /**
-   * 弹窗里面临时选择
+   * 临时筛选状态
    */
    */
   const [tempFilters, setTempFilters] = useState<Record<string, string>>({});
   const [tempFilters, setTempFilters] = useState<Record<string, string>>({});
-
   const [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>(
   const [expandedGroups, setExpandedGroups] = useState<Record<string, boolean>>(
     {},
     {},
   );
   );
 
 
-  /**
-   * 打开弹窗同步父组件状态
-   */
-  useEffect(() => {
-    // 仅从关闭切换到打开时执行
-    if (!prevIsOpenRef.current && isOpen) {
-      console.log("dadadadadadadadisopen");
-      // 使用 queueMicrotask 把状态更新延后,避开同步setState警告
-      queueMicrotask(() => {
-        // 浅拷贝,断开引用
-        setTempFilters({ ...filters });
-        console.log("tempFilters---------------------", tempFilters);
-
-        const expand: Record<string, boolean> = {};
-        filterAttributes.forEach((attr) => {
-          expand[attr.code] = true;
-        });
-        setExpandedGroups(expand);
-      });
-    }
-    prevIsOpenRef.current = isOpen;
-
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isOpen]);
+  // 父组件filters更新时同步到本地临时筛选
   useEffect(() => {
   useEffect(() => {
-    console.log("【父组件传入筛选filters】", filters);
-    console.log("【抽屉是否打开isOpen】", isOpen);
-  }, [isOpen, filters]);
+    setTempFilters({ ...filters });
+    const expand: Record<string, boolean> = {};
+    filterAttributes.forEach((attr) => {
+      expand[attr.code] = true;
+    });
+    setExpandedGroups(expand);
+  }, [filters, filterAttributes]);
 
 
   /**
   /**
-   * 单选
+   * 单选切换
    */
    */
   const toggleOption = (code: string, id: string) => {
   const toggleOption = (code: string, id: string) => {
     setTempFilters((prev) => {
     setTempFilters((prev) => {
       const current = prev[code];
       const current = prev[code];
-
       if (current === id) {
       if (current === id) {
         const next = { ...prev };
         const next = { ...prev };
-
         delete next[code];
         delete next[code];
-        // 打印点击后最新的临时筛选
         console.log("【点击选项后临时筛选tempFilters】", next);
         console.log("【点击选项后临时筛选tempFilters】", next);
         return next;
         return next;
       }
       }
-
       return {
       return {
         ...prev,
         ...prev,
-
         [code]: id,
         [code]: id,
       };
       };
     });
     });
@@ -98,7 +53,6 @@ export default function MobileFilter({
   const toggleGroupExpand = (code: string) => {
   const toggleGroupExpand = (code: string) => {
     setExpandedGroups((prev) => ({
     setExpandedGroups((prev) => ({
       ...prev,
       ...prev,
-
       [code]: !prev[code],
       [code]: !prev[code],
     }));
     }));
   };
   };
@@ -108,130 +62,101 @@ export default function MobileFilter({
    */
    */
   const applyFilters = () => {
   const applyFilters = () => {
     onChange(tempFilters);
     onChange(tempFilters);
-
-    onOpenChange();
   };
   };
 
 
   /**
   /**
-   * 清空
+   * 清空筛选
    */
    */
   const clearAll = () => {
   const clearAll = () => {
     setTempFilters({});
     setTempFilters({});
-
     onChange({});
     onChange({});
   };
   };
 
 
   const formatLabel = (str?: string) => {
   const formatLabel = (str?: string) => {
     return str ? str.charAt(0).toUpperCase() + str.slice(1).toLowerCase() : "";
     return str ? str.charAt(0).toUpperCase() + str.slice(1).toLowerCase() : "";
   };
   };
-  return (
-    <>
-      <div className="flex flex-wrap gap-3">
-        <Button size="md" className="flex bg-neutral-100" onPress={onOpen}>
-          <AdjustmentsHorizontalIcon className="h-6 w-8" />
 
 
-          <span>Filter</span>
-        </Button>
+  return (
+    <div className=" rounded-lg  w-full max-w-[300px]">
+      {/* Header */}
+      <div className="flex justify-between items-center pb-4 border-b">
+        <h2 className="text-xl font-semibold">Filters:</h2>
+        <button onClick={clearAll} className="text-sm text-primary">Clear All</button>
       </div>
       </div>
 
 
-      <Drawer
-        isOpen={isOpen}
-        placement="right"
-        onOpenChange={onOpenChange}
-        hideCloseButton
-      >
-        <DrawerContent className="h-full w-full max-w-full !rounded-none">
-          <>
-            <DrawerHeader className="flex justify-between px-6 py-4 border-b">
-              <h2 className="text-xl font-semibold">Filters:</h2>
-
-              <div className="flex gap-6">
-                <button onClick={clearAll}>Clear All</button>
-
-                <button onClick={() => onOpenChange()}>
-                  <XMarkIcon className="w-6 h-6" />
-                </button>
-              </div>
-            </DrawerHeader>
-
-            <DrawerBody className="px-6 overflow-y-auto">
-              {filterAttributes.map((attr) => {
-                const expand = expandedGroups[attr.node.code] ?? true;
-
-                return (
-                  <div key={attr.node._id} className="py-4 border-b">
-                    <button
-                      className="flex justify-between w-full text-xl"
-                      onClick={() => toggleGroupExpand(attr.node.code)}
-                    >
-                      <span>{formatLabel(attr.node.adminName)}</span>
-
-                      <span>⌄</span>
-                    </button>
-
-                    {expand && (
-                      <div className="flex flex-col gap-3 mt-4">
-                        {attr.node.code === "price" ? (
-                          <FilterPrice
-                            priceAttr={attr.node}
-                            initialRange={{
-                              minPrice: tempFilters.minPrice
-                                ? Number(tempFilters.minPrice)
-                                : undefined,
-                              maxPrice: tempFilters.maxPrice
-                                ? Number(tempFilters.maxPrice)
-                                : undefined,
-                            }}
-                            onChangeComplete={(range) => {
-                              setTempFilters((prev) => ({
-                                ...prev,
-                                minPrice: String(range.minPrice),
-                                maxPrice: String(range.maxPrice),
-                              }));
-                            }}
-                          />
-                        ) : (
-                          (attr.node.options.edges || []).map((opt: any) => {
-                            const checked =
-                              tempFilters[attr.node.code] == opt.node._id;
-                            return (
-                              <label
-                                key={opt.node._id}
-                                className="flex gap-3 text-lg"
-                              >
-                                <input
-                                  type="checkbox"
-                                  checked={checked}
-                                  onChange={() =>
-                                    toggleOption(attr.node.code, opt.node._id)
-                                  }
-                                />
-
-                                <span>{opt.node.adminName}</span>
-                              </label>
-                            );
-                          })
-                        )}
-                      </div>
-                    )}
-                  </div>
-                );
-              })}
-            </DrawerBody>
-
-            <div className="px-6 py-4 border-t">
-              <Button
-                color="primary"
-                size="lg"
-                className="w-full"
-                onPress={applyFilters}
+      {/* 筛选内容区域 */}
+      <div className="py-4 max-h-[70vh] overflow-y-auto overflow-x-hidden">
+        {filterAttributes.map((attr) => {
+          const expand = expandedGroups[attr.node.code] ?? true;
+          return (
+            <div key={attr.node._id} className="py-4 border-b">
+              <button
+                className="flex justify-between w-full text-xl"
+                onClick={() => toggleGroupExpand(attr.node.code)}
               >
               >
-                Apply Filter
-              </Button>
+                <span>{formatLabel(attr.node.adminName)}</span>
+                <span>⌄</span>
+              </button>
+              {expand && (
+                <div className="flex flex-col gap-3 mt-4">
+                  {attr.node.code === "price" ? (
+                    <FilterPrice
+                      priceAttr={attr.node}
+                      initialRange={{
+                        minPrice: tempFilters.price_from
+                          ? Number(tempFilters.price_from)
+                          : undefined,
+                        maxPrice: tempFilters.price_to
+                          ? Number(tempFilters.price_to)
+                          : undefined,
+                      }}
+                      onChangeComplete={(range) => {
+                        setTempFilters((prev) => ({
+                          ...prev,
+                          price_from: String(range.minPrice),
+                          price_to: String(range.maxPrice),
+                        }));
+                      }}
+                    />
+                  ) : (
+                    (attr.node.options.edges || []).map((opt: any) => {
+                      const checked =
+                        tempFilters[attr.node.code] == opt.node._id;
+                      return opt.node.productCount > 0 &&  (
+                        <label
+                          key={opt.node._id}
+                          className="flex gap-3 text-lg items-center"
+                        >
+                          <input
+                            type="checkbox"
+                            checked={checked}
+                            onChange={() =>
+                              toggleOption(attr.node.code, opt.node._id)
+                            }
+                          />
+                          <span>{opt.node.adminName}</span>
+                        </label>
+                      );
+                    })
+                  )}
+                </div>
+              )}
             </div>
             </div>
-          </>
-        </DrawerContent>
-      </Drawer>
-    </>
+          );
+        })}
+      </div>
+
+      {/* 底部应用按钮 */}
+      <div className="">
+        <Button
+          color="primary"
+          size="lg"
+          className="w-full"
+          onPress={applyFilters}
+        >
+          Apply Filter
+        </Button>
+      </div>
+    </div>
   );
   );
 }
 }

+ 1 - 0
src/graphql/catalog/queries/CategoryAttributeFilters.ts

@@ -25,6 +25,7 @@ export const GET_CATEGORY_ATTR_FILTERS = gql`
                 adminName
                 adminName
                 sortOrder
                 sortOrder
                 swatchValue
                 swatchValue
+                productCount
               }
               }
             }
             }
           }
           }

+ 2 - 0
src/graphql/catalog/queries/GetCategoryProducts.ts

@@ -4,6 +4,8 @@ export const CATEGORY_PRODUCTS = gql`
 query CategoryProducts($slug: String!, $filter: String, $sortKey: String, $reverse: Boolean, $first: Int, $after: String) {
 query CategoryProducts($slug: String!, $filter: String, $sortKey: String, $reverse: Boolean, $first: Int, $after: String) {
   categoryProducts(slug: $slug, filter: $filter, sortKey: $sortKey, reverse: $reverse, first: $first, after: $after) {
   categoryProducts(slug: $slug, filter: $filter, sortKey: $sortKey, reverse: $reverse, first: $first, after: $after) {
     totalCount
     totalCount
+    description
+    banner
     pageInfo {
     pageInfo {
      endCursor     
      endCursor     
      hasNextPage   
      hasNextPage   

+ 33 - 6
src/utils/helper.ts

@@ -300,7 +300,6 @@ export function buildProductFilters(params: {
 export function newBuildProductFilters(params: {
 export function newBuildProductFilters(params: {
   [key: string]: string | string[] | undefined;
   [key: string]: string | string[] | undefined;
 }) {
 }) {
-  // ==========黑名单:这些字段不会筛选==========
   const EXCLUDE_KEYS = new Set([
   const EXCLUDE_KEYS = new Set([
     "q",
     "q",
     "sort",
     "sort",
@@ -309,13 +308,13 @@ export function newBuildProductFilters(params: {
     "after",
     "after",
     "page",
     "page",
   ]);
   ]);
+  const PRICE_KEYS = new Set(["price_from", "price_to"]);
 
 
   const extractId = (value: string) => {
   const extractId = (value: string) => {
     if (/^\d+$/.test(value)) return value;
     if (/^\d+$/.test(value)) return value;
     const match = value.match(/\/(\d+)$/);
     const match = value.match(/\/(\d+)$/);
     return match ? match[1] : null;
     return match ? match[1] : null;
   };
   };
-
   const parseParamIds = (raw: string | string[] | undefined) => {
   const parseParamIds = (raw: string | string[] | undefined) => {
     let list: string[] = [];
     let list: string[] = [];
     if (typeof raw === "string") {
     if (typeof raw === "string") {
@@ -328,13 +327,20 @@ export function newBuildProductFilters(params: {
       .filter((id): id is string => Boolean(id));
       .filter((id): id is string => Boolean(id));
   };
   };
 
 
-  const filterObject: Record<string, string> = {};
+  const filterObject: Record<string, string > = {};
 
 
-  // 遍历全部传入参数
   for (const [key, value] of Object.entries(params)) {
   for (const [key, value] of Object.entries(params)) {
-    // 黑名单字段直接跳过
     if (EXCLUDE_KEYS.has(key)) continue;
     if (EXCLUDE_KEYS.has(key)) continue;
 
 
+    // ✅ 价格字段单独处理,不执行ID提取
+    if (PRICE_KEYS.has(key)) {
+      if (typeof value === "string" && value) {
+        filterObject[key] = value;
+      }
+      continue;
+    }
+
+    // 普通属性筛选,沿用原来的ID提取逻辑
     const ids = parseParamIds(value);
     const ids = parseParamIds(value);
     if (ids.length > 0) {
     if (ids.length > 0) {
       filterObject[key] = ids.join(",");
       filterObject[key] = ids.join(",");
@@ -362,4 +368,25 @@ export function getUuId() {
         localStorage.setItem(key, id);
         localStorage.setItem(key, id);
     }
     }
     return id;
     return id;
-};
+};
+// 筛选字段要多个match {\"wig_color\":{\"match\":\"33\"},\"price_from\":10,\"price_to\":212}
+ export function transformFiltersForApi(raw: Record<string, any>) {
+   // filter传参需要调整
+  const result: Record<string, any> = {};
+
+  for (const key in raw) {
+    const val = raw[key];
+    // 价格字段特殊处理:转数字,直接赋值,不包match
+    if (key === "price_from" || key === "price_to") {
+      // 空/undefined 跳过
+      if (val === undefined || val === null || val === "") continue;
+      result[key] = Number(val);
+    } else {
+      // 其他所有筛选属性:套上 {match:xxx}
+      result[key] = {
+        match:String(val),
+      };
+    }
+   }
+   return result;
+  }