feat(card-list): 添加字段列数配置支持多列布局
Some checks failed
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled

- 新增 fieldColumns 配置项控制卡片内字段排列列数
- 支持通过 colSpan 设置字段跨列显示
- 优化卡片内容布局样式和字段显示效果
- 调整对账模块相关页面布局
This commit is contained in:
2026-01-07 14:41:52 +08:00
parent 75111681b4
commit ccfa4b5f15
9 changed files with 84 additions and 22 deletions

View File

@@ -1,7 +1,7 @@
VITE_BASE=/ VITE_BASE=/
# 接口地址 # 接口地址
VITE_GLOB_API_URL=http://192.168.110.100:9000/finance VITE_GLOB_API_URL=http://59.110.212.44:9000/finance
# 是否开启压缩,可以设置为 none, brotli, gzip # 是否开启压缩,可以设置为 none, brotli, gzip
VITE_COMPRESS=none VITE_COMPRESS=none

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { defineComponent, ref, watch } from 'vue'; import { defineComponent, ref, watch } from 'vue';
import { Button, Divider, Input, Select, Space } from 'ant-design-vue'; import { Button, Divider, Input, Select } from 'ant-design-vue';
export interface RemoteOption { export interface RemoteOption {
label: string; label: string;
@@ -70,15 +70,20 @@ const addItem = (e: Event) => {
<template #dropdownRender="{ menuNode: menu }"> <template #dropdownRender="{ menuNode: menu }">
<VNodes :vnodes="menu" /> <VNodes :vnodes="menu" />
<Divider style="margin: 4px 0" /> <Divider style="margin: 4px 0" />
<Space style="padding: 4px 8px"> <div style="padding: 4px 8px; display: flex">
<Input ref="inputRef" v-model:value="inputValue" placeholder="在此手动输入" /> <Input
ref="inputRef"
v-model:value="inputValue"
placeholder="在此手动输入"
style="flex: 1"
/>
<Button type="text" @click="addItem"> <Button type="text" @click="addItem">
<template #icon> <template #icon>
<!-- <PlusOutlined /> --> <!-- <PlusOutlined /> -->
</template> </template>
新增 新增
</Button> </Button>
</Space> </div>
</template> </template>
</Select> </Select>
</template> </template>

View File

@@ -13,6 +13,7 @@ function getDefaultState(): CardListProps {
data: [], data: [],
showTitle: true, showTitle: true,
gridColumns: 3, gridColumns: 3,
fieldColumns: 1,
pagerConfig: { pagerConfig: {
enabled: true, enabled: true,
current: 1, current: 1,

View File

@@ -73,6 +73,40 @@ const cardStyle = (item: any) => {
return style; return style;
}; };
// 卡片内容样式
const cardContentStyle = computed(() => {
const fieldColumns = gridOptions.value?.fieldColumns || 1;
if (fieldColumns > 1) {
return {
display: 'grid',
gridTemplateColumns: `repeat(${fieldColumns}, 1fr)`,
columnGap: '10px', // 控制列间距
rowGap: '8px', // 控制行间距,设置更小的值
};
}
return {};
});
// 卡片字段样式
const cardFieldStyle = (column: any) => {
const fieldColumns = gridOptions.value?.fieldColumns || 1;
const colSpan = column?.colSpan || 1;
if (fieldColumns > 1) {
return {
marginBottom: '0px',
paddingBottom: '0px',
height: 'auto',
lineHeight: 'normal',
gridColumn: `span ${colSpan}`,
};
}
return {};
};
// const errorCardStyle = (item: any) => { // const errorCardStyle = (item: any) => {
// const errorData = gridOptions.value?.errorData || []; // const errorData = gridOptions.value?.errorData || [];
// return errorData.some((errorItem) => errorItem.id === item.id) // return errorData.some((errorItem) => errorItem.id === item.id)
@@ -234,11 +268,12 @@ onUnmounted(() => {
<slot name="card-extra" :row="item" :index="index"></slot> <slot name="card-extra" :row="item" :index="index"></slot>
</template> </template>
<div class="card-content"> <div class="card-content" :style="cardContentStyle">
<div <div
v-for="column in gridOptions?.columns" v-for="column in gridOptions?.columns"
:key="column?.field || ''" :key="column?.field || ''"
class="card-field" class="card-field"
:style="cardFieldStyle(column)"
v-show=" v-show="
!column?.show || !column?.show ||
(typeof column.show === 'function' ? column.show(item) : column.show) (typeof column.show === 'function' ? column.show(item) : column.show)
@@ -290,27 +325,26 @@ onUnmounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.card-content { .card-content {
.card-field { .card-field {
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
.field-item { .field-item {
display: flex; display: flex;
align-items: center; align-items: center;
flex-wrap: nowrap;
.field-title { .field-title {
flex-shrink: 0; flex-shrink: 0;
margin-right: 8px; margin-right: 8px;
font-weight: 500; font-weight: 500;
color: rgba(0, 0, 0, 0.85); color: rgba(0, 0, 0, 0.85);
white-space: nowrap;
} }
.field-value { .field-value {
flex: 1; flex: 1;
min-width: 0;
word-break: break-all; word-break: break-all;
color: rgba(0, 0, 0, 0.65); color: rgba(0, 0, 0, 0.65);
overflow: hidden;
text-overflow: ellipsis;
&.field-edit { &.field-edit {
display: flex; display: flex;
@@ -349,7 +383,6 @@ onUnmounted(() => {
} }
.card-content { .card-content {
flex: 1;
overflow-y: auto; overflow-y: auto;
min-width: 0; // 防止内容撑开 min-width: 0; // 防止内容撑开
} }

View File

@@ -17,6 +17,8 @@ export interface CardListColumn<T = any> {
show?: ((row: T) => boolean) | boolean; show?: ((row: T) => boolean) | boolean;
/** 编辑渲染组件 */ /** 编辑渲染组件 */
editRender?: { name: string; props?: Record<string, any> }; editRender?: { name: string; props?: Record<string, any> };
/** 字段占列数 */
colSpan?: number;
} }
export interface CardListPagination { export interface CardListPagination {
@@ -49,6 +51,8 @@ export interface CardListOptions<T = any> {
cardClass?: string; cardClass?: string;
/** 网格列数 */ /** 网格列数 */
gridColumns?: number; gridColumns?: number;
/** 字段列数,控制卡片内字段排列的列数 */
fieldColumns?: number;
/** 网格高度 */ /** 网格高度 */
gridHeight?: string; gridHeight?: string;
/** 分页配置 */ /** 分页配置 */

View File

@@ -21,11 +21,11 @@ const routes: RouteRecordRaw[] = [
}, },
// { // {
// meta: { // meta: {
// title: '用户管理2', // title: '测试',
// }, // },
// name: 'Users2', // name: 'Test',
// path: '/system/users2', // path: '/system/test',
// component: () => import('#/components/CardListDemo.vue'), // component: () => import('#/views/test.vue'),
// }, // },
], ],
}, },

View File

@@ -87,6 +87,7 @@ const gridOptions = computed(() => ({
}, },
field: 'tel', field: 'tel',
title: '用户手机号', title: '用户手机号',
colSpan: 2,
}, },
{ editRender: { name: 'Input' }, field: 'orderMoney', title: '订单金额' }, { editRender: { name: 'Input' }, field: 'orderMoney', title: '订单金额' },
{ editRender: { name: 'Input' }, field: 'realMoney', title: '实际金额' }, { editRender: { name: 'Input' }, field: 'realMoney', title: '实际金额' },
@@ -107,6 +108,8 @@ const gridOptions = computed(() => ({
name: 'Select', name: 'Select',
props: { props: {
options: [ options: [
{ label: '1个月', value: '1' },
{ label: '3个月', value: '3' },
{ label: '半年', value: '6' }, { label: '半年', value: '6' },
{ label: '一年', value: '12' }, { label: '一年', value: '12' },
{ label: '两年', value: '24' }, { label: '两年', value: '24' },
@@ -123,9 +126,10 @@ const gridOptions = computed(() => ({
], ],
showTitle: true, showTitle: true,
titleField: 'productName', titleField: 'productName',
gridColumns: 4, gridColumns: 3,
gridHeight: '355px', fieldColumns: 2,
cardHeight: '345px', gridHeight: '220px',
cardHeight: '210px',
})); }));
const [Grid, gridApi] = useCardList<CreateOrderType>({ const [Grid, gridApi] = useCardList<CreateOrderType>({

View File

@@ -266,7 +266,7 @@ function onCompleteCheckCreated() {
</div> </div>
</div> </div>
</Card> </Card>
<div class="flex flex-1 flex-col gap-2"> <div class="flex w-3/4 flex-1 flex-col gap-2">
<Card <Card
:tab-list="tabList" :tab-list="tabList"
:active-tab-key="activeTabKey" :active-tab-key="activeTabKey"
@@ -293,7 +293,7 @@ function onCompleteCheckCreated() {
<Card <Card
v-if="activeTabKey !== '0'" v-if="activeTabKey !== '0'"
:title="`已选列表(${selectedData.length})`" :title="`已选列表(${selectedData.length})`"
class="h-[405px] w-full" class="h-[270px] w-full"
size="small" size="small"
> >
<template #extra> <template #extra>
@@ -322,6 +322,7 @@ function onCompleteCheckCreated() {
padding: 5px !important; padding: 5px !important;
} }
.order-card { .order-card {
width: 100%;
:deep(.ant-card-body) { :deep(.ant-card-body) {
padding: 1px !important; padding: 1px !important;
height: calc(100% - 46px); // 减去标签页头部高度 height: calc(100% - 46px); // 减去标签页头部高度

View File

@@ -0,0 +1,14 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { TabPane, Tabs } from 'ant-design-vue';
const activeKey = ref(1);
</script>
<template>
<div>
<Tabs v-model:active-key="activeKey" :style="{ height: '200px' }">
<TabPane v-for="i in 30" :key="i" :tab="`Tab-${i}`">Content of tab {{ i }}</TabPane>
</Tabs>
</div>
</template>