feat: 添加虚拟表格示例 (#1007)

* feat: 添加虚拟表格示例
This commit is contained in:
xiaoming
2024-03-20 15:00:47 +08:00
committed by GitHub
parent f0a80c680e
commit 2367eedc5d
78 changed files with 16678 additions and 14 deletions

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import { tableData } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table :data="tableData" :columns="columns" />
</template>

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import { tableData } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table :data="tableData" :columns="columns" border />
</template>

View File

@@ -0,0 +1,72 @@
import { message } from "@/utils/message";
import { tableData } from "../data";
// 如果您不习惯tsx写法可以传slot然后在template里写
// 需是hooks写法函数中有return避免失去响应性
export function useColumns() {
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
cellRenderer: ({ row }) => (
<div style="display: flex; align-items: center">
<iconify-icon-online icon="ep:timer" />
<span style="margin-left: 10px">{row.date}</span>
</div>
)
},
{
label: "姓名",
prop: "name",
cellRenderer: ({ row }) => (
<el-popover effect="light" trigger="hover" placement="top" width="auto">
{{
default: () => (
<>
<div>name: {row.name}</div>
<div>address: {row.address}</div>
</>
),
reference: () => <el-tag>{row.name}</el-tag>
}}
</el-popover>
)
},
{
label: "地址",
prop: "address"
},
{
label: "操作",
cellRenderer: ({ index, row }) => (
<>
<el-button size="small" onClick={() => handleEdit(index + 1, row)}>
Edit
</el-button>
<el-button
size="small"
type="danger"
onClick={() => handleDelete(index + 1, row)}
>
Delete
</el-button>
</>
)
}
];
const handleEdit = (index: number, row) => {
message(`您修改了第 ${index} 行,数据为:${JSON.stringify(row)}`, {
type: "success"
});
};
const handleDelete = (index: number, row) => {
message(`您删除了第 ${index} 行,数据为:${JSON.stringify(row)}`);
};
return {
columns,
tableData
};
}

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, tableData } = useColumns();
</script>
<template>
<pure-table :data="tableData" :columns="columns" />
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import { tableData } from "./data";
const indexMethod = (index: number) => {
return index * 2;
};
const columns: TableColumnList = [
{
type: "index",
index: indexMethod
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table :data="tableData" :columns="columns" />
</template>

View File

@@ -0,0 +1,297 @@
import dayjs from "dayjs";
import { clone } from "@pureadmin/utils";
const date = dayjs(new Date()).format("YYYY-MM-DD");
const tableData = [
{
date,
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Jack",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Dick",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Harry",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Sam",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Lucy",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mary",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mike",
address: "No. 189, Grove St, Los Angeles"
}
];
const cloneData = clone(tableData, true);
const tableDataMore = cloneData.map(item =>
Object.assign(item, {
state: "California",
city: "Los Angeles",
"post-code": "CA 90036"
})
);
const tableDataImage = cloneData.map((item, index) =>
Object.assign(item, {
image: `https://pure-admin.github.io/pure-admin-table/imgs/${index + 1}.jpg`
})
);
const tableDataSortable = cloneData.map((item, index) =>
Object.assign(item, {
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`
})
);
const tableDataExpand = [
{
date: "2016-05-03",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-02",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-04",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-01",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-08",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-06",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
},
{
date: "2016-05-07",
name: "Tom",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114",
family: [
{
name: "Jerry",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Spike",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
},
{
name: "Tyke",
state: "California",
city: "San Francisco",
address: "3650 21st St, San Francisco",
zip: "CA 94114"
}
]
}
];
export {
tableData,
tableDataMore,
tableDataImage,
tableDataExpand,
tableDataSortable
};

View File

@@ -0,0 +1,72 @@
<script setup lang="ts">
import { ref } from "vue";
import { tableDataExpand } from "./data";
const parentBorder = ref(false);
const childBorder = ref(false);
const columns: TableColumnList = [
{
type: "expand",
slot: "expand"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
];
const childColumns: TableColumnList = [
{
label: "Name",
prop: "name"
},
{
label: "State",
prop: "state"
},
{
label: "City",
prop: "city"
},
{
label: "Address",
prop: "address"
},
{
label: "Zip",
prop: "zip"
}
];
</script>
<template>
<div>
switch parent border: <el-switch v-model="parentBorder" /> switch child
border: <el-switch v-model="childBorder" />
<pure-table
:data="tableDataExpand"
:columns="columns"
:border="parentBorder"
>
<template #expand="{ row }">
<div class="m-4">
<p class="mb-2">State: {{ row.state }}</p>
<p class="mb-2">City: {{ row.city }}</p>
<p class="mb-2">Address: {{ row.address }}</p>
<p class="mb-4">Zip: {{ row.zip }}</p>
<h3>Family</h3>
<pure-table
:data="row.family"
:columns="childColumns"
:border="childBorder"
/>
</div>
</template>
</pure-table>
</div>
</template>

View File

@@ -0,0 +1,107 @@
<script setup lang="ts">
import { ref } from "vue";
const tableRef = ref();
const clearFilter = val => {
const { clearFilter } = tableRef.value.getTableRef();
clearFilter(val);
};
const resetDateFilter = () => {
clearFilter(["date"]);
};
const filterTag = (value, row) => {
return row.tag === value;
};
const filterHandler = (value, row, column) => {
const property = column["property"];
return row[property] === value;
};
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
sortable: true,
columnKey: "date",
filters: [
{ text: "2016-05-01", value: "2016-05-01" },
{ text: "2016-05-02", value: "2016-05-02" },
{ text: "2016-05-03", value: "2016-05-03" },
{ text: "2016-05-04", value: "2016-05-04" }
],
filterMethod: filterHandler
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
},
{
label: "标签",
prop: "tag",
filters: [
{ text: "Home", value: "Home" },
{ text: "Office", value: "Office" }
],
filterMethod: filterTag,
filterPlacement: "bottom-end",
slot: "tag"
}
];
const tableData = [
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
tag: "Home"
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
tag: "Office"
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
tag: "Home"
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
tag: "Office"
}
];
</script>
<template>
<div>
<el-button @click="resetDateFilter">reset date filter</el-button>
<el-button @click="clearFilter">reset all filters</el-button>
<pure-table
ref="tableRef"
row-key="date"
:data="tableData"
:columns="columns"
>
<template #tag="{ row }">
<el-tag
:type="row.tag === 'Home' ? null : 'success'"
disable-transitions
>
{{ row.tag }}
</el-tag>
</template>
</pure-table>
</div>
</template>

View File

@@ -0,0 +1,79 @@
<script setup lang="ts">
import { tableDataMore } from "./data";
const props = withDefaults(
defineProps<{
height?: string;
}>(),
{
height: null
}
);
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
width: "260",
fixed: true
},
{
label: "姓名",
prop: "name",
width: "260"
},
{
label: "地区",
prop: "state",
width: "260"
},
{
label: "城市",
prop: "city",
width: "260"
},
{
label: "地址",
prop: "address",
width: "260"
},
{
label: "邮编",
prop: "post-code",
width: "260"
},
{
label: "操作",
width: "120",
fixed: "right",
slot: "operation"
}
];
function handleClick(row) {
console.log(
"%crow===>>>: ",
"color: MidnightBlue; background: Aquamarine; font-size: 20px;",
row
);
}
</script>
<template>
<pure-table
:data="
props.height
? tableDataMore.concat(tableDataMore).concat(tableDataMore)
: tableDataMore
"
:columns="columns"
:height="props.height"
>
<template #operation="{ row }">
<el-button link type="primary" size="small" @click="handleClick(row)">
Detail
</el-button>
<el-button link type="primary" size="small">Edit</el-button>
</template>
</pure-table>
</template>

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import { tableData } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table
:data="tableData.concat(tableData).concat(tableData)"
:columns="columns"
height="360"
/>
</template>

View File

@@ -0,0 +1,84 @@
<script setup lang="ts">
import { ref } from "vue";
import dayjs from "dayjs";
import { tableDataMore } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
width: "260",
fixed: true
},
{
label: "姓名",
prop: "name",
width: "260"
},
{
label: "地区",
prop: "state",
width: "260"
},
{
label: "城市",
prop: "city",
width: "260"
},
{
label: "地址",
prop: "address",
width: "260"
},
{
label: "邮编",
prop: "post-code",
width: "260"
},
{
label: "操作",
width: "120",
fixed: "right",
slot: "operation"
}
];
const now = new Date();
const tableData = ref(tableDataMore);
const deleteRow = (index: number) => {
tableData.value.splice(index, 1);
};
const onAddItem = () => {
now.setDate(now.getDate() + 1);
tableData.value.push({
date: dayjs(now).format("YYYY-MM-DD"),
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
state: "California",
city: "Los Angeles",
"post-code": "CA 90036"
});
};
</script>
<template>
<div>
<pure-table :data="tableData" :columns="columns" maxHeight="500">
<template #operation="{ $index }">
<el-button
link
type="primary"
size="small"
@click.prevent="deleteRow($index)"
>
Remove
</el-button>
</template>
</pure-table>
<el-button class="mt-4" style="width: 100%" @click="onAddItem">
Add Item
</el-button>
</div>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import { tableDataMore } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
fixed: true
},
{
label: "姓名",
prop: "name",
children: [
{
label: "地区",
children: [
{
label: "城市",
prop: "city"
},
{
label: "地址",
prop: "address"
}
]
}
]
},
{
label: "邮编",
prop: "post-code"
}
];
</script>
<template>
<pure-table :data="tableDataMore" :columns="columns" alignWhole="center" />
</template>

View File

@@ -0,0 +1,72 @@
import { message } from "@/utils/message";
import { tableData } from "../data";
import { ref, computed } from "vue";
// 如果您不习惯tsx写法可以传slot然后在template里写
// 需是hooks写法函数中有return避免失去响应性
export function useColumns() {
const search = ref("");
const filterTableData = computed(() =>
tableData.filter(
data =>
!search.value ||
data.name.toLowerCase().includes(search.value.toLowerCase())
)
);
const handleEdit = (index: number, row) => {
message(`您修改了第 ${index} 行,数据为:${JSON.stringify(row)}`, {
type: "success"
});
};
const handleDelete = (index: number, row) => {
message(`您删除了第 ${index} 行,数据为:${JSON.stringify(row)}`);
};
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
},
{
align: "right",
// 自定义表头
headerRenderer: () => (
<el-input
v-model={search.value}
size="small"
clearable
placeholder="Type to search"
/>
),
cellRenderer: ({ index, row }) => (
<>
<el-button size="small" onClick={() => handleEdit(index + 1, row)}>
Edit
</el-button>
<el-button
size="small"
type="danger"
onClick={() => handleDelete(index + 1, row)}
>
Delete
</el-button>
</>
)
}
];
return {
columns,
filterTableData
};
}

View File

@@ -0,0 +1,9 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, filterTableData } = useColumns();
</script>
<template>
<pure-table :data="filterTableData" :columns="columns" />
</template>

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { tableDataImage } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
},
{
label: "视频",
slot: "video"
},
{
label: "图像",
slot: "image"
}
];
</script>
<template>
<pure-table :data="tableDataImage" :columns="columns">
<template #video>
<video width="180" height="180" controls>
<source
src="https://yiming_chang.gitee.io/pure-admin-doc/video/url.mov"
type="video/mp4"
/>
</video>
</template>
<template #image="{ row, index }">
<el-image
preview-teleported
loading="lazy"
:src="row.image"
:preview-src-list="tableDataImage.map(v => v.image)"
:initial-index="index"
fit="cover"
class="w-[100px] h-[100px]"
/>
</template>
</pure-table>
</template>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import { ref } from "vue";
import { tableData } from "./data";
type TableLayout = "fixed" | "auto";
const tableLayout = ref<TableLayout>("fixed");
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<div>
<el-radio-group v-model="tableLayout">
<el-radio-button label="fixed" value="fixed" />
<el-radio-button label="auto" value="auto" />
</el-radio-group>
<pure-table
:data="tableData"
:columns="columns"
:table-layout="tableLayout"
/>
</div>
</template>

View File

@@ -0,0 +1,166 @@
import Base from "./base.vue";
import Stripe from "./stripe.vue";
import Border from "./border.vue";
import Status from "./status.vue";
import FixHeader from "./fixHeader.vue";
import FixColumn from "./fixColumn.vue";
import FluidHeight from "./fluidHeight.vue";
import GroupHeader from "./groupHeader.vue";
import Radio from "./radio.vue";
import MultipleChoice from "./multipleChoice.vue";
import Sortable from "./sortable.vue";
import Filters from "./filters.vue";
import ColumnTemplate from "./column-template/index.vue";
import HeaderRenderer from "./header-renderer/index.vue";
import Expand from "./expand.vue";
import TreeTable from "./tree.vue";
import TotalRow from "./totalRow.vue";
import Merge from "./merge.vue";
import CustomIndex from "./customIndex.vue";
import Layout from "./layout.vue";
import NestProp from "./nestProp.vue";
import ImgPreview from "./imgPreview.vue";
const rendContent = (val: string) =>
`代码位置src/views/table/base/${val}.vue`;
export const list = [
{
key: "base",
content: rendContent("base"),
title: "基础表格",
component: Base
},
{
key: "stripe",
content: rendContent("stripe"),
title: "带斑马纹表格",
component: Stripe
},
{
key: "border",
content: rendContent("border"),
title: "带边框表格",
component: Border
},
{
key: "status",
content: rendContent("status"),
title: "带状态表格",
component: Status
},
{
key: "fixHeader",
content: rendContent("fixHeader"),
title: "固定表头",
component: FixHeader
},
{
key: "fixColumn",
content: rendContent("fixColumn"),
title: "固定列",
component: FixColumn
},
{
key: "fixColumnHeader",
content: rendContent("fixColumn"),
title: "固定列和表头",
component: () => <FixColumn height={"360"} />
},
{
key: "groupHeader",
content: rendContent("groupHeader"),
title: "多级表头(表头分组)",
component: GroupHeader
},
{
key: "fluidHeight",
content: rendContent("fluidHeight"),
title: "流体高度",
component: FluidHeight
},
{
key: "radio",
content: rendContent("radio"),
title: "单选",
component: Radio
},
{
key: "multipleChoice",
content: rendContent("multipleChoice"),
title: "多选",
component: MultipleChoice
},
{
key: "sortable",
content: rendContent("sortable"),
title: "排序和格式化",
component: Sortable
},
{
key: "filters",
content: rendContent("filters"),
title: "筛选",
component: Filters
},
{
key: "column-template",
content: rendContent("column-template/index"),
title: "自定义列模板",
component: ColumnTemplate
},
{
key: "header-renderer",
content: rendContent("header-renderer/index"),
title: "自定义表头",
component: HeaderRenderer
},
{
key: "expand",
content: rendContent("expand"),
title: "展开行",
component: Expand
},
{
key: "tree",
content: rendContent("tree"),
title: "树形数据与懒加载",
component: TreeTable
},
{
key: "totalRow",
content: rendContent("totalRow"),
title: "表尾合计行",
component: TotalRow
},
{
key: "merge",
content: rendContent("merge"),
title: "合并行或列",
component: Merge
},
{
key: "customIndex",
content: rendContent("customIndex"),
title: "自定义索引",
component: CustomIndex
},
{
key: "layout",
content: rendContent("layout"),
title: "表格布局",
component: Layout
},
{
key: "nestProp",
content: rendContent("nestProp"),
title: "多种数据格式(深层结构)",
component: NestProp
},
{
key: "imgPreview",
content: rendContent("imgPreview"),
title: "图像预览",
component: ImgPreview
}
];

View File

@@ -0,0 +1,124 @@
<script setup lang="ts">
interface User {
id: string;
name: string;
amount1: string;
amount2: string;
amount3: number;
}
interface SpanMethodProps {
row: User;
column: any;
rowIndex: number;
columnIndex: number;
}
const arraySpanMethod = ({ rowIndex, columnIndex }: SpanMethodProps) => {
if (rowIndex % 2 === 0) {
if (columnIndex === 0) {
return [1, 2];
} else if (columnIndex === 1) {
return [0, 0];
}
}
};
const objectSpanMethod = ({ rowIndex, columnIndex }: SpanMethodProps) => {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
};
const tableData: User[] = [
{
id: "12987122",
name: "Tom",
amount1: "234",
amount2: "3.2",
amount3: 10
},
{
id: "12987123",
name: "Tom",
amount1: "165",
amount2: "4.43",
amount3: 12
},
{
id: "12987124",
name: "Tom",
amount1: "324",
amount2: "1.9",
amount3: 9
},
{
id: "12987125",
name: "Tom",
amount1: "621",
amount2: "2.2",
amount3: 17
},
{
id: "12987126",
name: "Tom",
amount1: "539",
amount2: "4.1",
amount3: 15
}
];
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "Name",
prop: "name"
},
{
label: "Amount 1",
prop: "amount1",
sortable: true
},
{
label: "Amount 2",
prop: "amount2",
sortable: true
},
{
label: "Amount 3",
prop: "amount3",
sortable: true
}
];
</script>
<template>
<div>
<pure-table
:data="tableData"
:columns="columns"
:span-method="arraySpanMethod"
border
/>
<pure-table
:data="tableData"
:columns="columns"
:span-method="objectSpanMethod"
border
/>
</div>
</template>

View File

@@ -0,0 +1,57 @@
<script setup lang="ts">
import { ref } from "vue";
import { tableData } from "./data";
const tableRef = ref();
const multipleSelection = ref([]);
const toggleSelection = (rows?: any) => {
const { toggleRowSelection, clearSelection } = tableRef.value.getTableRef();
if (rows) {
rows.forEach(row => {
toggleRowSelection(row, undefined);
});
} else {
clearSelection();
}
};
const handleSelectionChange = val => {
multipleSelection.value = val;
};
const columns: TableColumnList = [
{
type: "selection",
align: "left"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<div>
<pure-table
ref="tableRef"
:data="tableData"
:columns="columns"
@selection-change="handleSelectionChange"
/>
<div style="margin-top: 20px">
<el-button @click="toggleSelection([tableData[1], tableData[2]])">
Toggle selection status of second and third rows
</el-button>
<el-button @click="toggleSelection()">Clear selection</el-button>
</div>
</div>
</template>

View File

@@ -0,0 +1,86 @@
<script setup lang="ts">
const tableData = [
{
userInfo: { name: "Test1", age: 22 },
other: [
{ sex: "女" },
{
more: {
content: '<div><span style="color: red">我是 html 片段</span></div>'
}
}
],
role: "设计师"
},
{
userInfo: { name: "Test2", age: 28 },
other: [
{ sex: "男" },
{
more: {
content:
'<img width="100" height="100" src="https://pure-admin.github.io/pure-admin-table/imgs/11.jpg">'
}
}
],
role: "后端"
},
{
userInfo: { name: "Test3", age: 20 },
other: [
{ sex: "女" },
{
more: {
content:
'<img width="100" height="100" src="https://pure-admin.github.io/pure-admin-table/imgs/1.jpg">'
}
}
],
role: "程序员鼓励师"
},
{
userInfo: { name: "Test4", age: 26 },
other: [
{ sex: "男" },
{
more: {
content:
'<a href="https://github.com/xiaoxian521" target="_black">我是链接,点我去 Follow</a>'
}
}
],
role: "前端"
}
];
const columns: TableColumnList = [
{
label: "姓名",
prop: "userInfo.name"
},
{
label: "性别",
prop: "other[0].sex"
},
{
label: "年龄",
prop: "userInfo.age"
},
{
label: "Html片段",
slot: "content"
},
{
label: "角色",
prop: "role"
}
];
</script>
<template>
<pure-table :data="tableData" :columns="columns">
<template #content="{ row }">
<span v-html="row.other[1].more.content" />
</template>
</pure-table>
</template>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import { ref } from "vue";
import { tableData } from "./data";
const tableRef = ref();
const currentRow = ref();
const setCurrent = (row?: any) => {
// 获取表格的方法 tableRef.value.getTableRef()
const { setCurrentRow } = tableRef.value.getTableRef();
setCurrentRow(row);
};
const handleCurrentChange = val => {
currentRow.value = val;
};
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<div>
<pure-table
ref="tableRef"
:data="tableData"
:columns="columns"
highlight-current-row
@page-current-change="handleCurrentChange"
/>
<div style="margin-top: 20px">
<el-button @click="setCurrent(tableData[1])">Select second row</el-button>
<el-button @click="setCurrent()">Clear selection</el-button>
</div>
</div>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import { tableDataSortable } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date",
sortable: true
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address",
formatter: ({ address }) => `格式化后的内容:${address}`
}
];
</script>
<template>
<pure-table
:data="tableDataSortable"
:columns="columns"
:default-sort="{ prop: 'date', order: 'ascending' }"
/>
</template>

View File

@@ -0,0 +1,46 @@
<script setup lang="ts">
import { tableData } from "./data";
const tableRowClassName = ({ rowIndex }: { rowIndex: number }) => {
if (rowIndex === 1 || rowIndex === 5) {
return "pure-warning-row";
} else if (rowIndex === 3 || rowIndex === 7) {
return "pure-success-row";
}
return "";
};
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table
:data="tableData"
:columns="columns"
:row-class-name="tableRowClassName"
/>
</template>
<style>
/* 此处样式会在全局都生效,上面 tableRowClassName 函数返回的值也就是类名必须在全局中唯一,避免样式突出 */
.pure-warning-row {
--el-table-tr-bg-color: var(--el-color-warning-light-9);
}
.pure-success-row {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
</style>

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import { tableData } from "./data";
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
</script>
<template>
<pure-table :data="tableData" :columns="columns" stripe />
</template>

View File

@@ -0,0 +1,144 @@
<script setup lang="ts">
interface Product {
id: string;
name: string;
amount1: string;
amount2: string;
amount3: number;
}
interface SummaryMethodProps<T = Product> {
columns: any[];
data: T[];
}
const getSummaries = (param: SummaryMethodProps) => {
const { columns, data } = param;
const sums: string[] = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = "Total Cost";
return;
}
const values = data.map(item => Number(item[column.property]));
if (!values.every(value => Number.isNaN(value))) {
sums[index] = `$ ${values.reduce((prev, curr) => {
const value = Number(curr);
if (!Number.isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0)}`;
} else {
sums[index] = "N/A";
}
});
return sums;
};
const tableData: Product[] = [
{
id: "12987122",
name: "Tom",
amount1: "234",
amount2: "3.2",
amount3: 10
},
{
id: "12987123",
name: "Tom",
amount1: "165",
amount2: "4.43",
amount3: 12
},
{
id: "12987124",
name: "Tom",
amount1: "324",
amount2: "1.9",
amount3: 9
},
{
id: "12987125",
name: "Tom",
amount1: "621",
amount2: "2.2",
amount3: 17
},
{
id: "12987126",
name: "Tom",
amount1: "539",
amount2: "4.1",
amount3: 15
}
];
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "Name",
prop: "name"
},
{
label: "Amount 1",
prop: "amount1",
sortable: true
},
{
label: "Amount 2",
prop: "amount2",
sortable: true
},
{
label: "Amount 3",
prop: "amount3",
sortable: true
}
];
const columns1: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "Name",
prop: "name"
},
{
label: "Cost 1 ($)",
prop: "amount1"
},
{
label: "Cost 2 ($)",
prop: "amount2"
},
{
label: "Cost 3 ($)",
prop: "amount3"
}
];
</script>
<template>
<pure-table
:data="tableData"
:columns="columns"
border
show-summary
class="mb-6"
/>
<pure-table
:data="tableData"
:columns="columns1"
border
:summary-method="getSummaries"
show-summary
/>
</template>

View File

@@ -0,0 +1,122 @@
<script setup lang="ts">
interface User {
id: number;
date: string;
name: string;
hasChildren?: boolean;
children?: User[];
}
const load = (
row: User,
treeNode: unknown,
resolve: (date: User[]) => void
) => {
setTimeout(() => {
resolve([
{
id: 31,
date: "2016-05-01",
name: "wangxiaohu"
},
{
id: 32,
date: "2016-05-01",
name: "wangxiaohu"
}
]);
}, 1000);
};
const tableData: User[] = [
{
id: 1,
date: "2016-05-02",
name: "wangxiaohu"
},
{
id: 2,
date: "2016-05-04",
name: "wangxiaohu"
},
{
id: 3,
date: "2016-05-01",
name: "wangxiaohu",
children: [
{
id: 31,
date: "2016-05-01",
name: "wangxiaohu"
},
{
id: 32,
date: "2016-05-01",
name: "wangxiaohu"
}
]
},
{
id: 4,
date: "2016-05-03",
name: "wangxiaohu"
}
];
const tableData1: User[] = [
{
id: 1,
date: "2016-05-02",
name: "wangxiaohu"
},
{
id: 2,
date: "2016-05-04",
name: "wangxiaohu"
},
{
id: 3,
date: "2016-05-01",
name: "wangxiaohu",
hasChildren: true
},
{
id: 4,
date: "2016-05-03",
name: "wangxiaohu"
}
];
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
];
</script>
<template>
<div>
<pure-table
:data="tableData"
:columns="columns"
row-key="id"
border
default-expand-all
class="mb-6"
/>
<pure-table
:data="tableData1"
:columns="columns"
row-key="id"
border
lazy
:load="load"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
/>
</div>
</template>

82
src/views/table/edit.vue Normal file
View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
import { ref } from "vue";
import { list } from "./edit/list";
defineOptions({
name: "PureTableEdit"
});
const selected = ref(0);
function tabClick({ index }) {
selected.value = index;
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
可编辑用法全部采用 tsx 语法充分发挥
<el-link
href="https://github.com/pure-admin/pure-admin-table"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
@pureadmin/table
</el-link>
的灵活性维护整体表格只需操作 columns 配置即可
</span>
</div>
</template>
<el-alert
title="可编辑用法中所有表格都设置了 row-key 它是唯一值的字段比如id。作用1. 用来优化 Table
的渲染尤其当字段在深层结构中2. 防止拖拽后表格组件内部混乱(拖拽必须设置)"
type="info"
:closable="false"
/>
<el-tabs @tab-click="tabClick">
<template v-for="(item, index) of list" :key="item.key">
<el-tab-pane :lazy="true">
<template #label>
<el-tooltip
:content="`(第 ${index + 1} 个示例)${item.content}`"
placement="top-end"
>
<span>{{ item.title }}</span>
</el-tooltip>
</template>
<component :is="item.component" v-if="selected == index" />
</el-tab-pane>
</template>
</el-tabs>
</el-card>
</template>
<style scoped>
:deep(.el-tabs__nav-wrap)::after {
height: 1px;
}
:deep(.el-tabs__header) {
margin-top: 10px;
}
:deep(.el-alert__title) {
font-size: 16px;
}
:deep(.el-tabs__nav-next),
:deep(.el-tabs__nav-prev) {
font-size: 16px;
color: var(--el-text-color-primary);
}
:deep(.el-tabs__nav-next.is-disabled),
:deep(.el-tabs__nav-prev.is-disabled) {
opacity: 0.5;
}
</style>

View File

@@ -0,0 +1,74 @@
const tableData = [
{
id: 1,
name: "Tom",
sex: 0, // 0代表男 1代表女
hobby: 2,
date: "2024-03-17"
},
{
id: 2,
name: "Jack",
sex: 0,
hobby: 1,
date: "2024-03-18"
},
{
id: 3,
name: "Lily",
sex: 1,
hobby: 1,
date: "2024-03-19"
},
{
id: 4,
name: "Mia",
sex: 1,
hobby: 3,
date: "2024-03-20"
}
];
const options = [
{
value: 0,
label: "上午写代码"
},
{
value: 1,
label: "下午写代码"
},
{
value: 2,
label: "晚上写代码"
},
{
value: 3,
label: "凌晨休息了"
}
];
const tableDataEdit = [
{
id: 1,
name: "Tom",
address: "home"
},
{
id: 2,
name: "Jack",
address: "office"
},
{
id: 3,
name: "Lily",
address: "library"
},
{
id: 4,
name: "Mia",
address: "playground"
}
];
export { tableData, tableDataEdit, options };

View File

@@ -0,0 +1,87 @@
import { ref } from "vue";
import { options } from "../data";
export function useColumns() {
const dataList = ref([]);
const columns: TableColumnList = [
{
label: "姓名",
prop: "name",
cellRenderer: ({ row }) => <el-input v-model={row.name} />
},
{
label: "性别",
prop: "sex",
cellRenderer: ({ row }) => (
<el-switch
v-model={row.sex}
inline-prompt
active-value={0}
inactive-value={1}
active-text="男"
inactive-text="女"
/>
)
},
{
label: "爱好",
prop: "hobby",
cellRenderer: ({ row }) => (
<el-select v-model={row.hobby} clearable placeholder="请选择爱好">
{options.map(item => {
return (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
);
})}
</el-select>
)
},
{
label: "日期",
prop: "date",
cellRenderer: ({ row }) => (
<el-date-picker
v-model={row.date}
type="date"
format="YYYY/MM/DD"
value-format="YYYY-MM-DD"
placeholder="请选择日期"
/>
),
minWidth: 110
},
{
label: "操作",
fixed: "right",
width: 90,
slot: "operation"
}
];
function onAdd() {
dataList.value.push({
id: dataList.value.length + 1,
name: "",
sex: 0,
hobby: "",
date: ""
});
}
function onDel(row) {
const index = dataList.value.indexOf(row);
if (index !== -1) dataList.value.splice(index, 1);
}
return {
columns,
dataList,
onAdd,
onDel
};
}

View File

@@ -0,0 +1,58 @@
<script setup lang="ts">
import { useColumns } from "./columns";
import Empty from "../empty.svg?component";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import AddFill from "@iconify-icons/ep/plus";
import Delete from "@iconify-icons/ep/delete";
const { columns, dataList, onAdd, onDel } = useColumns();
</script>
<template>
<div class="flex">
<el-scrollbar height="540px">
<code>
<pre class="w-[400px]"> {{ dataList }}</pre>
</code>
</el-scrollbar>
<pure-table
row-key="id"
align-whole="center"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
:data="dataList"
:columns="columns"
>
<template #empty>
<Empty fill="var(--el-svg-monochrome-grey)" class="m-auto" />
</template>
<template #append>
<el-button
plain
class="w-full my-2"
:icon="useRenderIcon(AddFill)"
@click="onAdd"
>
添加一行数据
</el-button>
</template>
<template #operation="{ row }">
<el-button
class="reset-margin"
link
type="primary"
:icon="useRenderIcon(Delete)"
@click="onDel(row)"
/>
</template>
</pure-table>
</div>
</template>
<style scoped>
:deep(.el-table__inner-wrapper::before) {
height: 0;
}
</style>

View File

@@ -0,0 +1,116 @@
import { ref } from "vue";
import { tableData, options } from "../data";
import { clone, delObjectProperty } from "@pureadmin/utils";
export function useColumns() {
const editMap = ref({});
const dataList = ref(clone(tableData, true));
const columns: TableColumnList = [
{
label: "姓名",
prop: "name",
cellRenderer: ({ row, index }) => (
<>
{editMap.value[index]?.editable ? (
<el-input v-model={row.name} />
) : (
<p>{row.name}</p>
)}
</>
)
},
{
label: "性别",
prop: "sex",
cellRenderer: ({ row, index }) => (
<>
{editMap.value[index]?.editable ? (
<el-switch
v-model={row.sex}
inline-prompt
active-value={0}
inactive-value={1}
active-text="男"
inactive-text="女"
/>
) : (
<p>{row.sex === 0 ? "男" : "女"}</p>
)}
</>
)
},
{
label: "爱好",
prop: "hobby",
cellRenderer: ({ row, index }) => (
<>
{editMap.value[index]?.editable ? (
<el-select v-model={row.hobby} clearable placeholder="请选择爱好">
{options.map(item => {
return (
<el-option
key={item.value}
label={item.label}
value={item.value}
/>
);
})}
</el-select>
) : (
<el-tag type="primary">
{options.filter(opt => opt.value == row.hobby)[0]?.label}
</el-tag>
)}
</>
)
},
{
label: "日期",
prop: "date",
cellRenderer: ({ row, index }) => (
<>
{editMap.value[index]?.editable ? (
<el-date-picker
v-model={row.date}
type="date"
format="YYYY/MM/DD"
value-format="YYYY-MM-DD"
placeholder="请选择日期"
/>
) : (
<p>{row.date}</p>
)}
</>
),
minWidth: 110
},
{
label: "操作",
fixed: "right",
slot: "operation"
}
];
function onEdit(row, index) {
editMap.value[index] = Object.assign({ ...row, editable: true });
}
function onSave(index) {
editMap.value[index].editable = false;
}
function onCancel(index) {
editMap.value[index].editable = false;
dataList.value[index] = delObjectProperty(editMap.value[index], "editable");
}
return {
editMap,
columns,
dataList,
onEdit,
onSave,
onCancel
};
}

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { editMap, columns, dataList, onEdit, onSave, onCancel } = useColumns();
</script>
<template>
<div class="flex">
<el-scrollbar>
<code>
<pre class="w-[400px]"> {{ dataList }}</pre>
</code>
</el-scrollbar>
<pure-table
row-key="id"
align-whole="center"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
:data="dataList"
:columns="columns"
>
<template #operation="{ row, index }">
<el-button
v-if="!editMap[index]?.editable"
class="reset-margin"
link
type="primary"
@click="onEdit(row, index)"
>
修改
</el-button>
<div v-else>
<el-button
class="reset-margin"
link
type="primary"
@click="onSave(index)"
>
保存
</el-button>
<el-button class="reset-margin" link @click="onCancel(index)">
取消
</el-button>
</div>
</template>
</pure-table>
</div>
</template>

View File

@@ -0,0 +1,87 @@
import { ref, computed } from "vue";
import { tableDataEdit } from "../data";
import EditPen from "@iconify-icons/ep/edit-pen";
import Check from "@iconify-icons/ep/check";
export function useColumns() {
const editMap = ref({});
const activeIndex = ref(-1);
const dataList = ref(tableDataEdit);
const editing = computed(() => {
return index => {
return editMap.value[index]?.editing;
};
});
const iconClass = computed(() => {
return (index, other = false) => {
return [
"cursor-pointer",
"ml-2",
"transition",
"delay-100",
other
? ["hover:scale-110", "hover:text-red-500"]
: editing.value(index) && ["scale-150", "text-red-500"]
];
};
});
const columns: TableColumnList = [
{
label: "姓名(可修改)",
prop: "name",
cellRenderer: ({ row, index }) => (
<div
class="flex-bc w-full h-[32px]"
onMouseenter={() => (activeIndex.value = index)}
onMouseleave={() => onMouseleave(index)}
>
{!editing.value(index) ? (
<p>{row.name}</p>
) : (
<>
<el-input v-model={row.name} />
<iconify-icon-offline
icon={Check}
class={iconClass.value(index)}
onClick={() => onSave(index)}
/>
</>
)}
<iconify-icon-offline
v-show={activeIndex.value === index && !editing.value(index)}
icon={EditPen}
class={iconClass.value(index, true)}
onClick={() => onEdit(row, index)}
/>
</div>
)
},
{
label: "地址",
prop: "address"
}
];
function onMouseleave(index) {
editing.value[index]
? (activeIndex.value = index)
: (activeIndex.value = -1);
}
function onEdit(row, index) {
editMap.value[index] = Object.assign({ ...row, editing: true });
}
function onSave(index) {
editMap.value[index].editing = false;
}
return {
columns,
dataList
};
}

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList } = useColumns();
</script>
<template>
<div class="flex">
<el-scrollbar>
<code>
<pre class="w-[400px]"> {{ dataList }}</pre>
</code>
</el-scrollbar>
<pure-table
class="!w-[30vw]"
row-key="id"
border
:data="dataList"
:columns="columns"
/>
</div>
</template>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" class="empty-icon" viewBox="0 0 1024 1024"><path d="M855.6 427.2H168.5c-12.7 0-24.4 6.9-30.6 18L4.4 684.7C1.5 689.9 0 695.8 0 701.8v287.1c0 19.4 15.7 35.1 35.1 35.1H989c19.4 0 35.1-15.7 35.1-35.1V701.8c0-6-1.5-11.8-4.4-17.1L886.2 445.2c-6.2-11.1-17.9-18-30.6-18M673.4 695.6c-16.5 0-30.8 11.5-34.3 27.7-12.7 58.5-64.8 102.3-127.2 102.3s-114.5-43.8-127.2-102.3c-3.5-16.1-17.8-27.7-34.3-27.7H119c-26.4 0-43.3-28-31.1-51.4l81.7-155.8c6.1-11.6 18-18.8 31.1-18.8h622.4c13 0 25 7.2 31.1 18.8l81.7 155.8c12.2 23.4-4.7 51.4-31.1 51.4zm146.5-486.1c-1-1.8-2.1-3.7-3.2-5.5-9.8-16.6-31.1-22.2-47.8-12.6L648.5 261c-17 9.8-22.7 31.6-12.6 48.4.9 1.4 1.7 2.9 2.5 4.4 9.5 17 31.2 22.8 48 13L807 257.3c16.7-9.7 22.4-31 12.9-47.8m-444.5 51.6L255 191.6c-16.7-9.6-38-4-47.8 12.6-1.1 1.8-2.1 3.6-3.2 5.5-9.5 16.8-3.8 38.1 12.9 47.8L337.3 327c16.9 9.7 38.6 4 48-13.1.8-1.5 1.7-2.9 2.5-4.4 10.2-16.8 4.5-38.6-12.4-48.4M512 239.3h2.5c19.5.3 35.5-15.5 35.5-35.1v-139c0-19.3-15.6-34.9-34.8-35.1h-6.4C489.6 30.3 474 46 474 65.2v139c0 19.5 15.9 35.4 35.5 35.1z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,27 @@
import Demo1 from "./demo1/index.vue";
import Demo2 from "./demo2/index.vue";
import Demo3 from "./demo3/index.vue";
const rendContent = (val: string) =>
`代码位置src/views/table/edit/${val}/index.vue`;
export const list = [
{
key: "demo1",
content: rendContent("demo1"),
title: "整体编辑",
component: Demo1
},
{
key: "demo2",
content: rendContent("demo2"),
title: "单行编辑",
component: Demo2
},
{
key: "demo3",
content: rendContent("demo3"),
title: "单元格编辑",
component: Demo3
}
];

82
src/views/table/high.vue Normal file
View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
import { ref } from "vue";
import { list } from "./high/list";
defineOptions({
name: "PureTableHigh"
});
const selected = ref(0);
function tabClick({ index }) {
selected.value = index;
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
高级用法全部采用 tsx 语法充分发挥
<el-link
href="https://github.com/pure-admin/pure-admin-table"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
@pureadmin/table
</el-link>
的灵活性维护整体表格只需操作 columns 配置即可
</span>
</div>
</template>
<el-alert
title="高级用法中所有表格都设置了 row-key 后端需返回唯一值的字段比如id。作用1. 用来优化 Table
的渲染尤其当字段在深层结构中2. 防止拖拽后表格组件内部混乱(拖拽必须设置)"
type="info"
:closable="false"
/>
<el-tabs @tab-click="tabClick">
<template v-for="(item, index) of list" :key="item.key">
<el-tab-pane :lazy="true">
<template #label>
<el-tooltip
:content="`(第 ${index + 1} 个示例)${item.content}`"
placement="top-end"
>
<span>{{ item.title }}</span>
</el-tooltip>
</template>
<component :is="item.component" v-if="selected == index" />
</el-tab-pane>
</template>
</el-tabs>
</el-card>
</template>
<style scoped>
:deep(.el-tabs__nav-wrap)::after {
height: 1px;
}
:deep(.el-tabs__header) {
margin-top: 10px;
}
:deep(.el-alert__title) {
font-size: 16px;
}
:deep(.el-tabs__nav-next),
:deep(.el-tabs__nav-prev) {
font-size: 16px;
color: var(--el-text-color-primary);
}
:deep(.el-tabs__nav-next.is-disabled),
:deep(.el-tabs__nav-prev.is-disabled) {
opacity: 0.5;
}
</style>

View File

@@ -0,0 +1,105 @@
import type {
LoadingConfig,
AdaptiveConfig,
PaginationProps
} from "@pureadmin/table";
import { tableData } from "../data";
import { ref, onMounted, reactive } from "vue";
import { clone, delay } from "@pureadmin/utils";
export function useColumns() {
const dataList = ref([]);
const loading = ref(true);
const columns: TableColumnList = [
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 20,
currentPage: 1,
pageSizes: [20, 40, 60],
total: 0,
align: "right",
background: true,
small: false
});
/** 加载动画配置 */
const loadingConfig = reactive<LoadingConfig>({
text: "正在加载第一页...",
viewBox: "-10, -10, 50, 50",
spinner: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
// svg: "",
// background: rgba()
});
/** 撑满内容区自适应高度相关配置 */
const adaptiveConfig: AdaptiveConfig = {
/** 表格距离页面底部的偏移量,默认值为 `96` */
offsetBottom: 110
/** 是否固定表头,默认值为 `true`如果不想固定表头fixHeader设置为false并且表格要设置table-layout="auto" */
// fixHeader: true
/** 页面 `resize` 时的防抖时间,默认值为 `60` ms */
// timeout: 60
/** 表头的 `z-index`,默认值为 `100` */
// zIndex: 100
};
function onSizeChange(val) {
console.log("onSizeChange", val);
}
function onCurrentChange(val) {
loadingConfig.text = `正在加载第${val}页...`;
loading.value = true;
delay(600).then(() => {
loading.value = false;
});
}
onMounted(() => {
delay(600).then(() => {
const newList = [];
Array.from({ length: 6 }).forEach(() => {
newList.push(clone(tableData, true));
});
newList.flat(Infinity).forEach((item, index) => {
dataList.value.push({ id: index, ...item });
});
pagination.total = dataList.value.length;
loading.value = false;
});
});
return {
loading,
columns,
dataList,
pagination,
loadingConfig,
adaptiveConfig,
onSizeChange,
onCurrentChange
};
}

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const tableRef = ref();
const {
loading,
columns,
dataList,
pagination,
loadingConfig,
adaptiveConfig,
onSizeChange,
onCurrentChange
} = useColumns();
</script>
<template>
<pure-table
ref="tableRef"
border
adaptive
:adaptiveConfig="adaptiveConfig"
row-key="id"
alignWhole="center"
showOverflowTooltip
:loading="loading"
:loading-config="loadingConfig"
:data="
dataList.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@page-size-change="onSizeChange"
@page-current-change="onCurrentChange"
/>
</template>

View File

@@ -0,0 +1,73 @@
import { ref } from "vue";
import { tableDataDrag } from "../data";
import { clone } from "@pureadmin/utils";
import { message } from "@/utils/message";
import { CustomMouseMenu } from "@howdyjs/mouse-menu";
export function useColumns() {
const dataList = ref(clone(tableDataDrag, true));
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
];
// 配置参考https://kongfandong.cn/howdy/mouse-menu/readme
const menuOptions = {
menuList: [
{
label: ({ id }) => `ID为${id}`,
disabled: true
},
{
label: "修改",
tips: "Edit",
fn: row =>
message(
`您修改了第 ${
dataList.value.findIndex(v => v.id === row.id) + 1
} 行,数据为:${JSON.stringify(row)}`,
{
type: "success"
}
)
}
]
};
function showMouseMenu(row, column, event) {
event.preventDefault();
const { x, y } = event;
const ctx = CustomMouseMenu({
el: event.currentTarget,
params: row,
// 菜单容器的CSS设置
menuWrapperCss: {
background: "var(--el-bg-color)"
},
menuItemCss: {
labelColor: "var(--el-text-color)",
hoverLabelColor: "var(--el-color-primary)",
hoverTipsColor: "var(--el-color-primary)"
},
...menuOptions
});
ctx.show(x, y);
}
return {
columns,
dataList,
showMouseMenu
};
}

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList, showMouseMenu } = useColumns();
</script>
<template>
<pure-table
row-key="id"
border
:data="dataList"
:columns="columns"
@row-contextmenu="showMouseMenu"
/>
</template>

View File

@@ -0,0 +1,106 @@
import dayjs from "dayjs";
import { clone } from "@pureadmin/utils";
const date = dayjs(new Date()).format("YYYY-MM-DD");
const tableData = [
{
date,
name: "Tom",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Jack",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Dick",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Harry",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Sam",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Lucy",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mary",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mike",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mike1",
address: "No. 189, Grove St, Los Angeles"
},
{
date,
name: "Mike2",
address: "No. 189, Grove St, Los Angeles"
}
];
const tableDataMore = clone(tableData, true).map(item =>
Object.assign(item, {
state: "California",
city: "Los Angeles",
"post-code": "CA 90036"
})
);
const tableDataImage = clone(tableData, true).map((item, index) =>
Object.assign(item, {
image: `https://pure-admin.github.io/pure-admin-table/imgs/${index + 1}.jpg`
})
);
const tableDataSortable = clone(tableData, true).map((item, index) => {
delete item.date;
Object.assign(item, {
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`
});
});
const tableDataDrag = clone(tableData, true).map((item, index) => {
delete item.address;
delete item.date;
return Object.assign(item, {
id: index + 1,
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`
});
});
const tableDataEdit = clone(tableData, true).map((item, index) => {
delete item.date;
return Object.assign(item, {
id: index + 1,
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`,
address: "China",
sex: index % 2 === 0 ? "男" : "女"
});
});
export {
tableData,
tableDataDrag,
tableDataMore,
tableDataEdit,
tableDataImage,
tableDataSortable
};

View File

@@ -0,0 +1,69 @@
import Sortable from "sortablejs";
import { clone } from "@pureadmin/utils";
import { tableDataDrag } from "../../data";
import { ref, nextTick, onMounted } from "vue";
// 列拖拽演示
export function useColumns() {
const dataList = ref(clone(tableDataDrag, true));
const columnsDrag = ref<TableColumnList>([
{
label: "ID",
prop: "id"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
]);
const columns = ref<TableColumnList>([
{
label: "ID",
prop: index => columnsDrag.value[index].prop as string
},
{
label: "日期",
prop: index => columnsDrag.value[index].prop as string
},
{
label: "姓名",
prop: index => columnsDrag.value[index].prop as string
}
]);
const columnDrop = (event: { preventDefault: () => void }) => {
event.preventDefault();
nextTick(() => {
const wrapper: HTMLElement = document.querySelector(
".el-table__header-wrapper tr"
);
Sortable.create(wrapper, {
animation: 300,
delay: 0,
onEnd: ({ newIndex, oldIndex }) => {
const oldItem = columnsDrag.value[oldIndex];
columnsDrag.value.splice(oldIndex, 1);
columnsDrag.value.splice(newIndex, 0, oldItem);
}
});
});
};
onMounted(() => {
nextTick(() => {
columnDrop(event);
});
});
return {
columns,
dataList,
columnsDrag
};
}

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList, columnsDrag } = useColumns();
</script>
<template>
<div class="flex">
<el-scrollbar height="700px">
<code>
<pre class="w-[700px]"> {{ columnsDrag }}</pre>
</code>
</el-scrollbar>
<pure-table row-key="id" :data="dataList" :columns="columns" />
</div>
</template>

View File

@@ -0,0 +1,70 @@
import Sortable from "sortablejs";
import { ref, nextTick } from "vue";
import { clone } from "@pureadmin/utils";
import { tableDataDrag } from "../../data";
// 行拖拽演示
export function useColumns() {
const dataList = ref(clone(tableDataDrag, true));
const rowDrop = (event: { preventDefault: () => void }) => {
event.preventDefault();
nextTick(() => {
const wrapper: HTMLElement = document.querySelector(
".el-table__body-wrapper tbody"
);
Sortable.create(wrapper, {
animation: 300,
handle: ".drag-btn",
onEnd: ({ newIndex, oldIndex }) => {
const currentRow = dataList.value.splice(oldIndex, 1)[0];
dataList.value.splice(newIndex, 0, currentRow);
}
});
});
};
const columns: TableColumnList = [
// {
// width: 60,
// cellRenderer: () => (
// <iconify-icon-online
// icon="icon-park-outline:drag"
// class="drag-btn cursor-grab"
// onMouseenter={(event: { preventDefault: () => void }) =>
// rowDrop(event)
// }
// />
// )
// },
{
label: "ID",
prop: "id",
cellRenderer: ({ row }) => (
<div class="flex items-center">
<iconify-icon-online
icon="icon-park-outline:drag"
class="drag-btn cursor-grab"
onMouseenter={(event: { preventDefault: () => void }) =>
rowDrop(event)
}
/>
<p class="ml-[16px]">{row.id}</p>
</div>
)
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
];
return {
columns,
dataList
};
}

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList } = useColumns();
</script>
<template>
<div class="flex">
<el-scrollbar height="700px">
<code>
<pre class="w-[700px]"> {{ dataList }}</pre>
</code>
</el-scrollbar>
<pure-table row-key="id" :data="dataList" :columns="columns" />
</div>
</template>

View File

@@ -0,0 +1,83 @@
import { ref, computed } from "vue";
import { tableDataDrag } from "../data";
import { message } from "@/utils/message";
import { templateRef } from "@vueuse/core";
import { clone, useDark, useECharts } from "@pureadmin/utils";
export function useColumns() {
const dataList = ref(clone(tableDataDrag, true).splice(0, 4));
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "姓名",
prop: "name"
},
{
label: "日期",
prop: "date"
},
{
label: "echarts图表",
slot: "echart"
}
];
const { isDark } = useDark();
const theme = computed(() => (isDark.value ? "dark" : "light"));
dataList.value.forEach((_, i) => {
const { setOptions } = useECharts(templateRef(`PieChartRef${i}`), {
theme
});
setOptions(
{
tooltip: {
trigger: "item",
// 将 tooltip 控制在图表区域里
confine: true
},
series: [
{
name: "Github信息",
type: "pie",
// center: ["30%", "50%"],
data: [
{ value: 1067, name: "watchers" },
{ value: 4037, name: "star" },
{ value: 859, name: "forks" }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: "rgba(0, 0, 0, 0.5)"
}
}
}
]
},
{
name: "click",
callback: ({ data: { name, value } }) => {
message(
`您点击了第 ${i + 1} 行,图表标题为${name},图表数据为:${value}`,
{
type: "success"
}
);
}
}
);
});
return {
columns,
dataList
};
}

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList } = useColumns();
</script>
<template>
<pure-table row-key="id" border :data="dataList" :columns="columns">
<template #echart="{ index }">
<div :ref="'PieChartRef' + index" class="w-full h-[100px]" />
</template>
</pure-table>
</template>

View File

@@ -0,0 +1,52 @@
import { ref } from "vue";
import { utils, writeFile } from "xlsx";
import { tableDataDrag } from "../data";
import { clone } from "@pureadmin/utils";
import { message } from "@/utils/message";
export function useColumns() {
const dataList = ref(clone(tableDataDrag, true));
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
}
];
const exportExcel = () => {
const res = dataList.value.map(item => {
const arr = [];
columns.forEach(column => {
arr.push(item[column.prop as string]);
});
return arr;
});
const titleList = [];
columns.forEach(column => {
titleList.push(column.label);
});
res.unshift(titleList);
const workSheet = utils.aoa_to_sheet(res);
const workBook = utils.book_new();
utils.book_append_sheet(workBook, workSheet, "数据报表");
writeFile(workBook, "pure-admin-table.xlsx");
message("导出成功", {
type: "success"
});
};
return {
columns,
dataList,
exportExcel
};
}

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const { columns, dataList, exportExcel } = useColumns();
</script>
<template>
<div>
<el-button
type="primary"
class="mb-[20px] float-right"
@click="exportExcel"
>
导出
</el-button>
<pure-table row-key="id" border :data="dataList" :columns="columns" />
</div>
</template>

View File

@@ -0,0 +1,76 @@
import Adaptive from "./adaptive/index.vue";
import Page from "./page/index.vue";
import RowDrag from "./drag/row/index.vue";
import ColumnDrag from "./drag/column/index.vue";
import Contextmenu from "./contextmenu/index.vue";
import Excel from "./excel/index.vue";
import Watermark from "./watermark/index.vue";
import Print from "./prints/index.vue";
import Echarts from "./echarts/index.vue";
import TableSelect from "./table-select/index.vue";
const rendContent = (val: string) =>
`代码位置src/views/table/high/${val}/index.vue`;
export const list = [
{
key: "adaptive",
content: rendContent("adaptive"),
title: "自适应内容区高度",
component: Adaptive
},
{
key: "page",
content: rendContent("page"),
title: "分页、加载动画、动态列",
component: Page
},
{
key: "tableSelect",
content: rendContent("table-select"),
title: "表格选择器",
component: TableSelect
},
{
key: "rowDrag",
content: rendContent("drag/row"),
title: "拖拽表格(行拖拽)",
component: RowDrag
},
{
key: "columnDrag",
content: rendContent("drag/column"),
title: "拖拽表格(列拖拽)",
component: ColumnDrag
},
{
key: "contextmenu",
content: rendContent("contextmenu"),
title: "右键菜单",
component: Contextmenu
},
{
key: "excel",
content: rendContent("excel"),
title: "导出excel",
component: Excel
},
{
key: "print",
content: rendContent("print"),
title: "打印",
component: Print
},
{
key: "watermark",
content: rendContent("watermark"),
title: "水印",
component: Watermark
},
{
key: "echarts",
content: rendContent("echarts"),
title: "内嵌echarts图表",
component: Echarts
}
];

View File

@@ -0,0 +1,116 @@
import { tableData } from "../data";
import { clone, delay } from "@pureadmin/utils";
import { ref, onMounted, reactive, watchEffect } from "vue";
import type { PaginationProps, LoadingConfig, Align } from "@pureadmin/table";
export function useColumns() {
const dataList = ref([]);
const loading = ref(true);
const select = ref("no");
const hideVal = ref("nohide");
const tableSize = ref("default");
const paginationSmall = ref(false);
const paginationAlign = ref("right");
const columns: TableColumnList = [
{
type: "selection",
align: "left",
reserveSelection: true,
hide: () => (select.value === "no" ? true : false)
},
{
label: "日期",
prop: "date",
hide: () => (hideVal.value === "hideDate" ? true : false)
},
{
label: "姓名",
prop: "name",
hide: () => (hideVal.value === "hideName" ? true : false)
},
{
label: "地址",
prop: "address",
hide: () => (hideVal.value === "hideAddress" ? true : false)
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 10,
currentPage: 1,
pageSizes: [10, 15, 20],
total: 0,
align: "right",
background: true,
small: false
});
/** 加载动画配置 */
const loadingConfig = reactive<LoadingConfig>({
text: "正在加载第一页...",
viewBox: "-10, -10, 50, 50",
spinner: `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`
// svg: "",
// background: rgba()
});
function onChange(val) {
pagination.small = val;
}
function onSizeChange(val) {
console.log("onSizeChange", val);
}
function onCurrentChange(val) {
loadingConfig.text = `正在加载第${val}页...`;
loading.value = true;
delay(600).then(() => {
loading.value = false;
});
}
watchEffect(() => {
pagination.align = paginationAlign.value as Align;
});
onMounted(() => {
delay(600).then(() => {
const newList = [];
Array.from({ length: 6 }).forEach(() => {
newList.push(clone(tableData, true));
});
newList.flat(Infinity).forEach((item, index) => {
dataList.value.push({ id: index, ...item });
});
pagination.total = dataList.value.length;
loading.value = false;
});
});
return {
loading,
columns,
dataList,
select,
hideVal,
tableSize,
pagination,
loadingConfig,
paginationAlign,
paginationSmall,
onChange,
onSizeChange,
onCurrentChange
};
}

View File

@@ -0,0 +1,80 @@
<script setup lang="ts">
import { useColumns } from "./columns";
const {
loading,
columns,
dataList,
select,
hideVal,
tableSize,
pagination,
loadingConfig,
paginationAlign,
paginationSmall,
onChange,
onSizeChange,
onCurrentChange
} = useColumns();
</script>
<template>
<div>
<el-space class="float-right mb-4">
<p class="text-sm">多选</p>
<el-radio-group v-model="select" size="small">
<el-radio-button value="yes"></el-radio-button>
<el-radio-button value="no"></el-radio-button>
</el-radio-group>
<el-divider direction="vertical" />
<p class="text-sm">动态列</p>
<el-radio-group v-model="hideVal" size="small">
<el-radio-button value="nohide">不隐藏</el-radio-button>
<el-radio-button value="hideDate">隐藏日期</el-radio-button>
<el-radio-button value="hideName">隐藏姓名</el-radio-button>
<el-radio-button value="hideAddress">隐藏地址</el-radio-button>
</el-radio-group>
<el-divider direction="vertical" />
<p class="text-sm">表格大小</p>
<el-radio-group v-model="tableSize" size="small">
<el-radio-button value="large">large</el-radio-button>
<el-radio-button value="default">default</el-radio-button>
<el-radio-button value="small">small</el-radio-button>
</el-radio-group>
<el-divider direction="vertical" />
<p class="text-sm">分页大小</p>
<el-radio-group v-model="paginationSmall" size="small" @change="onChange">
<el-radio-button :value="false">no small</el-radio-button>
<el-radio-button :value="true">small</el-radio-button>
</el-radio-group>
<el-divider direction="vertical" />
<p class="text-sm">分页的对齐方式</p>
<el-radio-group v-model="paginationAlign" size="small">
<el-radio-button value="right">right</el-radio-button>
<el-radio-button value="center">center</el-radio-button>
<el-radio-button value="left">left</el-radio-button>
</el-radio-group>
</el-space>
<pure-table
border
row-key="id"
alignWhole="center"
showOverflowTooltip
:size="tableSize as any"
:loading="loading"
:loading-config="loadingConfig"
:height="tableSize === 'small' ? 352 : 440"
:data="
dataList.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@page-size-change="onSizeChange"
@page-current-change="onCurrentChange"
/>
</div>
</template>

View File

@@ -0,0 +1,60 @@
import Print from "@/utils/print";
import { ref, type Ref } from "vue";
import { tableDataEdit } from "../data";
import { clone } from "@pureadmin/utils";
export function useColumns(printRef: Ref) {
const dataList = ref(clone(tableDataEdit, true));
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
const print = () => {
Print(printRef.value.getTableDoms().tableWrapper).toPrint;
};
function cellStyle({ column: { property }, rowIndex }) {
if (property === "id") {
return rowIndex < 3
? { background: "#87baf9" }
: { background: "#87e8de" };
}
}
function headerCellStyle({ columnIndex }) {
return columnIndex === 0
? { background: "#f3b2d0" }
: { background: "#fafafa" };
}
function rowStyle({ rowIndex }) {
return rowIndex % 2 === 1
? { background: "#ffa39e" }
: { background: "#91d5ff" };
}
return {
columns,
dataList,
print,
rowStyle,
cellStyle,
headerCellStyle
};
}

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const printRef = ref();
const { columns, dataList, print, cellStyle, rowStyle, headerCellStyle } =
useColumns(printRef);
</script>
<template>
<div>
<el-button type="primary" class="mb-[20px] float-right" @click="print">
打印
</el-button>
<!-- rowHoverBgColor="transparent" 鼠标经过行时去掉行的背景色 -->
<pure-table
ref="printRef"
rowHoverBgColor="transparent"
row-key="id"
border
:data="dataList"
:columns="columns"
:row-style="rowStyle"
:cell-style="cellStyle"
:header-cell-style="headerCellStyle"
/>
</div>
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { ref } from "vue";
import radioSelectTable from "./radio/index.vue";
import multipleSelectTable from "./multiple/index.vue";
const model = ref("radio");
</script>
<template>
<el-space>
<el-radio-group v-model="model">
<el-radio-button value="radio">单选</el-radio-button>
<el-radio-button value="multiple">多选</el-radio-button>
</el-radio-group>
<el-divider direction="vertical" />
<component
:is="model === 'radio' ? radioSelectTable : multipleSelectTable"
/>
</el-space>
</template>

View File

@@ -0,0 +1,142 @@
import { message } from "@/utils/message";
import { tableDataEdit } from "../../data";
import type { PaginationProps } from "@pureadmin/table";
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
import { type Ref, ref, reactive, watch, nextTick } from "vue";
export function useColumns(selectRef: Ref, formRef: Ref, tableRef: Ref) {
const tableData = ref(tableDataEdit);
const cloneTableData = cloneDeep(tableData.value);
const selectValue = ref([1, 3, 4]);
const searchForm = reactive({
sexValue: "",
searchDate: ""
});
const sexOptions = [
{
value: 0,
label: "男"
},
{
value: 1,
label: "女"
}
];
const columns: TableColumnList = [
{
type: "selection",
reserveSelection: true,
align: "left"
},
{
label: "ID",
prop: "id",
width: 50
},
{
label: "姓名",
prop: "name"
},
{
label: "性别",
prop: "sex"
},
{
label: "地址",
prop: "address"
},
{
label: "日期",
prop: "date",
minWidth: 120
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 5,
currentPage: 1,
layout: "prev, pager, next",
total: tableData.value.length,
background: true,
small: true
});
const handleSelectionChange = val => {
const arr = [];
val.forEach(v => {
arr.push({ label: v.name, id: v.id });
});
selectValue.value = arr;
};
const onSearch = () => {
tableData.value = cloneTableData;
if (!isAllEmpty(searchForm.sexValue)) {
let sex = sexOptions
.map(sex => sex.value === Number(searchForm.sexValue) && sex.label)
.filter(Boolean)[0];
tableData.value = tableData.value.filter(data => data.sex === sex);
}
if (!isAllEmpty(searchForm.searchDate)) {
tableData.value = tableData.value.filter(
data => data.date === searchForm.searchDate
);
}
pagination.total = tableData.value.length;
};
const onReset = () => {
formRef.value.resetFields();
onClear();
tableData.value = cloneTableData;
pagination.total = tableData.value.length;
};
const removeTag = ({ id }) => {
const { toggleRowSelection } = tableRef.value.getTableRef();
toggleRowSelection(tableData.value.filter(v => v.id == id)?.[0], false);
};
const onClear = () => {
const { clearSelection } = tableRef.value.getTableRef();
clearSelection();
};
const onSure = () => {
selectRef.value.blur();
message(`当前选中的数据为:${JSON.stringify(selectValue.value)}`, {
type: "success"
});
};
watch(
selectValue,
async () => {
await nextTick();
const { toggleRowSelection } = tableRef.value.getTableRef();
selectValue.value.forEach(val => {
tableData.value.forEach(row => {
// 默认回显
row.id === val ? toggleRowSelection(row) : undefined;
});
});
},
{ immediate: true }
);
return {
searchForm,
sexOptions,
columns,
pagination,
selectValue,
tableData,
onSure,
onClear,
onReset,
onSearch,
removeTag,
handleSelectionChange
};
}

View File

@@ -0,0 +1,107 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const formRef = ref();
const tableRef = ref();
const selectRef = ref();
const {
searchForm,
sexOptions,
columns,
pagination,
selectValue,
tableData,
onSure,
onClear,
onReset,
onSearch,
removeTag,
handleSelectionChange
} = useColumns(selectRef, formRef, tableRef);
</script>
<template>
<el-select
ref="selectRef"
v-model="selectValue"
class="!w-[200px]"
placeholder="请选择"
clearable
multiple
collapse-tags
value-key="id"
@remove-tag="removeTag"
@clear="onClear"
>
<template #empty>
<div class="m-4">
<!-- <el-config-provider size="small"> -->
<el-form ref="formRef" :inline="true" :model="searchForm">
<el-form-item prop="sexValue">
<el-select
v-model="searchForm.sexValue"
class="!w-[120px]"
placeholder="请选择性别"
:teleported="false"
clearable
>
<el-option
v-for="item in sexOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item prop="searchDate">
<el-date-picker
v-model="searchForm.searchDate"
class="!w-[150px]"
type="date"
placeholder="请选择日期"
format="YYYY/MM/DD"
value-format="YYYY-MM-D"
/>
</el-form-item>
<el-form-item class="float-right !mr-0">
<el-button type="primary" text bg @click="onSearch">
查询
</el-button>
<el-button text bg @click="onReset"> 重置 </el-button>
</el-form-item>
</el-form>
<pure-table
ref="tableRef"
row-key="id"
alignWhole="center"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
:data="
tableData.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@selection-change="handleSelectionChange"
/>
<el-button
class="absolute bottom-[25px] left-[20px]"
type="primary"
size="small"
text
bg
@click="onSure"
>
确定
</el-button>
<!-- </el-config-provider> -->
</div>
</template>
</el-select>
</template>

View File

@@ -0,0 +1,62 @@
import { message } from "@/utils/message";
import { tableDataEdit } from "../../data";
import { ref, reactive, type Ref } from "vue";
import type { PaginationProps } from "@pureadmin/table";
export function useColumns(selectRef: Ref) {
const selectValue = ref("");
const columns: TableColumnList = [
{
label: "ID",
prop: "id",
width: 80
},
{
label: "日期",
prop: "date",
minWidth: 120
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
/** 分页配置 */
const pagination = reactive<PaginationProps>({
pageSize: 5,
currentPage: 1,
layout: "prev, pager, next",
total: tableDataEdit.length,
background: true,
small: true
});
/** 高亮当前选中行 */
function rowStyle({ row: { name } }) {
return {
cursor: "pointer",
background: name === selectValue.value ? "var(--el-fill-color-light)" : ""
};
}
/** 行点击 */
function onRowClick(row) {
selectValue.value = row.name;
selectRef.value.blur();
message(`当前选中行的数据为:${JSON.stringify(row)}`, { type: "success" });
}
return {
columns,
pagination,
selectValue,
tableDataEdit,
rowStyle,
onRowClick
};
}

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const selectRef = ref();
const {
columns,
pagination,
selectValue,
tableDataEdit,
rowStyle,
onRowClick
} = useColumns(selectRef);
</script>
<template>
<el-select
ref="selectRef"
v-model="selectValue"
class="!w-[200px]"
placeholder="请选择"
value-key="id"
clearable
>
<template #empty>
<div class="m-4">
<pure-table
row-key="id"
alignWhole="center"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
:row-style="rowStyle"
:data="
tableDataEdit.slice(
(pagination.currentPage - 1) * pagination.pageSize,
pagination.currentPage * pagination.pageSize
)
"
:columns="columns"
:pagination="pagination"
@row-click="onRowClick"
/>
</div>
</template>
</el-select>
</template>

View File

@@ -0,0 +1,47 @@
import { tableDataEdit } from "../data";
import { ref, onMounted, type Ref } from "vue";
import { clone, useWatermark, delay } from "@pureadmin/utils";
export function useColumns(waterRef: Ref) {
const dataList = ref(clone(tableDataEdit, true));
const columns: TableColumnList = [
{
label: "ID",
prop: "id"
},
{
label: "日期",
prop: "date"
},
{
label: "姓名",
prop: "name"
},
{
label: "地址",
prop: "address"
}
];
onMounted(() => {
delay().then(() => {
// https://pure-admin-utils.netlify.app/hooks/useWatermark/useWatermark.html
const { setWatermark } = useWatermark(
waterRef.value.getTableDoms().tableWrapper
);
setWatermark("编程即艺术", {
font: "16px Microsoft YaHei",
globalAlpha: 0.8,
forever: true,
width: 240,
height: 90
});
});
});
return {
columns,
dataList
};
}

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import { ref } from "vue";
import { useColumns } from "./columns";
const waterRef = ref();
const { columns, dataList } = useColumns(waterRef);
</script>
<template>
<pure-table
ref="waterRef"
row-key="id"
border
:data="dataList"
:columns="columns"
/>
</template>

90
src/views/table/index.vue Normal file
View File

@@ -0,0 +1,90 @@
<script setup lang="ts">
import { ref } from "vue";
import { list } from "./base/list";
defineOptions({
name: "PureTable"
});
const selected = ref(0);
function tabClick({ index }) {
selected.value = index;
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
二次封装 element-plus
<el-link
href="https://element-plus.org/zh-CN/component/table.html"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
el-table
</el-link>
完全兼容 api 并提供灵活的配置项以及完善的类型提示不用将代码都写在
template 里了
<el-link
href="https://github.com/pure-admin/pure-admin-table"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
@pureadmin/table 源码
</el-link>
</span>
</div>
</template>
<el-alert
title="基础用法中大部分表格都没设置 row-key 不过最好都设置一下后端需返回唯一值的字段比如id。作用1. 用来优化 Table
的渲染尤其当字段在深层结构中2. 防止某些操作导致表格组件内部混乱"
type="info"
:closable="false"
/>
<el-tabs @tab-click="tabClick">
<template v-for="(item, index) of list" :key="item.key">
<el-tab-pane :lazy="true">
<template #label>
<el-tooltip
:content="`(第 ${index + 1} 个示例)${item.content}`"
placement="top-end"
>
<span>{{ item.title }}</span>
</el-tooltip>
</template>
<component :is="item.component" v-if="selected == index" />
</el-tab-pane>
</template>
</el-tabs>
</el-card>
</template>
<style scoped>
:deep(.el-tabs__nav-wrap)::after {
height: 1px;
}
:deep(.el-tabs__header) {
margin-top: 10px;
}
:deep(.el-alert__title) {
font-size: 16px;
}
:deep(.el-tabs__nav-next),
:deep(.el-tabs__nav-prev) {
font-size: 16px;
color: var(--el-text-color-primary);
}
:deep(.el-tabs__nav-next.is-disabled),
:deep(.el-tabs__nav-prev.is-disabled) {
opacity: 0.5;
}
</style>

View File

@@ -0,0 +1,66 @@
<script setup lang="ts">
import { ref } from "vue";
import { list } from "./virtual/list";
defineOptions({
name: "VxeTable"
});
const selected = ref(0);
function tabClick({ index }) {
selected.value = index;
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
虚拟滚动采用
<el-link
href="https://vxetable.cn/#/table/scroll/scroll"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
vxe-table
</el-link>
</span>
</div>
</template>
<el-tabs @tab-click="tabClick">
<template v-for="(item, index) of list" :key="item.key">
<el-tab-pane :lazy="true">
<template #label>
<el-tooltip
:content="`(第 ${index + 1} 个示例)${item.content}`"
placement="top-end"
>
<span>{{ item.title }}</span>
</el-tooltip>
</template>
<component :is="item.component" v-if="selected == index" />
</el-tab-pane>
</template>
</el-tabs>
</el-card>
</template>
<style scoped>
:deep(.el-tabs__nav-wrap)::after {
height: 1px;
}
:deep(.el-tabs__nav-next),
:deep(.el-tabs__nav-prev) {
font-size: 16px;
color: var(--el-text-color-primary);
}
:deep(.el-tabs__nav-next.is-disabled),
:deep(.el-tabs__nav-prev.is-disabled) {
opacity: 0.5;
}
</style>

View File

@@ -0,0 +1,20 @@
import List from "./list.vue";
import TreeList from "./treeList.vue";
const rendContent = (val: string) =>
`代码位置src/views/table/virtual/${val}.vue`;
export const list = [
{
key: "list",
content: rendContent("list"),
title: "虚拟列表",
component: List
},
{
key: "treeList",
content: rendContent("treeList"),
title: "虚拟树",
component: TreeList
}
];

View File

@@ -0,0 +1,35 @@
<script lang="ts" setup>
import { ref } from "vue";
const tableData = ref([]);
setTimeout(() => {
// 模拟数据
const mockList = [];
for (let index = 0; index < 500; index++) {
mockList.push({
id: index,
name: "Test" + index,
role: "Developer",
sex: "男"
});
}
tableData.value = mockList;
}, 100);
</script>
<template>
<vxe-table
border
show-overflow
height="500"
:row-config="{ isHover: true }"
:data="tableData"
:scroll-y="{ enabled: true }"
>
<vxe-column type="seq" title="序号" width="100" />
<vxe-column field="name" title="名称" sortable />
<vxe-column field="role" title="角色" />
<vxe-column field="sex" title="性别" />
</vxe-table>
</template>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
<script lang="ts" setup>
import { ref } from "vue";
import treeList from "./tree.json";
const tableRef = ref();
const loading = ref(false);
const tableData = ref([]);
const loadList = () => {
loading.value = true;
setTimeout(() => {
tableData.value = treeList;
loading.value = false;
}, 200);
};
const expandAllEvent = () => {
const $table = tableRef.value;
if ($table) {
$table.setAllTreeExpand(true);
}
};
const claseExpandEvent = () => {
const $table = tableRef.value;
if ($table) {
$table.clearTreeExpand();
}
};
loadList();
</script>
<template>
<div>
<div class="mb-4">
<el-button @click="expandAllEvent">展开所有</el-button>
<el-button @click="claseExpandEvent">收起所有</el-button>
</div>
<vxe-table
ref="tableRef"
show-overflow
height="500"
:loading="loading"
:tree-config="{ transform: true }"
:scroll-y="{ enabled: true, gt: 20 }"
:data="tableData"
>
<vxe-column type="seq" title="序号" width="200" tree-node />
<vxe-column field="id" title="Id" />
<vxe-column field="name" title="地点" />
</vxe-table>
</div>
</template>