feat: 新增 @pureadmin/table 行、列拖拽示例

This commit is contained in:
xiaoxian521 2022-11-23 12:03:17 +08:00
parent 27d9339a4c
commit 7472c25c0a
44 changed files with 406 additions and 90 deletions

View File

@ -95,6 +95,8 @@ menus:
hsExecl: Export Excel hsExecl: Export Excel
hsInfiniteScroll: Table Infinite Scroll hsInfiniteScroll: Table Infinite Scroll
hsdanmaku: Danmaku Components hsdanmaku: Danmaku Components
hsPureTableBase: Base Usage
hsPureTableHigh: High Usage
status: status:
hsLoad: Loading... hsLoad: Loading...
login: login:

View File

@ -95,6 +95,8 @@ menus:
hsExecl: 导出Excel hsExecl: 导出Excel
hsInfiniteScroll: 表格无限滚动 hsInfiniteScroll: 表格无限滚动
hsdanmaku: 弹幕组件 hsdanmaku: 弹幕组件
hsPureTableBase: 基础用法
hsPureTableHigh: 高级用法
status: status:
hsLoad: 加载中... hsLoad: 加载中...
login: login:

View File

@ -34,7 +34,7 @@
"@logicflow/extension": "^1.1.30", "@logicflow/extension": "^1.1.30",
"@pureadmin/components": "^1.1.0", "@pureadmin/components": "^1.1.0",
"@pureadmin/descriptions": "^1.1.0", "@pureadmin/descriptions": "^1.1.0",
"@pureadmin/table": "^1.6.0", "@pureadmin/table": "^1.7.0",
"@pureadmin/utils": "^1.6.7", "@pureadmin/utils": "^1.6.7",
"@vueuse/core": "^9.5.0", "@vueuse/core": "^9.5.0",
"@vueuse/motion": "2.0.0-beta.12", "@vueuse/motion": "2.0.0-beta.12",
@ -107,7 +107,7 @@
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/qrcode": "^1.4.2", "@types/qrcode": "^1.4.2",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@types/sortablejs": "^1.13.0", "@types/sortablejs": "^1.15.0",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-vue": "^3.2.0", "@vitejs/plugin-vue": "^3.2.0",

View File

@ -20,7 +20,7 @@ specifiers:
"@logicflow/extension": ^1.1.30 "@logicflow/extension": ^1.1.30
"@pureadmin/components": ^1.1.0 "@pureadmin/components": ^1.1.0
"@pureadmin/descriptions": ^1.1.0 "@pureadmin/descriptions": ^1.1.0
"@pureadmin/table": ^1.6.0 "@pureadmin/table": ^1.7.0
"@pureadmin/theme": ^2.4.0 "@pureadmin/theme": ^2.4.0
"@pureadmin/utils": ^1.6.7 "@pureadmin/utils": ^1.6.7
"@types/element-resize-detector": 1.1.3 "@types/element-resize-detector": 1.1.3
@ -33,7 +33,7 @@ specifiers:
"@types/nprogress": 0.2.0 "@types/nprogress": 0.2.0
"@types/qrcode": ^1.4.2 "@types/qrcode": ^1.4.2
"@types/qs": ^6.9.7 "@types/qs": ^6.9.7
"@types/sortablejs": ^1.13.0 "@types/sortablejs": ^1.15.0
"@typescript-eslint/eslint-plugin": ^5.43.0 "@typescript-eslint/eslint-plugin": ^5.43.0
"@typescript-eslint/parser": ^5.43.0 "@typescript-eslint/parser": ^5.43.0
"@vitejs/plugin-vue": ^3.2.0 "@vitejs/plugin-vue": ^3.2.0
@ -132,7 +132,7 @@ dependencies:
"@logicflow/extension": 1.1.31 "@logicflow/extension": 1.1.31
"@pureadmin/components": 1.1.0_vue@3.2.45 "@pureadmin/components": 1.1.0_vue@3.2.45
"@pureadmin/descriptions": 1.1.1_element-plus@2.2.22 "@pureadmin/descriptions": 1.1.1_element-plus@2.2.22
"@pureadmin/table": 1.6.0_element-plus@2.2.22 "@pureadmin/table": 1.7.0_element-plus@2.2.22
"@pureadmin/utils": 1.6.7_aotapuqn7htzdjltsyimavekky "@pureadmin/utils": 1.6.7_aotapuqn7htzdjltsyimavekky
"@vueuse/core": 9.5.0_vue@3.2.45 "@vueuse/core": 9.5.0_vue@3.2.45
"@vueuse/motion": 2.0.0-beta.12_vue@3.2.45 "@vueuse/motion": 2.0.0-beta.12_vue@3.2.45
@ -1379,10 +1379,10 @@ packages:
vue: 3.2.45 vue: 3.2.45
dev: false dev: false
/@pureadmin/table/1.6.0_element-plus@2.2.22: /@pureadmin/table/1.7.0_element-plus@2.2.22:
resolution: resolution:
{ {
integrity: sha512-ryTZbfkNT/PTUS6pdrq7vuHr3f74lXs6kgRHaAPz64iTZmzaeVzf27TPakf4YDEcnQ/Gw6RqlQzE71W9m+P48w== integrity: sha512-6SWHJFiMf0V3L+Y5/1MZMClZo8QrOvkpLjkqeaZFO5N80Th17Nv1I4Feve6HPPS4dFBf5J0oRPr8L6OjjdaASw==
} }
peerDependencies: peerDependencies:
element-plus: ^2.0.0 element-plus: ^2.0.0

