refactor api

This commit is contained in:
Timi
2026-08-27 16:08:56 +08:00
parent 6fa75b3045
commit eef7a78fe9
62 changed files with 835 additions and 2057 deletions
+13
View File
@@ -0,0 +1,13 @@
import view from "./index.vue";
import Toolkit from "../../utils/Toolkit";
export type {
DescriptionGridAlign,
DescriptionGridItem,
DescriptionGridProps
} from "./type";
export const DescriptionGrid = Toolkit.withInstall(view);
export default DescriptionGrid;
+133
View File
@@ -0,0 +1,133 @@
<template>
<div
class="cw-description-grid"
:class="{
bordered,
'mobile-single': singleColumnOnMobile
}"
:style="gridStyle"
>
<template v-for="item in items" :key="item.key">
<div class="cell label" :class="item.labelClass">
<slot :name="`label-${item.key}`" :item="item">
<span v-text="item.label" />
</slot>
</div>
<div class="cell value" :class="item.valueClass">
<slot :name="`value-${item.key}`" :item="item">
<span v-text="formatValue(item.value)" />
</slot>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import { computed } from "vue";
import type { CSSProperties, PropType } from "vue";
import type { DescriptionGridAlign, DescriptionGridItem } from "./type";
defineOptions({
name: "DescriptionGrid"
});
const props = defineProps({
items: {
type: Array as PropType<DescriptionGridItem[]>,
default: () => []
},
columns: {
type: Number,
default: 2
},
emptyText: {
type: String,
default: "-"
},
bordered: {
type: Boolean,
default: true
},
singleColumnOnMobile: {
type: Boolean,
default: true
},
labelAlign: {
type: String as PropType<DescriptionGridAlign>,
default: "left"
},
valueAlign: {
type: String as PropType<DescriptionGridAlign>,
default: "left"
}
});
const normalizedColumns = computed(() => {
if (!props.columns || props.columns < 1) {
return 1;
}
return Math.floor(props.columns);
});
const gridStyle = computed<CSSProperties>(() => {
return {
"--cw-description-columns": String(normalizedColumns.value),
"--cw-description-grid-template": Array
.from({ length: normalizedColumns.value }, () => "max-content minmax(0, 1fr)")
.join(" "),
"--cw-description-label-align": props.labelAlign,
"--cw-description-value-align": props.valueAlign
} as CSSProperties;
});
function formatValue(value: DescriptionGridItem["value"]): string {
if (value === null || value === undefined || value === "") {
return props.emptyText;
}
return String(value);
}
</script>
<style lang="less" scoped>
.cw-description-grid {
display: grid;
max-width: 100%;
grid-template-columns: var(--cw-description-grid-template);
.cell {
min-width: 0;
padding: 12px 16px;
}
.label {
color: var(--t-color-text-placeholder, var(--td-text-color-placeholder));
text-align: var(--cw-description-label-align);
white-space: nowrap;
background: var(--t-color-bg-container-secondary, var(--td-bg-color-secondarycontainer));
}
.value {
text-align: var(--cw-description-value-align);
word-break: break-all;
background: var(--t-color-bg-container, var(--td-bg-color-container));
}
&.bordered {
border-top: 1px solid var(--t-color-border-1, var(--td-component-border));
border-left: 1px solid var(--t-color-border-1, var(--td-component-border));
.cell {
border-right: 1px solid var(--t-color-border-1, var(--td-component-border));
border-bottom: 1px solid var(--t-color-border-1, var(--td-component-border));
}
}
@media (max-width: 48rem) {
&.mobile-single {
grid-template-columns: max-content minmax(0, max-content);
}
}
}
</style>
+33
View File
@@ -0,0 +1,33 @@
export type DescriptionGridAlign = "left" | "center" | "right";
export interface DescriptionGridItem {
/** 唯一标识,同时用于生成具名插槽名称 */
key: string;
/** 标签文本 */
label: string;
/** 默认展示值 */
value?: string | number | boolean | null;
/** 标签单元格自定义类名 */
labelClass?: string;
/** 内容单元格自定义类名 */
valueClass?: string;
}
export interface DescriptionGridProps {
/** 描述项列表 */
items: DescriptionGridItem[];
/** 一行显示几组 key/value */
columns?: number;
/** 空值占位文本 */
emptyText?: string;
/** 是否显示边框 */
bordered?: boolean;
/** 是否在窄屏下自动降为单列 */
singleColumnOnMobile?: boolean;
/** 标签内容对齐方式 */
labelAlign?: DescriptionGridAlign;
/** 值内容对齐方式 */
valueAlign?: DescriptionGridAlign;
}