业务背景 - 项目里 17+ 列的列表页 (manager/Projects, Meetings, AdminUsers 等) 在手机端只能 横向滚动, 操作体验差. - refer/gr-table.vue 是 Vue2 + Vant 组件, Vue3 不能直接用 (componentOptions 私有 API 已被 Vue3 移除). - 新建 Vue3 + EP 兼容版本 GrTable, 吸收"主列显示 / 次要列折叠到展开行"思想. 组件设计 - props: data, mainCols (默认 = 第一个 prop + 最后一个 prop), mobileBreakpoint - 通过 slots.default() 取 vnode, 读 vnode.props.prop/label (Vue3 思路, 替代 Vue2 componentOptions 私有 API) - v-bind="\$attrs" 全透传 el-table 原生 props/events - el-table 引用方法 (clearSelection / toggleRowSelection 等) defineExpose 暴露 manager/Projects 试点 - 引入 GrTable, :main-cols="['projectNo', 'projectName']" - 手机端: 只显示项目编号 + 项目名称 + 操作列, 其他 13 列折叠到展开行 - 桌面端: 与原 el-table 完全一致 (所有 17 列) - 操作列 (label=操作, 无 prop) 自动保留 (因为 shouldRenderColumn 跳过无 prop 列)
161 lines
6.1 KiB
Vue
161 lines
6.1 KiB
Vue
<template>
|
|
<div class="gr-table-container">
|
|
<div class="gr-table-filter">
|
|
<div class="gr-query-wrap">
|
|
<div>筛选</div>
|
|
<van-field class="gr-query" v-model="queryVal"></van-field>
|
|
<van-icon name="search" size="18"></van-icon>
|
|
</div>
|
|
</div>
|
|
<el-table class="gr-table" ref="mesSysTable" :data="data2Show" :border="border" :height="height"
|
|
:max-height="maxHeight" :stripe="stripe" :size="size" :fit="fit" :show-header="showHeader"
|
|
:highlight-current-row="highlightCurrentRow" :highlight-selection-row="highlightSelectionRow"
|
|
:current-row-key="currentRowKey" :row-class-name="rowClassName" :row-style="rowStyle" :cell-class-name="cellClassName"
|
|
:cell-style="cellStyle" :header-row-class-name="headerRowClassName" :header-row-style="headerRowStyle"
|
|
:header-cell-class-name="headerCellClassName" :header-cell-style="headerCellStyle" :row-key="rowKey"
|
|
:empty-text="emptyText" :default-expand-all="defaultExpandAll" :expand-row-keys="expandRowKeys"
|
|
:default-sort="defaultSort" :tooltip-effect="tooltipEffect" :show-summary="showSummary" :summary-method="summaryMethod"
|
|
:sum-text="sumText" :span-method="spanMethod" :select-on-indeterminate="selectOnIndeterminate" :indent="indent"
|
|
:lazy="lazy" :load="load" :tree-props="treeProps"
|
|
@select="(evt) => $emit('select',evt)" @select-all="(evt) => $emit('select-all',evt)"
|
|
@selection-change="(evt) => $emit('selection-change',evt)" @cell-mouse-enter="(evt) => $emit('cell-mouse-enter',evt)"
|
|
@cell-mouse-leave="(evt) => $emit('cell-mouse-leave',evt)" @cell-click="(evt) => $emit('cell-click',evt)"
|
|
@cell-dblclick="(evt) => $emit('cell-dblclick',evt)" @row-click="(evt) => $emit('row-click',evt)"
|
|
@row-contextmenu="(evt) => $emit('row-contextmenu',evt)" @row-dblclick="(evt) => $emit('row-dblclick',evt)"
|
|
@header-click="(evt) => $emit('header-click',evt)" @header-contextmenu="(evt) => $emit('header-contextmenu',evt)"
|
|
@sort-change="(evt) => $emit('sort-change',evt)" @filter-change="(evt) => $emit('filter-change',evt)"
|
|
@current-change="(evt) => $emit('current-change',evt)" @header-dragend="(evt) => $emit('header-dragend',evt)"
|
|
@expand-change="(evt) => $emit('expand-change',evt)" >
|
|
<el-table-column width="30" type="expand" v-if="meta">
|
|
<template slot-scope="scope">
|
|
<el-descriptions :column="1" style="margin: 0 20px;">
|
|
<el-descriptions-item v-for="(item, index) in meta" :label="item.label" :key="index">{{scope.row[item.prop]}}</el-descriptions-item>
|
|
</el-descriptions>
|
|
</template>
|
|
</el-table-column>
|
|
<slot>
|
|
</slot>
|
|
</el-table>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import Vue from 'vue';
|
|
import { Field } from 'vant';
|
|
Vue.use(Field);
|
|
import {Icon} from "vant";
|
|
Vue.use(Icon);
|
|
export default {
|
|
props: ["data","meta" ,"major","border","height","maxHeight","stripe","size","fit","showHeader",
|
|
"highlightCurrentRow","highlightSelectionRow","currentRowKey","rowClassName","rowStyle","cellClassName",
|
|
"cellStyle","headerRowClassName", "headerRowStyle","headerCellClassName","headerCellStyle",
|
|
"rowKey", "emptyText","defaultExpandAll", "expandRowKeys","defaultSort","tooltipEffect","sumText","showSummary",
|
|
"summaryMethod", "spanMethod","selectOnIndeterminate","indent","lazy","load","treeProps",
|
|
],
|
|
data() {
|
|
return {
|
|
data2Show: [],
|
|
slight: null,
|
|
mainCol: this.major,
|
|
rows: null,
|
|
queryVal: null,
|
|
};
|
|
},
|
|
watch: {
|
|
data() {
|
|
this.refreshData();
|
|
},
|
|
queryVal(val) {
|
|
// console.log("queryval ", val);
|
|
this.refreshData();
|
|
}
|
|
},
|
|
methods: {
|
|
getDataList(row) {
|
|
return Object.keys(row)
|
|
.filter((key) => this.slight.some((s) => s.prop == key)).map((key) => {
|
|
return {
|
|
label: this.slight.find((s) => s.prop == key).label,
|
|
value: row[this.slight.find((s) => s.prop == key).prop],
|
|
};
|
|
});
|
|
},
|
|
clearSelection(...args) { this.$refs['mesSysTable'].clearSelection(...args)},
|
|
toggleRowSelection (...args) {this.$refs['mesSysTable'].toggleRowSelection(...args)},
|
|
toggleAllSelection (...args) { this.$refs['mesSysTable'].toggleAllSelection(...args)},
|
|
toggleRowExpansion (...args) { this.$refs['mesSysTable'].toggleRowExpansion(...args)},
|
|
setCurrentRow (...args) { this.$refs['mesSysTable'].setCurrentRow(...args)},
|
|
clearSort (...args) { this.$refs['mesSysTable'].clearSort(...args)},
|
|
clearFilter (...args){ this.$refs['mesSysTable'].clearFilter(...args)},
|
|
doLayout (...args){ this.$refs['mesSysTable'].doLayout(...args)},
|
|
sort (...args){ this.$refs['mesSysTable'].sort(...args)},
|
|
refreshData() {
|
|
if (this.queryVal) {
|
|
// this.$showLoading();
|
|
// console.log("queryval is",this.queryVal);
|
|
this.data2Show = this.data.filter(item => {
|
|
return JSON.stringify(item).indexOf(this.queryVal) != -1;
|
|
})
|
|
// this.$hideLoading();
|
|
} else {
|
|
this.data2Show = this.data;
|
|
}
|
|
}
|
|
},
|
|
|
|
// created() {
|
|
// this.data2Show = JSON.parse(JSON.stringify(this.data));
|
|
// },
|
|
|
|
};
|
|
</script>
|
|
<style scoped lang="scss">
|
|
@import "@/assets/styles/vant.scss";
|
|
|
|
::v-deep .van-cell {
|
|
padding: 0px !important
|
|
}
|
|
::v-deep .el-table .cell {
|
|
padding-left: 0 !important;
|
|
padding-right: 0 !important;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
}
|
|
.gr-table-container {
|
|
padding: 0 8px;
|
|
.gr-table-filter {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
border-radius: 16px;
|
|
height: 32px;
|
|
border: 1px solid $gr-border;
|
|
margin-bottom: 10px;
|
|
.gr-query-wrap {
|
|
width: calc(100% - 32px);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
}
|
|
.gr-query {
|
|
width: calc(100% - 60px);
|
|
}
|
|
}
|
|
}
|
|
::v-deep th.el-table__cell {
|
|
background: #0ECD4916 !important;
|
|
font-family: PingFangSC-Medium;
|
|
color: #323233;
|
|
line-height: 22px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
::v-deep .el-table__cell {
|
|
padding: 8px !important;
|
|
}
|
|
::v-deep .el-descriptions__body {
|
|
font-size: 12px !important;
|
|
line-height: 18px;
|
|
}
|
|
</style> |