Introducing new features. 新增 popverInput 、表单动态表格组件

This commit is contained in:
lbw 2023-05-29 16:59:29 +08:00
parent fb002a0ed6
commit de9ece8096
2 changed files with 302 additions and 0 deletions

View File

@ -0,0 +1,197 @@
<template>
<div class="form-table" ref="scFormTable">
<el-table :data="data" ref="table" border stripe :cell-style="{ textAlign: 'center' }" :header-cell-style="{
textAlign: 'center',
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)',
}">
<el-table-column type="index" width="50" fixed="left">
<template #header>
<el-button v-if="!hideAdd" type="primary" icon="el-icon-plus" size="small" circle @click="rowAdd"></el-button>
<el-tooltip content="序号" placement="top">
#
</el-tooltip>
</template>
<template #default="scope">
<div :class="['form-table-handle', { 'form-table-handle-delete': !hideDelete }]">
<span>{{ scope.$index + 1 }}</span>
<el-button v-if="!hideDelete" type="danger" icon="el-icon-delete" size="small" plain circle
@click="rowDel(scope.row, scope.$index)"></el-button>
</div>
</template>
</el-table-column>
<el-table-column label="" width="50" v-if="dragSort">
<template #header>
<el-icon>
<el-tooltip content="拖动排序" placement="top">
<WarningFilled />
</el-tooltip>
</el-icon>
</template>
<template #default>
<div class="move" style="cursor: move;">
<el-icon>
<Sort />
</el-icon>
</div>
</template>
</el-table-column>
<slot></slot>
<template #empty>
{{ placeholder }}
</template>
</el-table>
</div>
</template>
<script>
import Sortable from 'sortablejs'
export default {
props: {
/**
* 表格数据
*/
modelValue: { type: Array, default: () => [] },
/**
* 新增行模板
*/
addTemplate: { type: Object, default: () => {} },
/**
* 无数据时的提示语
*/
placeholder: { type: String, default: "暂无数据" },
/**
* 是否启用拖拽排序
*/
dragSort: { type: Boolean, default: false },
/**
* 是否隐藏新增按钮
*/
hideAdd: { type: Boolean, default: false },
/**
* 是否隐藏删除按钮
*/
hideDelete: { type: Boolean, default: false }
},
data(){
return {
/**
* 表格数据
*/
data: []
}
},
mounted(){
this.data = this.modelValue
if(this.dragSort){
this.rowDrop()
}
},
watch:{
modelValue(){
this.data = this.modelValue
},
data: {
handler(){
/**
* 更新表格数据
* @event update:modelValue
* @type {Array}
*/
this.$emit('update:modelValue', this.data);
},
deep: true
}
},
methods: {
/**
* 启用表格行拖拽排序
*/
rowDrop(){
const _this = this
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
handle: ".move",
animation: 300,
ghostClass: "ghost",
onEnd({ newIndex, oldIndex }) {
_this.data.splice(newIndex, 0, _this.data.splice(oldIndex, 1)[0])
const newArray = _this.data.slice(0)
const tmpHeight = _this.$refs.scFormTable.offsetHeight
_this.$refs.scFormTable.style.setProperty('height', tmpHeight + 'px')
_this.data = []
_this.$nextTick(() => {
_this.data = newArray
_this.$nextTick(() => {
_this.$refs.scFormTable.style.removeProperty('height')
})
})
}
})
},
/**
* 新增一行
*/
rowAdd(){
const temp = JSON.parse(JSON.stringify(this.addTemplate))
this.data.push(temp)
},
/**
* 删除一行
* @param {Object} row - 要删除的行数据
* @param {number} index - 要删除的行的索引
*/
rowDel(row, index){
this.data.splice(index, 1)
},
/**
* 插入一行
* @param {Object} row - 要插入的行数据默认为新增行模板
*/
pushRow(row){
const temp = row || JSON.parse(JSON.stringify(this.addTemplate))
this.data.push(temp)
},
/**
* 根据索引删除一行
* @param {number} index - 要删除的行的索引
*/
deleteRow(index){
this.data.splice(index, 1)
}
}
}
</script>
<style scoped>
.form-table {
width: 100%;
}
.form-table .form-table-handle {
text-align: center;
}
.form-table .form-table-handle span {
display: inline-block;
}
.form-table .form-table-handle button {
display: none;
}
.form-table .hover-row .form-table-handle-delete span {
display: none;
}
.form-table .hover-row .form-table-handle-delete button {
display: inline-block;
}
.form-table .move {
text-align: center;
font-size: 14px;
margin-top: 3px;
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<div @mouseenter="inPopover = true" @mouseleave="inPopover = false">
<el-popover placement="top" v-model:visible="visible" :width="width" trigger="contextmenu" class="popover-input"
:teleported="teleported" :persistent="false" popper-class="!p-0">
<div class="flex p-3" @click.stop="">
<div class="popover-input__input mr-[10px] flex-1">
<el-select class="flex-1" :size="size" v-if="type == 'select'" v-model="inputValue"
:teleported="teleported">
<el-option v-for="item in options" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
<el-input v-else v-model.trim="inputValue" :maxlength="limit" :show-word-limit="showLimit" :type="type"
:size="size" clearable :placeholder="placeholder" />
</div>
<div class="flex-none popover-input__btns">
<el-button link @click="close">取消</el-button>
<el-button type="primary" :size="size" @click="handleConfirm">确定</el-button>
</div>
</div>
<template #reference>
<div class="inline" @click.stop="handleOpen">
<slot></slot>
</div>
</template>
</el-popover>
</div>
</template>
<script lang="ts" setup>
import { useEventListener } from '@vueuse/core'
import type { PropType } from 'vue'
const props = defineProps({
value: {
type: String
},
type: {
type: String,
default: 'text'
},
width: {
type: [Number, String],
default: '300px'
},
placeholder: String,
disabled: {
type: Boolean,
default: false
},
options: {
type: Array as PropType<any[]>,
default: () => []
},
size: {
type: String as PropType<'default' | 'small' | 'large'>,
default: 'default'
},
limit: {
type: Number,
default: 200
},
showLimit: {
type: Boolean,
default: false
},
teleported: {
type: Boolean,
default: true
}
})
const emit = defineEmits(['confirm'])
const visible = ref(false)
const inPopover = ref(false)
const inputValue = ref()
const handleConfirm = () => {
close()
emit('confirm', inputValue.value)
}
const handleOpen = () => {
if (props.disabled) {
return
}
visible.value = true
}
const close = () => {
visible.value = false
}
watch(
() => props.value,
(value) => {
inputValue.value = value
},
{
immediate: true
}
)
useEventListener(document.documentElement, 'click', () => {
if (inPopover.value) return
close()
})
</script>
<style scoped lang="scss"></style>