1
src/assets/svg/hot.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="20" height="20"><path d="M428.698 107.315c-6.503 72.192-36.352 207.258-160.256 337.408 3.686-48.025-7.117-83.763-19.047-107.673-6.605-13.159-26.06-10.599-28.877 3.84-5.734 29.44-20.582 75.059-57.6 137.779-71.628 121.395-62.566 459.878 340.736 459.878S934.093 585.728 876.8 442.522c-37.376-93.44-93.952-152.525-128.82-182.324-11.417-9.779-29.132-1.945-29.593 13.056-.921 30.464-7.321 73.37-33.075 102.144-.666-52.787-38.144-208.384-202.445-296.857-23.296-12.544-51.763 2.457-54.17 28.774z" fill="#FF5D50"/><path d="M702.26 678.4c-4.2-45.056-60.673-166.554-212.634-246.426-10.599-5.58-23.092 3.124-21.504 15.002 6.246 46.848 12.953 140.493-24.064 184.73 4.044-40.397-18.125-73.83-36.66-94.31-8.396-9.217-23.552-4.66-25.497 7.68-3.533 22.322-12.851 56.268-36.557 97.945-42.086 74.035-86.989 188.672 124.57 294.656 10.956.563 22.17.87 33.74.87 11.213 0 22.068-.307 32.717-.87C694.631 878.182 709.837 759.706 702.26 678.4z" fill="#FFDF99"/></svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@ -1,10 +1,12 @@
import { $t } from "@/plugins/i18n";
import hot from "@/assets/svg/hot.svg?component";
import type { RouteConfigsTable } from "/#/index"; import type { RouteConfigsTable } from "/#/index";
const flowChartRouter: RouteConfigsTable = { const flowChartRouter: RouteConfigsTable = {
path: "/pure-table", path: "/pure-table",
redirect: "/pure-table/index", redirect: "/pure-table/index",
meta: { meta: {
icon: "mdi:table-large", icon: hot,
title: "pure-admin-table", title: "pure-admin-table",
rank: 4 rank: 4
}, },
@ -14,11 +16,15 @@ const flowChartRouter: RouteConfigsTable = {
name: "PureTable", name: "PureTable",
component: () => import("@/views/pure-table/index.vue"), component: () => import("@/views/pure-table/index.vue"),
meta: { meta: {
title: "pure-admin-table", title: $t("menus.hsPureTableBase")
extraIcon: { }
svg: true, },
name: "team-iconxinpin" {
} path: "/pure-table/high",
name: "PureTableHigh",
component: () => import("@/views/pure-table/high.vue"),
meta: {
title: $t("menus.hsPureTableHigh")
} }
} }
] ]

