feat: 优化表格列设置
parent
85b781d946
commit
1f7c1a95b3
|
|
@ -8,8 +8,9 @@ import {
|
|||
Button,
|
||||
PaginationProps,
|
||||
} from '@arco-design/web-vue';
|
||||
import { isObject, merge } from 'lodash-es';
|
||||
import { merge } from 'lodash-es';
|
||||
import { PropType, defineComponent, ref } from 'vue';
|
||||
import { TableColumnConfig } from './TableColumnConfig';
|
||||
|
||||
type DataFn = (filter: { page: number; size: number; [key: string]: any }) => any | Promise<any>;
|
||||
|
||||
|
|
@ -158,13 +159,15 @@ export const AnTable = defineComponent({
|
|||
return (
|
||||
<div class="table w-full">
|
||||
<div class={`mb-3 flex toolbar justify-between`}>
|
||||
<div class={`flex-1 flex gap-2 `}>TODO</div>
|
||||
<div class={`flex-1 flex gap-2 `}>
|
||||
<Button type='primary'>{{ icon: () => <i class="icon-park-outline-add"></i>, default: () => '新增' }}</Button>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex gap-1">
|
||||
<Button disabled={this.loading} onClick={this.loadData}>
|
||||
<Button loading={this.loading} onClick={this.loadData}>
|
||||
{{ icon: () => <span class="icon-park-outline-redo"></span> }}
|
||||
</Button>
|
||||
<Button>{{ icon: () => <span class="icon-park-outline-config"></span> }}</Button>
|
||||
<TableColumnConfig columns={this.columns} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
import { Button, Checkbox, Divider, InputNumber, Popover, Scrollbar, Tag } from '@arco-design/web-vue';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
interface Item {
|
||||
dataIndex: string;
|
||||
enable: boolean;
|
||||
autoWidth: boolean;
|
||||
width: number;
|
||||
editable: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const TableColumnConfig = defineComponent({
|
||||
props: {
|
||||
columns: {
|
||||
type: Object as PropType<any[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const checkAll = ref(false);
|
||||
const visible = ref(false);
|
||||
const items = ref<Item[]>([]);
|
||||
const checked = computed(() => items.value.filter(i => i.enable));
|
||||
const indeterminate = computed(() => {
|
||||
const check = checked.value.length;
|
||||
const total = items.value.length;
|
||||
return 0 < check && check < total;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => visible.value,
|
||||
value => {
|
||||
if (value) {
|
||||
init();
|
||||
} else {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const init = () => {
|
||||
const list: Item[] = [];
|
||||
for (const column of props.columns) {
|
||||
list.push({
|
||||
dataIndex: column.dataIndex,
|
||||
title: column.title,
|
||||
enable: true,
|
||||
autoWidth: !column.width,
|
||||
width: column.width ?? 60,
|
||||
editable: !column.configable,
|
||||
});
|
||||
}
|
||||
items.value = list;
|
||||
};
|
||||
|
||||
const onItemChange = () => {
|
||||
if (checked.value.length === 0) {
|
||||
checkAll.value = false;
|
||||
return;
|
||||
}
|
||||
if (checked.value.length === items.value.length) {
|
||||
checkAll.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onCheckAllChange = (value: any) => {
|
||||
for (const item of items.value) {
|
||||
if (item.editable) {
|
||||
item.enable = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onItemUp = (index: number) => {
|
||||
[items.value[index - 1], items.value[index]] = [items.value[index], items.value[index - 1]];
|
||||
};
|
||||
|
||||
const onItemDown = (index: number) => {
|
||||
[items.value[index + 1], items.value[index]] = [items.value[index], items.value[index + 1]];
|
||||
};
|
||||
|
||||
return () => (
|
||||
<Popover v-model:popup-visible={visible.value} position="br" trigger="click">
|
||||
{{
|
||||
default: () => (
|
||||
<Button class="float-right">{{ icon: () => <span class="icon-park-outline-config"></span> }}</Button>
|
||||
),
|
||||
content: () => (
|
||||
<>
|
||||
<div class="mb-1 leading-none border-b border-gray-100 pb-3">设置表格列</div>
|
||||
<Scrollbar outer-class="h-96 overflow-hidden" class="h-full overflow-auto">
|
||||
<ul class="grid m-0 p-0 divide-y divide-gray-100 w-[700px] overflow-auto overscroll-contain">
|
||||
{items.value.map((item, index) => (
|
||||
<li
|
||||
key={item.dataIndex}
|
||||
class="group flex items-center justify-between gap-6 py-2 pr-8 select-none"
|
||||
>
|
||||
<div class="flex-1 flex justify-between gap-2">
|
||||
<Checkbox v-model={item.enable} disabled={!item.editable} onChange={onItemChange}>
|
||||
{item.title}
|
||||
</Checkbox>
|
||||
<span class="hidden group-hover:inline-block ml-4">
|
||||
<Button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="mini"
|
||||
disabled={index == 0}
|
||||
onClick={() => onItemUp(index)}
|
||||
>
|
||||
{{ icon: () => <i class="icon-park-outline-arrow-up"></i> }}
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="mini"
|
||||
disabled={index == items.value.length - 1}
|
||||
onClick={() => onItemDown(index)}
|
||||
>
|
||||
{{ icon: () => <i class="icon-park-outline-arrow-down"></i> }}
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<Checkbox v-model={item.autoWidth} disabled={!item.editable}>
|
||||
{{
|
||||
checkbox: ({ checked }: any) => (
|
||||
<Tag checked={checked} checkable={item.editable} color="blue">
|
||||
自适应
|
||||
</Tag>
|
||||
),
|
||||
}}
|
||||
</Checkbox>
|
||||
<Divider direction="vertical" margin={8}></Divider>
|
||||
<InputNumber
|
||||
size="small"
|
||||
v-model={item.width}
|
||||
disabled={item.autoWidth || !item.editable}
|
||||
min={60}
|
||||
step={10}
|
||||
class="!w-20"
|
||||
/>
|
||||
<span class="text-gray-400">像素</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Scrollbar>
|
||||
<div class="mt-4 flex gap-2 items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Checkbox indeterminate={indeterminate.value} v-model={checkAll.value} onChange={onCheckAllChange}>
|
||||
全选
|
||||
</Checkbox>
|
||||
<span class="text-xs text-gray-400 ml-1">
|
||||
({checked.value.length}/{items.value.length})
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-x-2">
|
||||
<Button onClick={onReset}>重置</Button>
|
||||
<Button type="primary" onClick={onConfirm}>
|
||||
确定
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
@ -85,7 +85,16 @@ interface TableDropdownColumn {
|
|||
}
|
||||
|
||||
export type TableColumn = TableColumnData &
|
||||
(TableIndexColumn | TableBaseColumn | TableButtonColumn | TableDropdownColumn);
|
||||
(TableIndexColumn | TableBaseColumn | TableButtonColumn | TableDropdownColumn) & {
|
||||
/**
|
||||
* 是否可配置
|
||||
* @example
|
||||
* ```ts
|
||||
* true
|
||||
* ```
|
||||
*/
|
||||
configable?: boolean;
|
||||
};
|
||||
|
||||
export function useTableColumns(data: TableColumn[]) {
|
||||
const columns = ref<TableColumnData[]>([]);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<a-popover position="br" trigger="click">
|
||||
<a-popover v-model:popup-visible="visible" position="br" trigger="click">
|
||||
<a-button class="float-right">设置</a-button>
|
||||
<template #content>
|
||||
<div class="mb-1 leading-none border-b border-gray-100 pb-3">设置表格列</div>
|
||||
|
|
@ -11,24 +11,24 @@
|
|||
class="group flex items-center justify-between py-2 pr-8 select-none"
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<a-checkbox v-model="item.enable" :disabled="item.disable" size="large" @change="onItemChange">
|
||||
<a-checkbox v-model="item.enable" :disabled="!item.editable" size="large" @change="onItemChange">
|
||||
{{ item.dataIndex }}
|
||||
</a-checkbox>
|
||||
<span class="hidden group-hover:inline-block ml-4">
|
||||
<i v-show="!item.disable" class="icon-park-outline-drag cursor-move"></i>
|
||||
<i v-show="!item.editable" class="icon-park-outline-drag cursor-move"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<a-checkbox v-model="item.autoWidth" :disabled="item.disable">
|
||||
<a-checkbox v-model="item.autoWidth" :disabled="!item.editable">
|
||||
<template #checkbox="{ checked }">
|
||||
<a-tag :checked="checked" :checkable="!item.disable" color="blue">自适应</a-tag>
|
||||
<a-tag :checked="checked" :checkable="item.editable" color="blue">自适应</a-tag>
|
||||
</template>
|
||||
</a-checkbox>
|
||||
<a-divider direction="vertical" :margin="8"></a-divider>
|
||||
<a-input-number
|
||||
size="small"
|
||||
v-model="item.width"
|
||||
:disabled="item.autoWidth || item.disable"
|
||||
:disabled="item.autoWidth || !item.editable"
|
||||
:min="60"
|
||||
:step="10"
|
||||
class="!w-20"
|
||||
|
|
@ -40,20 +40,14 @@
|
|||
</a-scrollbar>
|
||||
<div class="mt-4 flex gap-2 items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<a-checkbox
|
||||
:indeterminate="indeterminate"
|
||||
v-model="checkAll"
|
||||
@change="(v: any) => items.forEach(i => i.enable = v)"
|
||||
>
|
||||
全选
|
||||
</a-checkbox>
|
||||
<a-checkbox :indeterminate="indeterminate" v-model="checkAll" @change="onCheckAllChange"> 全选 </a-checkbox>
|
||||
<span class="text-xs text-gray-400 ml-1">
|
||||
({{ items.filter(i => i.enable).length }}/{{ items.length }})
|
||||
</span>
|
||||
</div>
|
||||
<div class="space-x-2">
|
||||
<a-button>重置</a-button>
|
||||
<a-button type="primary">确定</a-button>
|
||||
<a-button @click="onReset">重置</a-button>
|
||||
<a-button type="primary" @click="onConfirm">确定</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -63,46 +57,48 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const items = ref(
|
||||
Array(10)
|
||||
.fill(0)
|
||||
.map((v, i) => {
|
||||
return {
|
||||
dataIndex: `测试${i}`,
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
disable: false,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
items.value.unshift({
|
||||
dataIndex: '顺序',
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
disable: true,
|
||||
});
|
||||
|
||||
items.value.push({
|
||||
dataIndex: '操作',
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
disable: true,
|
||||
});
|
||||
interface Item {
|
||||
dataIndex: string;
|
||||
enable: boolean;
|
||||
autoWidth: boolean;
|
||||
width: number;
|
||||
editable: boolean;
|
||||
}
|
||||
|
||||
const checkAll = ref(false);
|
||||
|
||||
const visible = ref(false);
|
||||
const items = ref<Item[]>([]);
|
||||
const checked = computed(() => items.value.filter(i => i.enable));
|
||||
const indeterminate = computed(() => {
|
||||
const check = checked.value.length;
|
||||
const total = items.value.length;
|
||||
return 0 < check && check < total;
|
||||
});
|
||||
|
||||
const checked = computed(() => {
|
||||
return items.value.filter(i => i.enable);
|
||||
onMounted(() => {
|
||||
items.value.push({
|
||||
dataIndex: '顺序',
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
editable: false,
|
||||
});
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
items.value.push({
|
||||
dataIndex: `测试${i}`,
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
editable: true,
|
||||
});
|
||||
}
|
||||
items.value.push({
|
||||
dataIndex: '操作',
|
||||
enable: true,
|
||||
autoWidth: false,
|
||||
width: 80,
|
||||
editable: false,
|
||||
});
|
||||
});
|
||||
|
||||
const onItemChange = () => {
|
||||
|
|
@ -114,6 +110,22 @@ const onItemChange = () => {
|
|||
checkAll.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onCheckAllChange = (value: any) => {
|
||||
for (const item of items.value) {
|
||||
if (item.editable) {
|
||||
item.enable = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const onConfirm = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div class="m-4 bg-white p-4">
|
||||
<div class="border-2 border-green-500 px-2 w-40 text-3xl text-green-500 mb-4">AR K056</div>
|
||||
<column-configer></column-configer>
|
||||
<user-table></user-table>
|
||||
<div>{{ formatModel(emodel) }}</div>
|
||||
<UpForm />
|
||||
|
|
@ -10,10 +9,8 @@
|
|||
|
||||
<script setup lang="tsx">
|
||||
import { api } from '@/api';
|
||||
import { useForm } from '@/components/AnForm';
|
||||
import { formatModel } from '@/components/AnForm';
|
||||
import { formatModel, useForm } from '@/components/AnForm';
|
||||
import { useTable } from '@/components/AnTable';
|
||||
import ColumnConfiger from './components/ColumnConfiger.vue';
|
||||
|
||||
const { component: UserTable } = useTable({
|
||||
data(search) {
|
||||
|
|
@ -26,15 +23,49 @@ const { component: UserTable } = useTable({
|
|||
{
|
||||
dataIndex: 'id',
|
||||
title: 'ID',
|
||||
configable: false,
|
||||
},
|
||||
{
|
||||
dataIndex: 'nickname',
|
||||
title: '用户名称',
|
||||
},
|
||||
// {
|
||||
// dataIndex: 'description',
|
||||
// title: '用户描述',
|
||||
// },
|
||||
{
|
||||
dataIndex: 'username',
|
||||
title: '登录账号',
|
||||
},
|
||||
{
|
||||
dataIndex: 'email',
|
||||
title: '邮箱',
|
||||
},
|
||||
{
|
||||
dataIndex: 'phone',
|
||||
title: '手机号码',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createdBy',
|
||||
title: '创建人',
|
||||
},
|
||||
{
|
||||
dataIndex: 'createdAt',
|
||||
title: '创建时间',
|
||||
},
|
||||
{
|
||||
dataIndex: 'updatedBy',
|
||||
title: '更新人',
|
||||
},
|
||||
{
|
||||
dataIndex: 'updatedAt',
|
||||
title: '更新时间',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
type: 'button',
|
||||
width: 140,
|
||||
configable: false,
|
||||
buttons: [
|
||||
{
|
||||
text: '修改',
|
||||
|
|
|
|||
|
|
@ -7,27 +7,19 @@ export {}
|
|||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
AAlert: typeof import('@arco-design/web-vue')['Alert']
|
||||
AAutoComplete: typeof import('@arco-design/web-vue')['AutoComplete']
|
||||
AAvatar: typeof import('@arco-design/web-vue')['Avatar']
|
||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||
ACard: typeof import('@arco-design/web-vue')['Card']
|
||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||
ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup']
|
||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||
ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
|
||||
ADivider: typeof import('@arco-design/web-vue')['Divider']
|
||||
ADoption: typeof import('@arco-design/web-vue')['Doption']
|
||||
ADrawer: typeof import('@arco-design/web-vue')['Drawer']
|
||||
ADropdown: typeof import('@arco-design/web-vue')['Dropdown']
|
||||
ADropdownButton: typeof import('@arco-design/web-vue')['DropdownButton']
|
||||
AEmpty: typeof import('@arco-design/web-vue')['Empty']
|
||||
AForm: typeof import('@arco-design/web-vue')['Form']
|
||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||
AImage: typeof import('@arco-design/web-vue')['Image']
|
||||
AImagePreview: typeof import('@arco-design/web-vue')['ImagePreview']
|
||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||
|
|
@ -37,31 +29,15 @@ declare module '@vue/runtime-core' {
|
|||
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||
AList: typeof import('@arco-design/web-vue')['List']
|
||||
AListItem: typeof import('@arco-design/web-vue')['ListItem']
|
||||
AListItemMeta: typeof import('@arco-design/web-vue')['ListItemMeta']
|
||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||
AniEmpty: typeof import('./../components/empty/AniEmpty.vue')['default']
|
||||
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||
AProgress: typeof import('@arco-design/web-vue')['Progress']
|
||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||
ARadioGroup: typeof import('@arco-design/web-vue')['RadioGroup']
|
||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||
ATable: typeof import('@arco-design/web-vue')['Table']
|
||||
ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
|
||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||
ATree: typeof import('@arco-design/web-vue')['Tree']
|
||||
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
||||
BaseOption: typeof import('./../components/editor/components/BaseOption.vue')['default']
|
||||
BreadCrumb: typeof import('./../components/breadcrumb/bread-crumb.vue')['default']
|
||||
|
|
|
|||
Loading…
Reference in New Issue