feat: 拖拽表格

This commit is contained in:
kailong321200875
2023-07-20 16:37:18 +08:00
parent cfb3b3a5ce
commit b69b8ed1bd
18 changed files with 254 additions and 29 deletions

View File

@@ -78,7 +78,7 @@ export default defineComponent({
validateOnRuleChange: propTypes.bool.def(true),
size: {
type: String as PropType<ComponentSize>,
default: 'small'
default: undefined
},
disabled: propTypes.bool.def(false),
scrollToError: propTypes.bool.def(false),

View File

@@ -104,7 +104,9 @@ const getPasswordStrength = computed(() => {
height: inherit;
background-color: transparent;
border-radius: inherit;
transition: width 0.5s ease-in-out, background 0.25s;
transition:
width 0.5s ease-in-out,
background 0.25s;
&[data-score='0'] {
width: 20%;

View File

@@ -1,6 +1,13 @@
<script lang="tsx">
import { ElTable, ElTableColumn, ElPagination, ComponentSize, ElTooltipProps } from 'element-plus'
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
import {
ElTable,
ElTableColumn,
ElPagination,
ComponentSize,
ElTooltipProps,
ElImage
} from 'element-plus'
import { defineComponent, PropType, ref, computed, unref, watch, onMounted, nextTick } from 'vue'
import { propTypes } from '@/utils/propTypes'
import { setIndex } from './helper'
import type { TableProps, TableColumn, Pagination, TableSetProps } from './types'
@@ -8,6 +15,8 @@ import { set } from 'lodash-es'
import { CSSProperties } from 'vue'
import { getSlot } from '@/utils/tsxHelper'
import TableActions from './components/TableActions.vue'
import Sortable from 'sortablejs'
import { Icon } from '@/components/Icon'
export default defineComponent({
name: 'Table',
@@ -48,6 +57,12 @@ export default defineComponent({
type: Array as PropType<Recordable[]>,
default: () => []
},
// 是否自动预览
preview: {
type: Array as PropType<string[]>,
default: () => []
},
sortable: propTypes.bool.def(false),
height: propTypes.oneOfType([Number, String]),
maxHeight: propTypes.oneOfType([Number, String]),
stripe: propTypes.bool.def(false),
@@ -173,7 +188,7 @@ export default defineComponent({
scrollbarAlwaysOn: propTypes.bool.def(false),
flexible: propTypes.bool.def(false)
},
emits: ['update:pageSize', 'update:currentPage', 'register', 'refresh'],
emits: ['update:pageSize', 'update:currentPage', 'register', 'refresh', 'sortable-change'],
setup(props, { attrs, emit, slots, expose }) {
const elTableRef = ref<ComponentRef<typeof ElTable>>()
@@ -198,6 +213,33 @@ export default defineComponent({
return propsObj
})
const sortableEl = ref()
// 初始化拖拽
const initDropTable = () => {
const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody')
if (!el) return
if (unref(sortableEl)) unref(sortableEl).destroy()
sortableEl.value = Sortable.create(el, {
handle: '.table-move',
animation: 180,
onEnd(e: any) {
emit('sortable-change', e)
}
})
}
watch(
() => getProps.value.sortable,
async (v) => {
await nextTick()
v && initDropTable()
},
{
immediate: true
}
)
const setProps = (props: TableProps = {}) => {
mergeProps.value = Object.assign(unref(mergeProps), props)
outsideProps.value = { ...props } as any
@@ -301,7 +343,7 @@ export default defineComponent({
})
const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
const { align, headerAlign, showOverflowTooltip } = unref(getProps)
const { align, headerAlign, showOverflowTooltip, preview } = unref(getProps)
return columnsChildren.map((v) => {
if (v.hidden) return null
const props = { ...v } as any
@@ -312,12 +354,20 @@ export default defineComponent({
const slots = {
default: (...args: any[]) => {
const data = args[0]
let isImageUrl = false
if (preview.length) {
isImageUrl = preview.some((item) => (item as string) === v.field)
}
return children && children.length
? renderTreeTableColumn(children)
: props?.slots?.default
? props.slots.default(args)
: v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
data.row[v.field]
: v?.formatter
? v?.formatter?.(data.row, data.column, data.row[v.field], data.$index)
: isImageUrl
? renderPreview(data.row[v.field])
: data.row[v.field]
}
}
if (props?.slots?.header) {
@@ -338,6 +388,21 @@ export default defineComponent({
})
}
const renderPreview = (url: string) => {
return (
<div class="flex items-center">
<ElImage
src={url}
fit="cover"
class="w-[100%] h-100px"
lazy
preview-src-list={[url]}
preview-teleported
/>
</div>
)
}
const renderTableColumn = (columnsChildren?: TableColumn[]) => {
const {
columns,
@@ -347,7 +412,8 @@ export default defineComponent({
align,
headerAlign,
showOverflowTooltip,
reserveSelection
reserveSelection,
preview
} = unref(getProps)
return (columnsChildren || columns).map((v) => {
@@ -384,12 +450,21 @@ export default defineComponent({
const slots = {
default: (...args: any[]) => {
const data = args[0]
let isImageUrl = false
if (preview.length) {
isImageUrl = preview.some((item) => (item as string) === v.field)
}
return children && children.length
? renderTreeTableColumn(children)
: props?.slots?.default
? props.slots.default(args)
: v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
data.row[v.field]
: v?.formatter
? v?.formatter?.(data.row, data.column, data.row[v.field], data.$index)
: isImageUrl
? renderPreview(data.row[v.field])
: data.row[v.field]
}
}
if (props?.slots?.header) {
@@ -419,6 +494,21 @@ export default defineComponent({
if (getSlot(slots, 'append')) {
tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args)
}
const { sortable } = unref(getProps)
const sortableEl = sortable ? (
<ElTableColumn
className="table-move cursor-move"
type="sortable"
prop="sortable"
width="60px"
align="center"
>
<Icon icon="ant-design:drag-outlined" />
</ElTableColumn>
) : null
return (
<div v-loading={unref(getProps).loading}>
{unref(getProps).showAction ? (
@@ -426,7 +516,7 @@ export default defineComponent({
) : null}
<ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>
{{
default: () => renderTableColumn(),
default: () => [sortableEl, ...renderTableColumn()],
...tableSlots
}}
</ElTable>

View File

@@ -91,5 +91,7 @@ export interface TableProps extends Omit<Partial<ElTableProps<any[]>>, 'data'> {
align?: 'left' | 'center' | 'right'
// 表头对齐方式
headerAlign?: 'left' | 'center' | 'right'
preview?: string[]
sortable?: boolean
data?: Recordable
}

View File

@@ -94,7 +94,9 @@ const toDocument = () => {
<style scoped lang="less">
.fade-bottom-enter-active,
.fade-bottom-leave-active {
transition: opacity 0.25s, transform 0.3s;
transition:
opacity 0.25s,
transform 0.3s;
}
.fade-bottom-enter-from {