View File

@ -19,7 +19,9 @@ export const useAppStore = defineStore({
layout: layout:
storageLocal.getItem<StorageConfigs>("responsive-layout")?.layout ?? storageLocal.getItem<StorageConfigs>("responsive-layout")?.layout ??
getConfig().Layout, getConfig().Layout,
device: deviceDetection() ? "mobile" : "desktop" device: deviceDetection() ? "mobile" : "desktop",
// 作用于 src/views/components/draggable/index.vue 页面,当离开页面并不会销毁 new Swap()sortablejs 官网也没有提供任何销毁的 api
sortSwap: false
}), }),
getters: { getters: {
getSidebarStatus() { getSidebarStatus() {
@ -56,6 +58,9 @@ export const useAppStore = defineStore({
}, },
setLayout(layout) { setLayout(layout) {
this.layout = layout; this.layout = layout;
},
setSortSwap(val) {
this.sortSwap = val;
} }
} }
}); });

View File

@ -19,6 +19,7 @@ export type appType = {
}; };
layout: string; layout: string;
device: string; device: string;
sortSwap: boolean;
}; };
export type multiType = { export type multiType = {

View File

@ -1,13 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import Sortable from "sortablejs";
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import Sortable, { Swap } from "sortablejs";
import draggable from "vuedraggable/src/vuedraggable"; import draggable from "vuedraggable/src/vuedraggable";
import { useAppStoreHook } from "@/store/modules/app";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
defineOptions({ defineOptions({
name: "Draggable" name: "Draggable"
}); });
const { setSortSwap } = useAppStoreHook();
const gridLists = ref<Array<Object>>([ const gridLists = ref<Array<Object>>([
{ grid: "cn", num: 1 }, { grid: "cn", num: 1 },
{ grid: "cn", num: 2 }, { grid: "cn", num: 2 },
@ -39,7 +42,8 @@ const change = (evt): void => {
}; };
onMounted(() => { onMounted(() => {
// 使sortable if (!useAppStoreHook().sortSwap) Sortable.mount(new Swap());
setSortSwap(true);
new Sortable(document.querySelector(".cut-container"), { new Sortable(document.querySelector(".cut-container"), {
swap: true, swap: true,
forceFallback: true, forceFallback: true,
@ -121,7 +125,7 @@ onMounted(() => {
<el-card> <el-card>
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>拖拽实现元素位置</span> <span>拖拽实现元素位置</span>
</div> </div>
</template> </template>
<!-- 拖拽实现元素位置切换 --> <!-- 拖拽实现元素位置切换 -->

View File

@ -1,30 +1,28 @@
import { import Base from "./base.vue";
Base, import Stripe from "./stripe.vue";
Stripe, import Border from "./border.vue";
Border, import Status from "./status.vue";
Status, import FixHeader from "./fixHeader.vue";
FixHeader, import FixColumn from "./fixColumn.vue";
FixColumn, import FluidHeight from "./fluidHeight.vue";
FluidHeight, import GroupHeader from "./groupHeader.vue";
GroupHeader, import Radio from "./radio.vue";
Radio, import MultipleChoice from "./multipleChoice.vue";
MultipleChoice, import Sortable from "./sortable.vue";
Sortable, import Filters from "./filters.vue";
Filters, import ColumnTemplate from "./column-template/index.vue";
ColumnTemplate, import HeaderRenderer from "./header-renderer/index.vue";
HeaderRenderer, import Expand from "./expand.vue";
Expand, import TreeTable from "./tree.vue";
TreeTable, import TotalRow from "./totalRow.vue";
TotalRow, import Merge from "./merge.vue";
Merge, import CustomIndex from "./customIndex.vue";
CustomIndex, import Layout from "./layout.vue";
Layout, import NestProp from "./nestProp.vue";
NestProp, import ImgPreview from "./imgPreview.vue";
ImgPreview
} from "./components";
const rendContent = (val: string) => const rendContent = (val: string) =>
`代码位置src/views/pure-table/components/${val}.vue`; `代码位置src/views/pure-table/base/${val}.vue`;
export const list = [ export const list = [
{ {

View File

@ -1,47 +0,0 @@
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";
export {
Base,
Stripe,
Border,
Status,
FixHeader,
FixColumn,
FluidHeight,
GroupHeader,
Radio,
MultipleChoice,
Sortable,
Filters,
ColumnTemplate,
HeaderRenderer,
Expand,
TreeTable,
TotalRow,
Merge,
CustomIndex,
Layout,
NestProp,
ImgPreview
};

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import { list } from "./high/list";
import { Tabs, TabPane } from "@pureadmin/components";
defineOptions({
name: "PureTableHigh"
});
</script>
<template>
<el-card>
<template #header>
<div class="card-header">
<span class="font-medium">
高级用法全部采用 tsx 语法充分发挥
<el-link
href="https://github.com/xiaoxian521/pure-admin-table"
target="_blank"
style="font-size: 16px; margin: 0 4px 5px"
>
@pureadmin/table
</el-link>
的灵活性维护整体表格只需操作 columns 配置即可
</span>
</div>
</template>
<el-alert
title="高级用法中所有表格都设置了 row-key 后端需返回唯一值的字段比如id 作用1. 用来优化 Table
的渲染尤其当字段在深层结构中2. 防止拖拽后表格组件内部混乱拖拽必须设置哦坑都帮您们踩过啦 "
type="info"
:closable="false"
/>
<Tabs :destroyInactiveTabPane="true">
<TabPane v-for="item of list" :key="item.key">
<template #tab>
<el-tooltip :content="item.content" placement="top-end">
<span>{{ item.title }}</span>
</el-tooltip>
</template>
<component :is="item.component" />
</TabPane>
</Tabs>
</el-card>
</template>
<style scoped>
:deep(.ant-tabs-content-holder) {
margin-top: 10px;
}
:deep(.el-alert__title) {
font-size: 16px;
}
</style>

View File

@ -0,0 +1,88 @@
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 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://xiaoxian521.github.io/pure-admin-table/imgs/${
index + 1
}.jpg`
})
);
const tableDataSortable = clone(tableData, true).map((item, index) =>
Object.assign(item, {
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`
})
);
const tableDataDrag = clone(tableData, true).map((item, index) => {
delete item["address"];
return Object.assign(
{
id: index + 1,
date: `${dayjs(new Date()).format("YYYY-MM")}-${index + 1}`
},
item
);
});
export {
tableData,
tableDataDrag,
tableDataMore,
tableDataImage,
tableDataSortable
};

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,20 @@
import RowDrag from "./drag/row/index.vue";
import ColumnDrag from "./drag/column/index.vue";
const rendContent = (val: string) =>
`代码位置src/views/pure-table/high/${val}/index.vue`;
export const list = [
{
key: "rowDrag",
content: rendContent("drag/row"),
title: "拖拽表格(行拖拽)",
component: RowDrag
},
{
key: "columnDrag",
content: rendContent("drag/column"),
title: "拖拽表格(列拖拽)",
component: ColumnDrag
}
];

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { list } from "./list"; import { list } from "./base/list";
import { Tabs, TabPane } from "@pureadmin/components"; import { Tabs, TabPane } from "@pureadmin/components";
defineOptions({ defineOptions({
@ -26,6 +26,13 @@ defineOptions({
</div> </div>
</template> </template>
<el-alert
title="基础用法中大部分表格都没设置 row-key 不过最好都设置一下后端需返回唯一值的字段比如id 作用1. 用来优化 Table
的渲染尤其当字段在深层结构中2. 防止某些操作导致表格组件内部混乱"
type="info"
:closable="false"
/>
<Tabs :destroyInactiveTabPane="true"> <Tabs :destroyInactiveTabPane="true">
<TabPane v-for="item of list" :key="item.key"> <TabPane v-for="item of list" :key="item.key">
<template #tab> <template #tab>
@ -43,4 +50,8 @@ defineOptions({
:deep(.ant-tabs-content-holder) { :deep(.ant-tabs-content-holder) {
margin-top: 10px; margin-top: 10px;
} }
:deep(.el-alert__title) {
font-size: 16px;
}
</style> </style>