Skip to content

Descriptions

数据展示组件,基于 Element Plus Descriptions 封装,支持字典、文件、富文本等多种展示类型。

基础用法

vue
<script setup lang="ts">
import { ref } from "vue";
import { Descriptions, defineDescriptionsConfig } from "vue-admin-kit";

const config = defineDescriptionsConfig([
  {
    title: "基本信息",
    column: 3,
    children: [
      { label: "客户名称", value: "customerName" },
      { label: "联系电话", value: "phone" },
      { label: "状态", value: "status", dictType: "sys_status" },
    ],
  },
]);

const data = ref({
  customerName: "张三",
  phone: "13800138000",
  status: "1",
});
</script>

<template>
  <Descriptions :config="config" :data="data" />
</template>

Props

属性类型默认值说明
configDescriptionSectionConfig[]-配置数组,定义展示的分组
dataRecord<string, unknown>-数据对象,包含要展示的数据

配置类型

DescriptionSectionConfig(分组配置)

属性类型默认值说明
titlestring-分组标题
columnnumber3每行显示列数
childrenDescriptionItemConfig[]-子项配置数组
labelWidthstring'200px'标签宽度
widthnumber500内容宽度
align'left' | 'center' | 'right''left'内容对齐方式
labelAlign'left' | 'center' | 'right''left'标签对齐方式
styleRecord<string, unknown>-自定义样式

DescriptionItemConfig(子项配置)

属性类型说明
labelstring标签文本
valuestring数据字段名
spannumber占用列数,默认 1
type'file' | 'richText'展示类型
dictTypestring字典类型,自动转换为字典标签
optionsDictDataOption[]自定义选项(优先于 dictType)
format(value, row) => string自定义格式化函数
labelWidthstring标签宽度(覆盖分组配置)
align'left' | 'center' | 'right'内容对齐方式(覆盖分组配置)
labelAlign'left' | 'center' | 'right'标签对齐方式(覆盖分组配置)

展示类型

普通文本

默认展示类型,直接显示字段值:

typescript
const config = defineDescriptionsConfig([
  {
    title: "基本信息",
    children: [
      { label: "名称", value: "name" },
      { label: "编码", value: "code" },
      { label: "描述", value: "description" },
    ],
  },
]);

字典类型

自动将字段值转换为字典标签文本:

typescript
const config = defineDescriptionsConfig([
  {
    title: "状态信息",
    children: [
      // 使用系统字典
      { label: "状态", value: "status", dictType: "sys_status" },

      // 使用自定义选项
      {
        label: "类型",
        value: "type",
        options: [
          { label: "类型A", value: "a" },
          { label: "类型B", value: "b" },
        ],
      },
    ],
  },
]);

文件类型

点击后弹出文件列表弹窗,支持预览和下载:

typescript
const config = defineDescriptionsConfig([
  {
    title: "附件信息",
    children: [
      { label: "合同附件", value: "contractFiles", type: "file" },
      { label: "图片资料", value: "imageFiles", type: "file" },
    ],
  },
]);

// 数据格式
const data = {
  contractFiles: [
    { url: "https://example.com/file.pdf", originalName: "合同.pdf" },
    { url: "https://example.com/file2.docx", originalName: "协议.docx" },
  ],
  imageFiles: [
    { url: "https://example.com/img.jpg", originalName: "图片.jpg" },
  ],
};

文件数据格式:

typescript
interface FileItem {
  url: string; // 文件 URL
  name?: string; // 文件名(备选)
  originalName?: string; // 原始文件名(优先)
}

富文本类型

安全渲染 HTML 内容(使用 DOMPurify 过滤):

typescript
const config = defineDescriptionsConfig([
  {
    title: "详细描述",
    column: 1,
    children: [{ label: "内容描述", value: "content", type: "richText" }],
  },
]);

const data = {
  content: "<p>这是一段<strong>富文本</strong>内容</p>",
};

自定义格式化

使用 format 函数自定义显示内容:

typescript
const config = defineDescriptionsConfig([
  {
    title: "金额信息",
    children: [
      // 简单格式化
      {
        label: "金额",
        value: "amount",
        format: (val) => `¥${Number(val).toFixed(2)}`,
      },

      // 访问整行数据
      {
        label: "全名",
        value: "firstName",
        format: (val, row) => `${val} ${row.lastName}`,
      },

      // 日期格式化
      {
        label: "创建时间",
        value: "createTime",
        format: (val) => {
          if (!val) return "-";
          return new Date(val).toLocaleString();
        },
      },

      // 条件显示
      {
        label: "审核状态",
        value: "auditStatus",
        format: (val, row) => {
          if (val === "1") return `已通过(${row.auditor})`;
          if (val === "0") return "待审核";
          return "已拒绝";
        },
      },
    ],
  },
]);

多分组配置

typescript
const config = defineDescriptionsConfig([
  {
    title: "基本信息",
    column: 3,
    children: [
      { label: "客户名称", value: "customerName" },
      { label: "联系电话", value: "phone" },
      { label: "状态", value: "status", dictType: "sys_status" },
      { label: "地址", value: "address", span: 2 },
      { label: "邮编", value: "zipCode" },
    ],
  },
  {
    title: "业务信息",
    column: 2,
    labelWidth: "150px",
    children: [
      { label: "合同金额", value: "contractAmount", format: (v) => `¥${v}` },
      { label: "签约日期", value: "signDate" },
      { label: "合同附件", value: "contractFiles", type: "file" },
      { label: "备注说明", value: "remark", type: "richText" },
    ],
  },
  {
    title: "系统信息",
    column: 4,
    children: [
      { label: "创建人", value: "createBy" },
      { label: "创建时间", value: "createTime" },
      { label: "更新人", value: "updateBy" },
      { label: "更新时间", value: "updateTime" },
    ],
  },
]);

