仿写讯飞AI生成PPT大纲组件

news2024/9/9 7:47:02

效果

别的不说先上效果
在这里插入图片描述

在这里插入图片描述

难点

  1. 树的实现 :需要递归自身,有丶难度。但是对于各位应该是有手就彳亍
  2. 双亲节点样式 :可以观察到双亲节点在连接线左侧是有内容的,叶子节点则没有。
  3. 连接线:可以观察到双亲节点是实心圆点,叶子节点是空心圆圈且连接线有缩进。
  4. 确定层级:缩进是根据层级确定的,如何确定层级。
  5. 确定最后的叶子节点:最后一个叶子节点的连接线是没有往下的,这里需要做判断。
  6. 插槽透传:因为需要递归,一些插槽的透传也是必不可少的。
  7. 重新渲染:有时数据结构变了,但是组件的并没有重新渲染导致与预期不一致。

实现

在这里插入图片描述

确定层级

在组件props中 保留一个默认层级level = 0,当有子节点时把:level = "level + 1" 传入子组件中即可完成标识。例如第一层的level = 0 ,第二层的level = 1。后续还可以根据level计算缩进线宽度。

连接线

1.第一个节点没有上边线,这个很好判断。
2.中间的节点有上边线,下边线,缩进线。
3.最后一个节点没有下边线,这个不太好判断。
4.点还是圆圈,这个很好判断。
5.缩进线的宽度计算公式:level * 每一节缩进线的宽度,这里默认一节缩进线宽度为16
例如:第一层的双亲节点他的缩进线宽度为:0 * 16 = 0
例如:第二层的叶子节点他的缩进线宽度为:1 * 16 =16

重新渲染

在组件上加key,改变key即可强制重新渲染。
在这里插入图片描述

确定最后的叶子节点

这个在组件内不好判断呐,那我干脆让使用者来判断。让他来做这件事,那在编程中怎么去做事呢?当然是函数辣,让用户传入函数,根据函数调用返回的结果去决定是否是最后的叶子节点。

在这里插入图片描述
我把数据都给你,我让你自己去判断。

插槽透传

循环$slots即可
在这里插入图片描述上面这样写ts会报错,但是其实是没问题,其它项目也是这么写的都没问题。但是为了不报红我还是用下面的写法在这里插入图片描述

源码

<template>
  <div v-bind="$attrs" class="tree-wrap">
    <template v-for="(chapter, chapterIdx) in treeData" :key="chapter.id">
      <div class="tree-node">
        <div class="node-desc" :style="descStyle">
          <slot
            v-if="$slots.desc"
            name="desc"
            :chapter="chapter"
            :chapterIdx="chapterIdx"
            :level="level"
          ></slot>
          <span v-else>{{ nodeDescText(level, chapterIdx) }}</span>
        </div>
        <div class="node-dot-line" :style="nodeDotLineStyle">
          <div v-if="shouldShowTopVertical(chapterIdx)" class="line-top"></div>
          <div
            :class="[shouldShowHorizontal(level, chapter) ? 'circle' : 'dot']"
            :style="nodeDotCircleStyle"
          ></div>
          <div v-if="isRenderLineBottom(level, chapter, chapterIdx)" class="line-bottom"></div>
          <div
            v-if="shouldShowHorizontal(level, chapter)"
            class="line-horizontal"
            :style="horizontalLineStyle"
          ></div>
        </div>
        <div class="node-info" :style="nodeInfoStyle">
          <slot
            v-if="$slots.title"
            name="title"
            :chapter="chapter"
            :chapterIdx="chapterIdx"
            :level="level"
          ></slot>
          <span v-else :class="{ 'font-bold': level === 0 }">{{ chapter.chapterTitle }}</span>
        </div>
        <div v-if="$slots.extra" class="extra" :style="extraStyle">
          <slot name="extra" :chapter="chapter" :chapterIdx="chapterIdx" :level="level"></slot>
        </div>
      </div>
      <PptOutline
        v-if="chapter && chapter.chapterContents"
        :level="level + 1"
        :tree-data="chapter.chapterContents"
        :key="chapter.id"
        :is-render-line-bottom="isRenderLineBottom"
      >
        <template v-for="(_slotFn, slotName) in $slots" :key="slotName" #[slotName]="slotProps">
          <slot v-if="slotName === 'desc'" name="desc" v-bind="slotProps"></slot>
          <slot v-if="slotName === 'title'" name="title" v-bind="slotProps"></slot>
          <slot v-if="slotName === 'extra'" name="extra" v-bind="slotProps"></slot>
        </template>
      </PptOutline>
    </template>
  </div>
</template>

