You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js+Sanity中用@portabletext/react实现多样式嵌套列表

实现嵌套列表及双样式切换方案

核心修改思路

要实现嵌套列表并切换两种样式,需要利用@portabletext/react提供的level属性识别嵌套层级,同时通过外部参数控制样式切换。以下是具体修改后的代码和说明:

修改后的RichText组件代码

import Image from 'next/image';
import urlFor from '../lib/urlFor';
import Link from 'next/link';
import { PortableTextReactComponents } from '@portabletext/react';
import CodeBlock from './CodeBlock';

// 改为函数组件,接收样式切换参数
export const RichText = ({ isAlternativeStyle }: { isAlternativeStyle: boolean }): Partial<PortableTextReactComponents> => {
  // 根据层级和样式类型返回对应的列表类名
  const getBulletListClass = (level: number) => {
    if (isAlternativeStyle) {
      // 替代样式:调整缩进、符号类型和间距
      return `my-3 ml-${8 + level * 6} list-square text-[#333a4a]`;
    }
    // 常规样式:按层级递增缩进
    return `my-5 ml-${10 + level * 6} list-disc tracking-[0.005em] text-base text-[#333a4a]`;
  };

  const getNumberListClass = (level: number) => {
    if (isAlternativeStyle) {
      return `my-3 ml-${8 + level * 6} list-decimal text-[#333a4a]`;
    }
    return `my-5 ml-${10 + level * 6} list-decimal tracking-[0.005em] text-base text-[#333a4a]`;
  };

  return {
    types: {
      image: ({ value }: any) => {
        return (
          <div>
            <section className="relative h-[25rem]">
              <Image
                src={urlFor(value).url()}
                fill
                alt="内容图片"
                className="rounded-md"
              />
            </section>
            <section className="flex flex-col items-center">
              <p className="tracking-[0.005em] text-sm text-[#9fa2aa] pt-4">
                {value.caption}
              </p>
            </section>
          </div>
        );
      },
      code: ({ _, value: { code, language } }: any) => (
        <CodeBlock language={language} code={code} />
      ),
    },
    list: {
      bullet: ({ children, level = 0 }: any) => (
        <ul className={getBulletListClass(level)}>
          {children}
        </ul>
      ),
      number: ({ children, level = 0 }: any) => (
        <ol className={getNumberListClass(level)}>
          {children}
        </ol>
      ),
    },
    // 自定义列表项样式,适配不同样式需求
    listItem: {
      bullet: ({ children }: any) => (
        <li className={isAlternativeStyle ? "py-2" : "py-1"}>
          {children}
        </li>
      ),
      number: ({ children }: any) => (
        <li className={isAlternativeStyle ? "py-2" : "py-1"}>
          {children}
        </li>
      ),
    },
    block: {
      normal: ({ children }: any) => (
        <p className="my-5 tracking-[0.005em] text-base text-[#333a4a] leading-7">
          {children}
        </p>
      ),
      h1: ({ children }: any) => (
        <h1 className="my-5 text-5xl font-semibold text-fs-black">{children}</h1>
      ),
      h2: ({ children }: any) => (
        <h2 className="mt-10 mb-5 text-4xl font-medium text-fs-black">
          {children}
        </h2>
      ),
      h3: ({ children }: any) => (
        <h3 className="mt-14 mb-5 text-3xl font-medium pb-2 border-b-[1px] border-b-[#E6E6E6] text-fs-black">
          {children}
        </h3>
      ),
      h4: ({ children }: any) => (
        <h4 className="mt-14 mb-5 text-2xl font-medium pb-2 border-b-[1px] border-b-[#E6E6E6] text-fs-black">
          {children}
        </h4>
      ),
      h5: ({ children }: any) => (
        <h4 className="mt-10 text-xl font-medium text-fs-black">{children}</h4>
      ),
      blockquote: ({ children }: any) => (
        <blockquote className="py-5 pl-5 my-5 border-l-4 border-l-primary tracking-[0.005em] text-base text-fs-black">
          {children}
        </blockquote>
      ),
    },
    marks: {
      link: ({ children, value }: any) => {
        const rel = !value.href.startsWith('/')
          ? 'noreferrer noopener'
          : undefined;
        return (
          <Link href={value.href} rel={rel}>
            {children}
          </Link>
        );
      },
    },
  };
};

关键实现点说明

  1. 样式切换参数:将RichText改为函数组件,通过isAlternativeStyle参数控制两种样式的切换
  2. 嵌套层级处理:利用list组件接收的level属性,动态计算不同层级的缩进距离,确保嵌套列表显示正确
  3. 列表项自定义:新增listItem配置,针对不同样式调整列表项的内边距,优化视觉效果
  4. 样式类名动态生成:通过getBulletListClass和getNumberListClass函数,根据层级和样式类型返回对应的Tailwind类名

使用方式

在渲染PortableText组件时,根据传入的条件切换样式:

import { PortableText } from '@portabletext/react';
import { RichText } from './RichText';

// 常规样式
<PortableText value={yourContent} components={RichText({ isAlternativeStyle: false })} />

// 替代样式
<PortableText value={yourContent} components={RichText({ isAlternativeStyle: true })} />

内容的提问来源于stack exchange,提问作者Toss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 19:05:07