Build lightweight AI agent admin
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export { default as ZqDesc } from './zq-desc.vue';
|
||||
export { default as ZqDescItem } from './zq-desc-item.vue';
|
||||
@@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'ZqDescItem' });
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label: string;
|
||||
span?: number;
|
||||
}>(),
|
||||
{
|
||||
span: 1,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template></template>
|
||||
@@ -0,0 +1,156 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VNode } from 'vue';
|
||||
|
||||
import { Comment, Fragment, computed, useSlots } from 'vue';
|
||||
|
||||
export interface ZqDescRowItem {
|
||||
label: string;
|
||||
span: number;
|
||||
vnode: VNode;
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
column?: number;
|
||||
}>(),
|
||||
{
|
||||
column: 2,
|
||||
},
|
||||
);
|
||||
|
||||
const slots = useSlots();
|
||||
|
||||
function flattenVNodes(vnodes: VNode[]): VNode[] {
|
||||
const result: VNode[] = [];
|
||||
for (const vnode of vnodes) {
|
||||
if (!vnode || vnode.type === Comment) {
|
||||
continue;
|
||||
}
|
||||
if (vnode.type === Fragment && Array.isArray(vnode.children)) {
|
||||
result.push(...flattenVNodes(vnode.children as VNode[]));
|
||||
continue;
|
||||
}
|
||||
result.push(vnode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function isDescItem(vnode: VNode) {
|
||||
const type = vnode.type as { name?: string; __name?: string } | string;
|
||||
if (typeof type === 'object') {
|
||||
return type.name === 'ZqDescItem' || type.__name === 'ZqDescItem';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedRows = computed(() => {
|
||||
const vnodes = flattenVNodes(slots.default?.() ?? []);
|
||||
const items: ZqDescRowItem[] = vnodes
|
||||
.filter(isDescItem)
|
||||
.map((vnode) => ({
|
||||
label: String(vnode.props?.label ?? ''),
|
||||
span: Math.min(Number(vnode.props?.span ?? 1), props.column),
|
||||
vnode,
|
||||
}));
|
||||
|
||||
const rows: ZqDescRowItem[][] = [];
|
||||
let currentRow: ZqDescRowItem[] = [];
|
||||
let usedColumns = 0;
|
||||
|
||||
for (const item of items) {
|
||||
if (usedColumns + item.span > props.column) {
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow);
|
||||
}
|
||||
currentRow = [];
|
||||
usedColumns = 0;
|
||||
}
|
||||
|
||||
currentRow.push(item);
|
||||
usedColumns += item.span;
|
||||
|
||||
if (usedColumns >= props.column) {
|
||||
rows.push(currentRow);
|
||||
currentRow = [];
|
||||
usedColumns = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow);
|
||||
}
|
||||
|
||||
return rows;
|
||||
});
|
||||
|
||||
function getContentColspan(span: number) {
|
||||
return span * 2 - 1;
|
||||
}
|
||||
|
||||
function renderItemContent(vnode: VNode) {
|
||||
const render = vnode.children as { default?: () => VNode[] } | undefined;
|
||||
return render?.default?.() ?? [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="zq-desc">
|
||||
<table class="zq-desc__table">
|
||||
<tbody>
|
||||
<tr v-for="(row, rowIndex) in parsedRows" :key="rowIndex">
|
||||
<template v-for="(item, itemIndex) in row" :key="itemIndex">
|
||||
<th class="zq-desc__label">{{ item.label }}</th>
|
||||
<td
|
||||
class="zq-desc__content"
|
||||
:colspan="getContentColspan(item.span)"
|
||||
>
|
||||
<component :is="() => renderItemContent(item.vnode)" />
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.zq-desc {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.zq-desc__table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.zq-desc__label {
|
||||
width: 120px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: hsl(var(--muted-foreground));
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
background-color: hsl(var(--background-deep));
|
||||
border-right: 1px solid var(--el-border-color-lighter);
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.zq-desc__content {
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-primary);
|
||||
word-break: break-word;
|
||||
vertical-align: top;
|
||||
background-color: var(--el-bg-color);
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.zq-desc__table tr:last-child .zq-desc__label,
|
||||
.zq-desc__table tr:last-child .zq-desc__content {
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user