vue自定義表格列的實(shí)現(xiàn)過(guò)程記錄
前言
在我們開發(fā)PC端的項(xiàng)目使用表單時(shí),尤其是crm系統(tǒng),應(yīng)該經(jīng)常會(huì)遇到這樣的需求, 用戶需要根據(jù)設(shè)置來(lái)自定義顯示列。 查了element的官方文檔, 并沒有此類組件, 所以手動(dòng)封裝了一個(gè)簡(jiǎn)單的組件, 希望能在大家開發(fā)此類需求時(shí)能夠有所幫助。
效果圖
具體效果圖如下:
自定義顯示列 (可實(shí)現(xiàn)拖拽進(jìn)行排序,點(diǎn)擊選中,再次點(diǎn)擊取消選中)

按照用戶已設(shè)置好的字段排序/顯示/隱藏每一列

setTable組件
首先實(shí)現(xiàn)拖拽排序的話我們需要借助一個(gè)插件:
npm install vuedraggable -S
具體的組件代碼如下, 代碼內(nèi)已寫有詳細(xì)的注釋,在這里就不過(guò)多贅述了。。
setTable.vue
<template>
<div>
<el-dialog title="自定義顯示列" :visible.sync="dialogVisible" width="50%">
<div class="select-menus menus-box">
<p class="menus-title">拖動(dòng)區(qū)塊調(diào)整顯示順序</p>
<div class="menus-content">
<draggable v-model="selected" @update="datadragEnd" :options="{animation:500}">
<transition-group>
<div v-for="menu in selected" :key="menu.field" class="drag-item item">{{menu.name}}</div>
</transition-group>
</draggable>
</div>
</div>
<div class="menus-container menus-box" v-if="fields.length">
<p class="menus-title">選擇顯示列</p>
<div class="menus-content">
<div
class="item"
:class="{active:menu.active}"
v-for="menu of fields"
:key="menu.field"
@click="onSelect(menu)"
>{{menu.name}}</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="onSave">確 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import draggable from "vuedraggable";
import { getFields, setFields, getFieldControl } from "@/api/user";
export default {
name: "setTable",
inject: ["reload"],
props: {
types: String,
},
components: {
draggable,
},
data() {
return {
dialogVisible: false,
fields: [],//全部菜單
selected: [],//已選中菜單
};
},
watch: {
selected: {
handler(oldVal, newVal) {
if (this.fields.length === 0) {
return;
}
newVal.map((i) => {
this.fields.map((j) => {
if (i.field === j.field) {
// 如果已選中數(shù)組內(nèi)已有相同字段, 則active為true。active主要用來(lái)控制全部菜單選中/未選中的樣式
j.active = true;
}
});
});
},
},
},
mounted() {
//為了防止火狐瀏覽器拖拽的時(shí)候以新標(biāo)簽打開
document.body.ondrop = function (event) {
event.preventDefault();
event.stopPropagation();
};
},
methods: {
async getData() {
// 獲取全部菜單數(shù)據(jù)
const { data: fields } = await getFields({
types: this.types,
});
fields.map((item) => {
// 由于服務(wù)器并沒有返回active字段, 為此需要每一條數(shù)據(jù)增加active字段, 來(lái)控制選中的樣式
item.active = false;
});
this.fields = fields;
},
async getFields() {
// 獲取用戶已選中的菜單, 為了再次打開設(shè)置時(shí)能夠把上次選中菜單回顯到頁(yè)面,方便用戶再次修改
let fields = await getFieldControl({
account_id: this.$store.state.user.token.account_id,
userid: this.$store.state.user.token.userid,
types: this.types,
});
this.$nextTick(() => {
this.selected.push(...fields.data);
});
},
async onSave() {
// 保存已選中菜單
await setFields({
account_id: this.$store.state.user.token.account_id,
userid: this.$store.state.user.token.userid,
types: this.types,
content: this.selected,
});
this.reload(); //刷新頁(yè)面
},
async open() {
// 打開設(shè)置窗口時(shí)清空數(shù)據(jù),并再次請(qǐng)求獲取最新數(shù)據(jù)
this.fields = [];
this.selected = [];
this.dialogVisible = true;
await this.getData();
await this.getFields();
},
onSelect(item) {
// 判斷已選中菜單內(nèi)是否已有點(diǎn)擊選擇的菜單
let findex = this.selected.findIndex((i) => {
return item.field === i.field;
});
if (findex === -1) {
// 如果已選中菜單內(nèi)沒有, 則添加到最后一條
this.selected.push(item);
} else {
// 如果已選中已有,則點(diǎn)擊時(shí)應(yīng)該對(duì)其移除,并把a(bǔ)ctive設(shè)置為false, 改變其選中的樣式
item.active = false;
this.selected.splice(findex, 1);
}
},
datadragEnd(evt) {
// 拖動(dòng)排序
evt.preventDefault();
},
},
};
</script>
<style lang="scss" scoped>
/* 全部菜單 */
.menus-container {
margin-top: 20px;
.menus-content {
.item {
color: #575757;
background: rgba(238, 238, 238, 1);
border: 1px solid rgba(220, 220, 220, 1);
border-radius: 2px 0px 0px 2px;
}
}
}
/* 菜單通用樣式 */
.menus-box {
.menus-title {
margin-top: 10px;
line-height: 32px;
}
.menus-content {
display: flex;
flex-wrap: wrap;
.item {
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 8px;
margin: 10px;
border-radius: 3px;
}
.active {
color: #fff;
background: rgba(72, 153, 229, 1);
border-radius: 2px 0px 0px 2px;
}
}
}
/* 已選菜單 */
.select-menus {
.menus-content {
.item {
margin: 0px;
border-radius: 0;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(220, 220, 220, 1);
}
}
}
</style>
使用
具體使用如下, 在這里已經(jīng)隱去不必要的業(yè)務(wù)代碼, 只把最核心實(shí)現(xiàn)的代碼貼了出來(lái), 以免對(duì)大家產(chǎn)生誤導(dǎo)..
<template>
<div>
<el-table
ref="multipleTable"
:data="tableData"
height="60vh"
:row-class-name="tableRowClassName"
@selection-change="handleSelectionChange"
@row-click="handleRead"
>
<el-table-column type="selection" min-width="55px;"></el-table-column>
<template v-for="(item,index) of fields">
<el-table-column
v-if="item.field==='name'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='gender'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="8%;"
show-overflow-tooltip
>
<template slot-scope="scope">{{scope.row.gender===1?'男':'女'}}</template>
</el-table-column>
<el-table-column
v-if="item.field==='corp_full_name'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="14%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='corp_name'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="12%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='up_date'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="14%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='position'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='remark_mobiles'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="14%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='source_name'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='address'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='detail_address'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='description'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='remark'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='recordContent'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="14%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='owner_name'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="item.field==='follow_time'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="8%;"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.follow_time===scope.row.createtime">暫無(wú)</div>
<div v-else>{{scope.row.follow_time | formatDate}}</div>
</template>
</el-table-column>
<el-table-column
v-if="item.field==='next_follow_time'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="8%;"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.next_follow_time===0">暫無(wú)</div>
<div v-else>{{scope.row.next_follow_time | formatDate}}</div>
</template>
</el-table-column>
<el-table-column
v-if="item.field==='createtime'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="8%;"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>{{scope.row.createtime | formatDate}}</div>
</template>
</el-table-column>
<el-table-column
v-if="item.field==='updatetime'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="8%;"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>{{scope.row.updatetime | formatDate}}</div>
</template>
</el-table-column>
<el-table-column
v-if="item.field==='is_record'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>{{scope.row.is_record === 0 ? '未跟進(jìn)' : '已跟進(jìn)' }}</div>
</template>
</el-table-column>
<el-table-column
v-if="item.field==='if_record'"
:key="index"
:prop="item.field"
:label="item.name"
min-width="10%;"
show-overflow-tooltip
></el-table-column>
</template>
<el-table-column label="操作" min-width="8%;">
<template slot-scope="scope">
<el-button @click="handleRead(scope.row)" type="text">詳情</el-button>
</template>
</el-table-column>
<el-table-column align="right" min-width="4%;">
<template slot="header">
<i class="iconfont icongengduo" @click="onMore"></i>
</template>
</el-table-column>
</el-table>
<set-table ref="setting" types="leads"></set-table>
</div>
</template>
<script>
import setTable from "@/components/setTable";
import { getFieldControl } from "@/api/user";
export default {
name: "clues",
components: {
setTable,
},
data() {
return {
fields: [],
};
},
async mounted() {
await this.getFields();
this.clues();
},
methods: {
async getFields() {
let fields = await getFieldControl({
account_id: this.$store.state.user.token.account_id,
userid: this.$store.state.user.token.userid,
types: "leads",
});
this.fields = fields.data;
},
onMore() {
this.$refs.setting.open();
},
},
};
</script>
在這里其實(shí)也可以設(shè)置成固定的列寬或者通過(guò)服務(wù)器返回具體的尺寸, 這樣的話就不用寫這么多的if語(yǔ)句了, 會(huì)更加方便簡(jiǎn)潔..
結(jié)束語(yǔ)
其實(shí)剛接到這個(gè)需求時(shí),感覺挺復(fù)雜的, 尤其是需要進(jìn)行拖動(dòng), 還要根據(jù)服務(wù)器返回不同的字段來(lái)進(jìn)行表單列的排序。 但整體做下來(lái)并沒有我想象的那么麻煩。 大家在遇到需求時(shí), 也一定不要一直想的太多, 一定要先去嘗試, 說(shuō)不定它并沒有你想的那么難..
到此這篇關(guān)于vue自定義表格列的文章就介紹到這了,更多相關(guān)vue自定義表格列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue使用MD5對(duì)前后端進(jìn)行加密的實(shí)現(xiàn)
前后端分離的項(xiàng)目,遇到了對(duì)密碼進(jìn)行加密的情況,在前端或者是在后端加密都是可以的,本文主要介紹了Vue使用MD5對(duì)前后端進(jìn)行加密的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類小游戲
這篇文章主要介紹了如何用VUE和Canvas實(shí)現(xiàn)雷霆戰(zhàn)機(jī)打字類小游戲,麻雀雖小,五臟俱全,對(duì)游戲感興趣的同學(xué),可以參考下,研究里面的原理和實(shí)現(xiàn)方法2021-04-04
vue對(duì)象復(fù)制方式(深拷貝,多層對(duì)象拷貝方式在后面)
這篇文章主要介紹了vue對(duì)象復(fù)制方式(深拷貝,多層對(duì)象拷貝方式在后面),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
Vue中訪問(wèn)指定鏈接并解析頁(yè)面內(nèi)容的完整指南
在現(xiàn)代Web開發(fā)中,經(jīng)常需要從其他網(wǎng)頁(yè)獲取并解析內(nèi)容,本文將詳細(xì)介紹如何在Vue項(xiàng)目中實(shí)現(xiàn)這一功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
vue3中?provide?和?inject?用法小結(jié)
父子組件傳遞數(shù)據(jù)時(shí),使用的是props和emit,父?jìng)髯訒r(shí),使用的是?props,如果是父組件傳孫組件時(shí),就需要先傳給子組件,子組件再傳給孫組件,如果多個(gè)子組件或多個(gè)孫組件使用時(shí),就需要傳很多次,會(huì)很麻煩,這篇文章主要介紹了vue3中?provide?和?inject?用法,需要的朋友可以參考下2023-11-11
Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性
本篇文章主要介紹了Vue 2.0學(xué)習(xí)筆記之Vue中的computed屬性,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
Vue3滑動(dòng)到最右驗(yàn)證功能實(shí)現(xiàn)
在登錄頁(yè)面需要啟動(dòng)向右滑塊驗(yàn)證功能,遇到這樣的需求怎么實(shí)現(xiàn)呢,下面小編通過(guò)示例代碼給大家分享Vue3滑動(dòng)到最右驗(yàn)證功能實(shí)現(xiàn),感興趣的朋友一起看看吧2024-06-06