布局配置

列数配置

typescript
// 单列布局
{
  title: "详细描述",
  column: 1,
  children: [
    { label: "描述", value: "description" },
  ],
}

// 两列布局
{
  title: "基本信息",
  column: 2,
  children: [
    { label: "名称", value: "name" },
    { label: "编码", value: "code" },
  ],
}

// 四列布局
{
  title: "系统信息",
  column: 4,
  children: [
    { label: "创建人", value: "createBy" },
    { label: "创建时间", value: "createTime" },
    { label: "更新人", value: "updateBy" },
    { label: "更新时间", value: "updateTime" },
  ],
}

跨列配置

使用 span 属性让某项占用多列:

typescript
{
  title: "基本信息",
  column: 3,
  children: [
    { label: "名称", value: "name" },
    { label: "编码", value: "code" },
    { label: "状态", value: "status" },
    { label: "详细地址", value: "address", span: 3 }, // 占满一行
    { label: "备注", value: "remark", span: 2 },      // 占两列
    { label: "创建时间", value: "createTime" },
  ],
}

对齐方式

typescript
{
  title: "金额信息",
  column: 2,
  align: "right",        // 内容右对齐
  labelAlign: "left",    // 标签左对齐
  children: [
    { label: "合同金额", value: "contractAmount" },
    { label: "已付金额", value: "paidAmount" },
    { label: "待付金额", value: "unpaidAmount", align: "center" }, // 单独设置
  ],
}

宽度配置

typescript
{
  title: "基本信息",
  labelWidth: "120px",   // 标签宽度
  width: 400,            // 内容宽度
  children: [
    { label: "名称", value: "name" },
    { label: "详细描述", value: "description", labelWidth: "100px" }, // 单独设置
  ],
}

加载状态

组件会在 data 为空时自动显示加载状态:

vue
<script setup>
import { ref, onMounted } from "vue";

const data = ref(null);

onMounted(async () => {
  // 加载数据前 data 为 null,显示 loading
  const res = await getDetail(id);
  data.value = res.data;
});
</script>

<template>
  <Descriptions :config="config" :data="data" />
</template>

在详情弹窗中使用

vue
<script setup>
import {
  useState,
  PageTemplate,
  Table,
  Descriptions,
  defineDescriptionsConfig,
} from "vue-admin-kit";

const detailConfig = defineDescriptionsConfig([
  {
    title: "用户信息",
    column: 2,
    children: [
      { label: "用户名", value: "userName" },
      { label: "昵称", value: "nickName" },
      { label: "手机号", value: "phone" },
      { label: "邮箱", value: "email" },
      { label: "状态", value: "status", dictType: "sys_status" },
      { label: "创建时间", value: "createTime" },
    ],
  },
]);

useState({
  api: { list: listUser, detail: getUser },
  // ...
});
</script>

<template>
  <PageTemplate ref="pageTemplateRef">
    <template #table>
      <Table />
    </template>

    <!-- 自定义详情内容 -->
    <template #detail="{ detailData }">
      <Descriptions :config="detailConfig" :data="detailData" />
    </template>
  </PageTemplate>
</template>

完整示例

vue
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { Descriptions, defineDescriptionsConfig } from "vue-admin-kit";
import { getOrderDetail } from "@/api/order";

const config = defineDescriptionsConfig([
  {
    title: "订单信息",
    column: 3,
    children: [
      { label: "订单号", value: "orderNo" },
      { label: "订单状态", value: "status", dictType: "order_status" },
      { label: "下单时间", value: "createTime" },
      { label: "客户名称", value: "customerName", span: 2 },
      { label: "联系电话", value: "phone" },
    ],
  },
  {
    title: "金额信息",
    column: 4,
    align: "right",
    children: [
      {
        label: "商品金额",
        value: "goodsAmount",
        format: (v) => `¥${v?.toFixed(2)}`,
      },
      { label: "运费", value: "freight", format: (v) => `¥${v?.toFixed(2)}` },
      {
        label: "优惠金额",
        value: "discountAmount",
        format: (v) => `-¥${v?.toFixed(2)}`,
      },
      {
        label: "实付金额",
        value: "payAmount",
        format: (v) => `¥${v?.toFixed(2)}`,
      },
    ],
  },
  {
    title: "收货信息",
    column: 1,
    children: [
      { label: "收货人", value: "receiverName" },
      { label: "收货电话", value: "receiverPhone" },
      {
        label: "收货地址",
        value: "province",
        format: (_, row) =>
          `${row.province}${row.city}${row.district}${row.address}`,
      },
    ],
  },
  {
    title: "附件信息",
    column: 2,
    children: [
      { label: "合同附件", value: "contractFiles", type: "file" },
      { label: "发票附件", value: "invoiceFiles", type: "file" },
    ],
  },
  {
    title: "备注",
    column: 1,
    children: [{ label: "订单备注", value: "remark", type: "richText" }],
  },
]);

const data = ref(null);

onMounted(async () => {
  const res = await getOrderDetail(orderId);
  data.value = res.data;
});
</script>

<template>
  <div class="order-detail">
    <Descriptions :config="config" :data="data" />
  </div>
</template>

Released under the MIT License.