封装
思路参考:封装Vue Element的可编辑table表格组件
代码: github.com/lyxxxh/erp-table-form
erp
表格最基础的封装,根据公司项目来完善。
实际情况复杂很多,就连 el-select
都需要重写(需求问题)。
我司的效果图:
环境
// element-plus-vite-starter = element-plus+vite+vue3集成
git clone https://github.com/element-plus/element-plus-vite-starter
// install
npm i
// start
npm run dev
效果图
src/components/TableForm
Display.vue
<template>
<h3>{{ str }}</h3>
</template>
<script setup lang="ts">
defineProps({
str:{
type: String,
default:() => ''
}
})
</script>
GoodSelect.vue
<template>
<div>
<el-select
filterable
remote
ref="selectRef"
:remote-method="remoteMethod"
:loading="loading"
@change="changeSelect"
v-model="value"
@keydown.tab.stop="tab" @keydown.enter.stop="enter"
>
<el-option
v-for="item in goods"
:key="item.id"
:label="item.goods_name"
:value="item.id"
/>
</el-select>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
const goods:any = ref([])
const loading = ref(false)
const emit = defineEmits(['selectedGood'])
const selectRef:any = ref(null)
const props = defineProps({
index: {
type: Number
},
goods_name: {
type: String,
default:() => ''
}
})
const value = ref('')
onMounted(() => {
setTimeout(() => (selectRef.value.selectedLabel = props.goods_name))
})
// 搜索商品
const remoteMethod = async (query: string) => {
loading.value = true
goods.value = await mockSearchGoods(query)
loading.value = false
}
// 选择商品
const changeSelect = (id:any) => {
emit('selectedGood',{
good: goods.value.find((good:any) => good.id === id),
index:props.index
})
}
// mock接口
const mockSearchGoods = async (query:string) => new Promise(resolve =>
setTimeout(() =>
resolve(randData())
,500)
)
// 随机返回数据
const randData = () => [
{'goods_name':'红牛','goods_bar_code':'69*****red','price':'2.00',id:1},
{'goods_name':'黄牛','goods_bar_code':'69*****yellow','price':'3.00',id:2},
{'goods_name':'蓝牛','goods_bar_code':'69*****blue','price':'4.00',id:3},
{'goods_name':'*牛','goods_bar_code':'69*****','price':'5.00',id:4},
{'goods_name':'*2','goods_bar_code':'69*****2','price':'6.00',id:5}
].sort(() => Math.random() - 0.5)
const tab = () => console.log('自定义tab键处理')
const enter = () => console.log('自定义enter键处理')
</script>
NumberInput.vue
<template>
<!-- 之所以不用el-input 忘记了 印象有坑就不用了 用css美化下input就好了-->
<input ref="inputRef" :value="modelValue" @input="oninput"
@keydown.tab.stop="tab" @keydown.enter.stop="enter" @blur="blurHandle">
</template>
<script setup lang="ts">
import { defineProps, nextTick, onMounted, ref, defineEmits } from 'vue'
const emit = defineEmits(['update:modelValue', 'enterTab'])
const oninput = (e: any) => emit('update:modelValue', e.target.value)
const props: any = defineProps({
modelValue: {}
})
const inputRef: any = ref(null)
onMounted(() =>
nextTick(() => {
inputRef.value.focus() // 聚焦
inputRef.value.select() // 全选
})
)
// 丢失焦点处理价格
const blurHandle = (e: any) => {
const price = e.target.value
if( false) // 价格不是数字之类的
emit('update:modelValue', '0.00')
emit('update:modelValue',parseFloat(price).toFixed(2))
// emit('update:modelValue', )
}
const tab = () => console.log('自定义tab键处理')
const enter = () => console.log('自定义enter键处理')
</script>
index.vue
<template>
<el-table :cell-style="{ textAlign: 'center' }" @cell-click="cellClick" :data="data">
<el-table-column v-for="column in columns" :key="column.field" :label="column.label" :min-width="column.minWidth">
<template #default="scope">
<!-- 表单处于不可编辑 或 字段本身就不可编辑 使用显示文本的组件 -->
<Display v-if="! isEdit || column.type === 'display'"
:str="scope.row[`${column.field}`]"/>
<GoodSelect v-if="column.type === 'good_select'" :goods_name="scope.row[`${column.field}`]"
:index="scope.$index" @selectedGood="selectedGood"/>
<NumberInput v-if="column.type === 'number_input'" :goods_name="scope.row[`${column.field}`]"
:index="scope.$index" v-model="scope.row[`${column.field}`]"/>
</template>
</el-table-column>
</el-table>
</template>
<script setup lang="ts">
import Display from './Display.vue'
import GoodSelect from './GoodSelect.vue'
import NumberInput from './NumberInput.vue'
const props = defineProps({
columns: { // 列
type: Object,
required: true,
default: () => {}
},
data: { // 数据
type: Array,
required: true,
default: () => []
},
isEdit: { // 是否可编辑
type: Boolean,
default: () => true
}
})
const cellClick = () => {
}
// 商品选择
const selectedGood = (data:any) => {
props.data[data.index] = data.good
}
</script>
<style scoped lang="scss">
:deep(.cell) {
text-align: center;
}
</style>
编辑src/App.vue
<template>
<TableFrom :columns="columns" :data="data" :isEdit="isEdit"/>
</template>
<script lang="ts" setup>
import TableFrom from './components/TableForm/index.vue'
import { onMounted, ref, getCurrentInstance } from 'vue';
const proxy = getCurrentInstance()
const columns = [
{field:'goods_name','type':'good_select','label':'商品'},
{field:'price','type':'number_input','label':'价格'},
{field:'goods_bar_code','type':'display','label':'条码'}
]
const data = ref([
{'goods_name':'红牛','goods_bar_code':'69*****red','price':'2.00',minWidth:200},
{'goods_name':'','goods_bar_code':'','price':'0.00',minWidth:200},
{'goods_name':'','goods_bar_code':'','price':'0.00',minWidth:200},
{'goods_name':'','goods_bar_code':'','price':'0.00',minWidth:200},
{'goods_name':'','goods_bar_code':'','price':'0.00',minWidth:200},
{'goods_name':'','goods_bar_code':'','price':'0.00',minWidth:200},
])
const isEdit = ref(true)
onMounted(() => {
window.app = proxy
// 控制台调用:window.app.setupState.data 即可打印data
})
</script>