<script setup lang="ts">
import PptOutline from '@/components/PptOutline.vue'

const props = withDefaults(
  defineProps<{
    level?: number
    treeData: ChaptersItem[]
    isRenderLineBottom?: (level: number, chapter: ChaptersItem, chapterIdx: number) => boolean
  }>(),
  {
    level: 0,
    isRenderLineBottom: () => true
  }
)
const slots = useSlots()
const dotSize = 7
const descWidth = 50
const extraWidth = 100
const subNodeLineLeft = 16
const descStyle = {
  width: `${descWidth}px`
}
const extraStyle = {
  width: `${extraWidth}px`
}
// 横向连接线长度
const horizontalLineWidth = computed(() => props.level * subNodeLineLeft)
const horizontalLineStyle = computed(() => ({
  width: `${horizontalLineWidth.value}px`
}))
// 节点点样式
const nodeDotCircleStyle = computed(() => {
  if (props.level === 0) {
    return {
      left: `${horizontalLineWidth.value}px`
    }
  } else {
    return {
      left: `${horizontalLineWidth.value + dotSize / 2}px`
    }
  }
})
// 节点点线样式
const nodeDotLineWidth = computed(() => 3 * dotSize + horizontalLineWidth.value)
const nodeDotLineStyle = computed(() => ({
  width: `${nodeDotLineWidth.value}px`
}))
// 节点信息样式
const nodeInfoStyle = computed(() => {
  if (slots.extra) {
    return {
      width: `calc(100% - ${descWidth + extraWidth + nodeDotLineWidth.value}px)`
    }
  } else {
    return {
      width: `calc(100% - ${descWidth + nodeDotLineWidth.value}px)`
    }
  }
})
// 是否展示横向连接线
const shouldShowHorizontal = (level: number, chapter: ChaptersItem): boolean =>
  level !== 0 && Boolean(chapter)
// 是否展示上半部分纵向连接线
const shouldShowTopVertical = (chapterIdx: number): boolean => chapterIdx !== 0 || props.level > 0
// 展示节点描述信息
const nodeDescText = (level: number, chapterIdx: number): string =>
  level === 0 ? `章节${chapterIdx + 1}` : ''
</script>

<style lang="less" scoped>
.tree-wrap {
  width: 100%;
  @titleColor: #161724;
  @titleFontSize: 14px;
  @descColor: #8e90a5;
  @descFontSize: 12px;
  @dotLineColor: #bfc7d6;
  @hoverBgColor: #f6f6f6;
  @hoverBorderRadius: 6px;
  @dotSize: 7px;
  @dotLineWidth: 1px;
  @subNodeLineleft: 16px;
  @dotTop: 15px;
  @nodeMinHeight: 36px;

  /**每一个节点-start */
  .tree-node {
    display: flex;
    padding: 0 4px 0 20px;
    min-height: @nodeMinHeight;
    line-height: @nodeMinHeight;
    &:hover {
      background-color: @hoverBgColor;
      border-radius: @hoverBorderRadius;
    }

    /**节点描述:例如章节1 */
    .node-desc {
      padding-right: 10px;
      color: @descColor;
      font-size: @descFontSize;
      word-wrap: break-word;
      white-space: pre-line;
    }
    /**节点描述-end */

    /**节点连接线-start */
    .node-dot-line {
      position: relative;
      width: 15px;
      height: inherit;
      .line-top {
        position: absolute;
        top: 0;
        left: calc(@dotSize / 2);
        width: @dotLineWidth;
        height: calc(@dotTop + @dotSize / 2);
        background-color: @dotLineColor;
      }
      .dot {
        position: absolute;
        top: @dotTop;
        width: @dotSize;
        height: @dotSize;
        border-radius: 50%;
        background-color: @dotLineColor;
      }
      .circle {
        position: relative;
        top: @dotTop;
        width: @dotSize;
        height: @dotSize;
        border-radius: 50%;
        border: @dotLineWidth solid @dotLineColor;
      }
      .line-bottom {
        position: absolute;
        bottom: 0;
        left: calc(@dotSize / 2);
        width: @dotLineWidth;
        height: calc(100% - @dotTop - @dotSize / 2);
        background-color: @dotLineColor;
      }
      .line-horizontal {
        position: relative;
        top: calc(@dotTop - @dotSize / 2);
        left: calc(@dotSize / 2);
        width: @subNodeLineleft;
        height: @dotLineWidth;
        background-color: @dotLineColor;
      }
    }
    /**节点连接线-end */

    /**节点信息:例如标题 */
    .node-info {
      min-width: 400px;
      height: fit-content;
      font-size: @titleFontSize;
      word-wrap: break-word;
      white-space: pre-line;
    }
    /**节点信息-end */

    /**节点额外信息:例如图标 */
    .extra {
      height: @nodeMinHeight;
      color: @descColor;
      text-align: right;
    }
    /**节点额外信息-end */
  }
  /**每一个节点-end */
}
.font-bold {
  font-weight: bold;
}
</style>

