mirror of
https://gitee.com/log4j/pig-ui.git
synced 2024-12-22 21:22:33 +08:00
✨ Introducing new features. 新增 popverInput 、表单动态表格组件
This commit is contained in:
parent
fb002a0ed6
commit
de9ece8096
197
src/components/FormTable/index.vue
Normal file
197
src/components/FormTable/index.vue
Normal 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>
|
105
src/components/PopoverInput/index.vue
Normal file
105
src/components/PopoverInput/index.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user