mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-11-15 14:03:36 +08:00
22
src/views/table/base/base.vue
Normal file
22
src/views/table/base/base.vue
Normal 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>
|
||||
22
src/views/table/base/border.vue
Normal file
22
src/views/table/base/border.vue
Normal 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>
|
||||
72
src/views/table/base/column-template/columns.tsx
Normal file
72
src/views/table/base/column-template/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
9
src/views/table/base/column-template/index.vue
Normal file
9
src/views/table/base/column-template/index.vue
Normal 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>
|
||||
30
src/views/table/base/customIndex.vue
Normal file
30
src/views/table/base/customIndex.vue
Normal 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>
|
||||
297
src/views/table/base/data.ts
Normal file
297
src/views/table/base/data.ts
Normal 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
|
||||
};
|
||||
72
src/views/table/base/expand.vue
Normal file
72
src/views/table/base/expand.vue
Normal 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>
|
||||
107
src/views/table/base/filters.vue
Normal file
107
src/views/table/base/filters.vue
Normal 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>
|
||||
79
src/views/table/base/fixColumn.vue
Normal file
79
src/views/table/base/fixColumn.vue
Normal 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>
|
||||
26
src/views/table/base/fixHeader.vue
Normal file
26
src/views/table/base/fixHeader.vue
Normal 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>
|
||||
84
src/views/table/base/fluidHeight.vue
Normal file
84
src/views/table/base/fluidHeight.vue
Normal 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>
|
||||
38
src/views/table/base/groupHeader.vue
Normal file
38
src/views/table/base/groupHeader.vue
Normal 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>
|
||||
72
src/views/table/base/header-renderer/columns.tsx
Normal file
72
src/views/table/base/header-renderer/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
9
src/views/table/base/header-renderer/index.vue
Normal file
9
src/views/table/base/header-renderer/index.vue
Normal 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>
|
||||
50
src/views/table/base/imgPreview.vue
Normal file
50
src/views/table/base/imgPreview.vue
Normal 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>
|
||||
38
src/views/table/base/layout.vue
Normal file
38
src/views/table/base/layout.vue
Normal 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>
|
||||
166
src/views/table/base/list.tsx
Normal file
166
src/views/table/base/list.tsx
Normal 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
|
||||
}
|
||||
];
|
||||
124
src/views/table/base/merge.vue
Normal file
124
src/views/table/base/merge.vue
Normal 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>
|
||||
57
src/views/table/base/multipleChoice.vue
Normal file
57
src/views/table/base/multipleChoice.vue
Normal 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>
|
||||
86
src/views/table/base/nestProp.vue
Normal file
86
src/views/table/base/nestProp.vue
Normal 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>
|
||||
47
src/views/table/base/radio.vue
Normal file
47
src/views/table/base/radio.vue
Normal 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>
|
||||
28
src/views/table/base/sortable.vue
Normal file
28
src/views/table/base/sortable.vue
Normal 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>
|
||||
46
src/views/table/base/status.vue
Normal file
46
src/views/table/base/status.vue
Normal 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>
|
||||
22
src/views/table/base/stripe.vue
Normal file
22
src/views/table/base/stripe.vue
Normal 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>
|
||||
144
src/views/table/base/totalRow.vue
Normal file
144
src/views/table/base/totalRow.vue
Normal 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>
|
||||
122
src/views/table/base/tree.vue
Normal file
122
src/views/table/base/tree.vue
Normal 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
82
src/views/table/edit.vue
Normal 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>
|
||||
74
src/views/table/edit/data.ts
Normal file
74
src/views/table/edit/data.ts
Normal 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 };
|
||||
87
src/views/table/edit/demo1/columns.tsx
Normal file
87
src/views/table/edit/demo1/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
58
src/views/table/edit/demo1/index.vue
Normal file
58
src/views/table/edit/demo1/index.vue
Normal 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>
|
||||
116
src/views/table/edit/demo2/columns.tsx
Normal file
116
src/views/table/edit/demo2/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
50
src/views/table/edit/demo2/index.vue
Normal file
50
src/views/table/edit/demo2/index.vue
Normal 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>
|
||||
87
src/views/table/edit/demo3/columns.tsx
Normal file
87
src/views/table/edit/demo3/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
22
src/views/table/edit/demo3/index.vue
Normal file
22
src/views/table/edit/demo3/index.vue
Normal 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>
|
||||
1
src/views/table/edit/empty.svg
Normal file
1
src/views/table/edit/empty.svg
Normal 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 |
27
src/views/table/edit/list.tsx
Normal file
27
src/views/table/edit/list.tsx
Normal 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
82
src/views/table/high.vue
Normal 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>
|
||||
105
src/views/table/high/adaptive/columns.tsx
Normal file
105
src/views/table/high/adaptive/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
41
src/views/table/high/adaptive/index.vue
Normal file
41
src/views/table/high/adaptive/index.vue
Normal 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>
|
||||
73
src/views/table/high/contextmenu/columns.tsx
Normal file
73
src/views/table/high/contextmenu/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
15
src/views/table/high/contextmenu/index.vue
Normal file
15
src/views/table/high/contextmenu/index.vue
Normal 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>
|
||||
106
src/views/table/high/data.ts
Normal file
106
src/views/table/high/data.ts
Normal 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
|
||||
};
|
||||
69
src/views/table/high/drag/column/columns.tsx
Normal file
69
src/views/table/high/drag/column/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
16
src/views/table/high/drag/column/index.vue
Normal file
16
src/views/table/high/drag/column/index.vue
Normal 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>
|
||||
70
src/views/table/high/drag/row/columns.tsx
Normal file
70
src/views/table/high/drag/row/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
16
src/views/table/high/drag/row/index.vue
Normal file
16
src/views/table/high/drag/row/index.vue
Normal 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>
|
||||
83
src/views/table/high/echarts/columns.tsx
Normal file
83
src/views/table/high/echarts/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
13
src/views/table/high/echarts/index.vue
Normal file
13
src/views/table/high/echarts/index.vue
Normal 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>
|
||||
52
src/views/table/high/excel/columns.tsx
Normal file
52
src/views/table/high/excel/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
18
src/views/table/high/excel/index.vue
Normal file
18
src/views/table/high/excel/index.vue
Normal 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>
|
||||
76
src/views/table/high/list.tsx
Normal file
76
src/views/table/high/list.tsx
Normal 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
|
||||
}
|
||||
];
|
||||
116
src/views/table/high/page/columns.tsx
Normal file
116
src/views/table/high/page/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
80
src/views/table/high/page/index.vue
Normal file
80
src/views/table/high/page/index.vue
Normal 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>
|
||||
60
src/views/table/high/prints/columns.tsx
Normal file
60
src/views/table/high/prints/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
28
src/views/table/high/prints/index.vue
Normal file
28
src/views/table/high/prints/index.vue
Normal 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>
|
||||
20
src/views/table/high/table-select/index.vue
Normal file
20
src/views/table/high/table-select/index.vue
Normal 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>
|
||||
142
src/views/table/high/table-select/multiple/columns.tsx
Normal file
142
src/views/table/high/table-select/multiple/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
107
src/views/table/high/table-select/multiple/index.vue
Normal file
107
src/views/table/high/table-select/multiple/index.vue
Normal 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>
|
||||
62
src/views/table/high/table-select/radio/columns.tsx
Normal file
62
src/views/table/high/table-select/radio/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
48
src/views/table/high/table-select/radio/index.vue
Normal file
48
src/views/table/high/table-select/radio/index.vue
Normal 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>
|
||||
47
src/views/table/high/watermark/columns.tsx
Normal file
47
src/views/table/high/watermark/columns.tsx
Normal 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
|
||||
};
|
||||
}
|
||||
17
src/views/table/high/watermark/index.vue
Normal file
17
src/views/table/high/watermark/index.vue
Normal 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
90
src/views/table/index.vue
Normal 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>
|
||||
66
src/views/table/virtual.vue
Normal file
66
src/views/table/virtual.vue
Normal 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>
|
||||
20
src/views/table/virtual/list.tsx
Normal file
20
src/views/table/virtual/list.tsx
Normal 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
|
||||
}
|
||||
];
|
||||
35
src/views/table/virtual/list.vue
Normal file
35
src/views/table/virtual/list.vue
Normal 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>
|
||||
16287
src/views/table/virtual/tree.json
Normal file
16287
src/views/table/virtual/tree.json
Normal file
File diff suppressed because it is too large
Load Diff
56
src/views/table/virtual/treeList.vue
Normal file
56
src/views/table/virtual/treeList.vue
Normal 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>
|
||||
Reference in New Issue
Block a user