erp element-plus的可编辑table表格组件基础封装

Vue
355
0
0
2022-11-10
标签   Vue实践

封装

思路参考:封装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>