// 二级标题(章节)
declare interface ChaptersItem {
  id: number
  chapterTitle: string | null
  fileUrl: string | null
  fileType: number
  chartFlag: string | null
  searchFlag: string | null
  chapterContents: ChaptersItem[] | null
  [key: string]: any
}
// 大纲
declare interface OutLine {
  id: number
  title: string | null
  subTitle: string | null
  fileUrl: string | null
  fileType: number
  chapters: ChaptersItem[] | null
  end: string | null
  fileId: string | null
  [key: string]: any
}
// 大纲返回内容
declare interface PptOutlineData {
  sid: string
  coverImgSrc: string
  title: string
  subTitle: string
  outline: OutLine
  [key: string]: any
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1963631.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为交换机Telnet配置

华为交换机Telnet配置 一、Telnet概述与作用 1、Telnet简介 Telnet&#xff08;Telnet协议&#xff09;是一种用于远程登录到计算机或网络设备的协议。它允许用户在本地计算机上通过网络连接到远程计算机或设备&#xff0c;并在远程计算机上执行命令和操作。Telnet协议最初是…

SpringCloudAliababa中使用最新版的Seata实现分布式事务

SpringCloud中使用Seata实现分布式事务 Hello&#xff0c;兄弟们好&#xff0c;我是Feri&#xff0c;最近整理了最新的基于Seata-Server2.0实现分布式事务的demo&#xff0c;希望对你有所帮助&#xff0c;有任何问题&#xff0c;可以随时沟通交流&#xff0c;在成为技术大牛的路…

如何处理selenium Webdriver中的文本框?

文本框或字段在整个网页中广泛使用,本文将介绍如何在Java中使用Selenium Webdriver处理文本框。可以有各种文本字段,我们将尝试包括其中的大多数,并执行各种操作,如清除和输入文本。 我们将使用我们的Selenium游乐场网站- testkru,与各种文本框进行交互。您也可以使用同一…

昇思25天学习打卡营第27天|munger85

Vision Transformer图像分类 Vit是最新的用了transformer架构的图像模型&#xff0c;在很多比赛都获得了大奖&#xff0c;是非常优秀的架构。而且据说cnn其实只是vit的一个子集&#xff0c;cnn的卷积其实就是vit在很小的范围做了注意力机制。非常有意思 整个架构就好像这样 图…

七天打造一套量化交易系统:Day6-人工智能在量化投资中的应用

七天打造一套量化交易系统&#xff1a;Day6-人工智能在量化投资中的应用 步骤一&#xff1a;数据获取步骤二&#xff1a;对股票样本进行初步处理步骤三&#xff1a;遗传算法选股遗传算 kmeans 类的主要代码 步骤四&#xff1a;回测结果 遗传算法是一种基础的人工智能算法&#…

springboot惠农服务平台-计算机毕业设计源码50601

目录 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 惠农服务平台app 系统分析 2.1 可行性分析 2.2 系统功能分析 2.3 系统用例分析 2.4 系统流程分析 2.5本章小结 3 惠农服务平台app 总体设计 3.1 系统功能模块设计 3.2 数据库设计 表access_token (…

成品库存周转率报表(一)

文章目录 成品库存周转率报表(一)每日库存结存到表单《历史库存信息》报表逻辑报表设计过滤框简单账表界面存储过程,根据传入条件获取并计算返回数据报表服务插件简单账表绑定数据源插件绑定权限,发布,授权即可访问成品库存周转率报表(一) 每日库存结存到表单《历史库存…

基于域名的虚拟主机,基于ip的域名主机,综合项目eleme

查看nginx配置文件 不看空行&#xff0c;不看注释&#xff0c;查找 [rootstaticserver ~]# grep -Ev "#|^$" /usr/local/nginx/conf/nginx.conf 把原有的文件备份 [rootstaticserver ~]# cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.…

虚拟化数据恢复—XenServer VPS不可用如何恢复数据?

虚拟化数据恢复环境&#xff1a; 某品牌R720服务器&#xff0c;4块STAT硬盘通过H710P阵列卡组建了一组raid10磁盘阵列。服务器上部署XenServer虚拟化平台&#xff0c;虚拟机安装Windows Server系统&#xff0c;作为Web服务器使用&#xff0c;运行SQL Server数据库。共有2个虚拟…

智能巡航,守护蓝天绿水:无人机视频技术如何高效监督非法排污行为

随着工业化进程的加速和环境保护意识的日益增强&#xff0c;非法排污行为成为了威胁生态环境、影响公众健康的重大问题。传统的排污监测手段往往受限于人力成本、覆盖范围及效率等因素&#xff0c;难以实现对广袤区域和隐蔽排污点的有效监控。而无人机技术的飞速发展&#xff0…

【网络协议】HTTP协议详解

文章目录 一、概念 二、简史 三、特点 四、工作流程 五、使用Wireshark抓TCP、http包 六、头域 6.1、请求信息&#xff1a; 6.2、请求方法 6.3、响应消息 6.4、响应头域 6.5、HTTP常见的请求头 6.6、HTTP常见的响应头 七、解决HTTP无状态的问题 7.1、通过Cookies保存状态信息 7…

软件性能测试内容和方法揭秘,专业第三方软件测试公司推荐

在数字经济迅猛发展的背景下&#xff0c;软件的性能已经成为企业竞争力的重要一环。性能测试&#xff0c;作为软件测试的一个重要组成部分&#xff0c;主要用于评估应用程序在特定负载下的表现。这不仅包括响应时间、吞吐量、资源使用率等指标&#xff0c;还涉及系统在不同条件…

Stable Diffusion教程|快速入门SD绘画原理与安装

什么是Stable Diffusion&#xff0c;什么是炼丹师&#xff1f;根据市场研究机构预测&#xff0c;到2025年全球AI绘画市场规模将达到100亿美元&#xff0c;其中Stable Diffusion&#xff08;简称SD&#xff09;作为一种先进的图像生成技术之一&#xff0c;市场份额也在不断增长&…

前端-05-VSCode自定义代码片段console.log(js/ts配置)、代码段快捷提示放在首位

目录 配置VSCode自定义代码片段console.log()log代码段快捷提示放在首位 配置VSCode自定义代码片段console.log() 点击VSCode左下角设置图标&#xff0c;点击用户代码片段 点击用户代码片段后&#xff0c;VSCode上方出现弹窗如下图&#xff08;没有显示这两个文件的话搜索一下…

探索国际TikTok矩阵:快速涨粉与变现的全新玩法

在当前的TikTok&#xff08;国际版抖音&#xff09;生态中&#xff0c;矩阵玩法正在成为越来越多内容创作者和电商卖家的热门选择。要有效利用TikTok矩阵&#xff0c;首先得清晰地确定每个账号的内容定位&#xff0c;选择你想要深入的领域。 一、热门TikTok账号类型解析 1. 流量…

milvus - VectorDBBench benchmaker 性能测试工具使用经验

IVF_FLAT (Inverted File with Flat Indexing) 优点: 在数据量适中且维度不是非常高的情况下&#xff0c;IVF_FLAT能提供精确的最近邻搜索结果。 相对简单&#xff0c;易于理解和实现。 缺点: 当数据集非常大时&#xff0c;IVF_FLAT需要大量的内存来存储整个数据集&#xff0c;…

基于欧氏距离的点云聚类(python)

1、背景介绍 欧式聚类是一种基于欧氏距离度量的聚类算法。它是点云处理中的一个重要步骤&#xff0c;它可以帮助我们从无序的点云数据中提取有意义的信息。一般来说&#xff0c;对点云进行欧式聚类处理&#xff0c;可以帮助后续数据处理&#xff0c;如物体检测与识别、三维重建…

链式栈,队列与树形结构

链式栈 链式存储的栈 实现方式&#xff1a;可以使用单向链表完成 对单向链表进行头插&#xff08;入栈&#xff09;、头删&#xff08;出栈&#xff09;&#xff0c;此时链表的头部就是链栈的栈顶&#xff0c;链表的尾部&#xff0c;就是链栈的栈底 队列 概念 队列&#…

二叉树算法题(1)

单值二叉树 思路&#xff1a; 若根结点为NULL&#xff0c;则直接返回true若根结点的左右结点存在&#xff0c;且与根结点的值不相等&#xff0c;则返回false递归根结点的左右子树 typedef struct TreeNode TreeNode; bool isUnivalTree(struct TreeNode* root) {if(root NU…

程序员修炼之路:深入广泛的必修课程

成为一名优秀的程序员&#xff0c;需要广泛而深入地学习多个领域的知识。这些课程不仅帮助建立扎实的编程基础&#xff0c;还培养了问题解决、算法设计、系统思维等多方面的能力。以下是一些核心的必修课&#xff1a; 计算机基础 计算机组成原理&#xff1a;理解计算机的硬件组…