# 多行省略

`ShowtimeTwoLineEllipsis` 默认在两行内展示正文，也可以通过 `lineCount` 自定义折叠行数。组件会用实际渲染字体测量每一行，最后一行为“更多”入口预留真实宽度；点击入口后展开全部内容，并把“收起”作为正文后的行内入口，空间不足时自然换行。

## 基础用法

```vue
<script setup lang="ts">
import { ref } from 'vue'
import { ShowtimeTwoLineEllipsis } from 'showtime-components'

const expanded = ref(false)
</script>

<template>
  <ShowtimeTwoLineEllipsis
    v-model:expanded="expanded"
    text="尹彤，教授，学校党委书记，四川省人民政府派驻电子科技大学工作组成员。这是一段较长的正文，用来展示可配置行数、末行省略和完整展开。"
    :line-count="3"
    more-text="查看详情"
    more-href="/detail"
    @more="({ href }) => console.log('open:', href)"
  />
</template>
```

默认入口是原生 `<a>`。`moreHref` 为 `#` 时会阻止页面跳回顶部；传入真实地址时保留链接行为，同时触发展开、`update:expanded` 和 `more` 事件。

## Props

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `text` | `string` | `''` | 要测量并拆分的正文。 |
| `lineCount` | `number` | `2` | 折叠态最多显示的行数，小于 1 的值按 1 处理。 |
| `showEllipsis` | `boolean` | `true` | 是否在折叠态末行显示 CSS 省略号；设为 `false` 时只裁切，不显示 `...`。 |
| `expanded` | `boolean` | `false` | 展开状态，支持 `v-model:expanded` 外部控制。 |
| `moreText` | `string` | `更多>>` | 默认入口文案，也会传给 `more` 插槽。 |
| `collapseText` | `string` | `收起` | 默认收起入口文案。 |
| `moreHref` | `string` | `#` | 默认入口的链接地址。 |
| `lineHeight` | `number \| string` | `28` | 单行高度；数字按 px 处理，字符串可传 CSS 长度。 |
| `moreGap` | `number \| string` | `12` | 末行正文与入口之间的间距；数字按 px 处理。 |

正文没有超出折叠行数时会隐藏“更多”入口。`showEllipsis` 只控制视觉上的省略号，不会关闭入口，也不会影响点击后展示全部正文。

## 事件

```ts
interface ShowtimeTwoLineEllipsisMorePayload {
  href: string
  event?: MouseEvent
}
```

| 事件 | 参数 | 说明 |
| --- | --- | --- |
| `more` | `{ href, event }` | 点击默认入口或调用插槽 `openMore` 后触发。 |
| `update:expanded` | `boolean` | 展开状态变化，可配合 `v-model:expanded` 使用。 |

没有溢出时不会触发 `more`。默认 `#` 链接和自定义插槽入口都会在点击后展示完整正文，默认收起入口会紧跟正文末尾，空间不足时自动换行。

## 自定义入口

`more` 是作用域插槽。方法和当前状态会一并传入，适合替换为按钮、路由链接或业务操作入口：

```vue
<ShowtimeTwoLineEllipsis
  text="这是一段需要在折叠行数内展示的长文本。"
  :line-count="3"
  :show-ellipsis="false"
  more-text="展开"
  @more="handleMore"
>
  <template #more="{ text, href, hasOverflow, expanded, lineCount, openMore, closeMore }">
    <button
      v-if="hasOverflow && !expanded"
      type="button"
      :data-href="href"
      :aria-label="`展开 ${lineCount} 行内容`"
      @click="openMore"
    >
      {{ text }}
    </button>
  </template>
</ShowtimeTwoLineEllipsis>
```

| 参数 | 说明 |
| --- | --- |
| `text` | `moreText` 的当前值。 |
| `href` | `moreHref` 的当前值。 |
| `hasOverflow` | 正文是否超出折叠行数。 |
| `expanded` | 当前是否已展示完整正文。 |
| `lineCount` | 当前生效的折叠行数。 |
| `openMore` | 统一的展开和 `more` 事件方法；没有溢出时不会执行。 |
| `closeMore` | 收起完整正文的方法，可在自定义入口中复用。 |

## 自定义收起入口

默认收起按钮会紧跟完整正文末尾，按钮放不下时会自动换到下一行。需要改变入口样式或位置时，使用 `collapse` 插槽；插槽同样会传入 `closeMore`：

```vue
<ShowtimeTwoLineEllipsis v-model:expanded="expanded" text="一段较长的正文。">
  <template #collapse="{ text, closeMore }">
    <button type="button" @click="closeMore">
      {{ text }}
    </button>
  </template>
</ShowtimeTwoLineEllipsis>
```

也可以通过组件模板 ref 调用 `closeMore()`，把收起按钮放在组件外部的业务操作区。

## 排版与动效

- 每一行使用 `Array.from` 拆分，不会把 Emoji 等 Unicode 代理对从中间截断。
- 最后一行会根据默认入口或自定义插槽的实际宽度重新测量，避免入口覆盖正文。
- 容器宽度变化时通过 `ResizeObserver` 重新排版，不可用时回退到 `window.resize`。
- 点击入口立即切换到完整正文，并使用高度过渡承接展开；预览宽度变化时会按展开内容的自然高度重新计算，系统启用“减少动态效果”时会移除高度与按压过渡。
- 默认入口保留键盘焦点样式，自定义插槽的语义和可访问性由插槽内容负责。
