Files
cl-crud/src/components/table.js
2022-06-07 09:04:28 +08:00

451 lines
8.9 KiB
JavaScript

import { renderNode } from "@/utils/vnode";
import { isNull } from "@/utils";
import { Emitter } from "@/mixins";
export default {
name: "cl-table",
componentName: "ClTable",
inject: ["crud"],
mixins: [Emitter],
props: {
columns: {
type: Array,
required: true,
default: () => []
},
on: {
type: Object,
default: () => {
return {};
}
},
props: {
type: Object,
default: () => {
return {};
}
}
},
data() {
return {
maxHeight: null,
data: [],
emit: {}
};
},
created() {
// Get default sort
const { order, prop } = this.props["default-sort"] || {};
// Set request params
this.crud.params.order = !order ? "" : order === "descending" ? "desc" : "asc";
this.crud.params.prop = prop;
// Crud event
this.$on("crud.resize", () => {
this.calcMaxHeight();
});
// Crud refresh
this.$on("crud.refresh", ({ list }) => {
this.data = list;
});
},
mounted() {
this.emptyRender();
this.calcMaxHeight();
this.bindEmit();
this.bindMethods()
},
methods: {
columnRender() {
return this.columns
.filter((e) => !e.hidden)
.map((item, index) => {
const deep = (item) => {
let params = {
props: item,
on: item.on
};
// If op
if (item.type === "op") {
return this.opRender(item);
}
// Default
if (!item.type || item.type === "expand") {
params.scopedSlots = {
default: (scope) => {
// Column-slot
let slot = this.$scopedSlots[`column-${item.prop}`];
let newScope = {
...scope,
...item
};
let value = scope.row[item.prop];
if (slot) {
// Use slot
return slot({
scope: newScope
});
} else {
// If component
if (item.component) {
return renderNode(item.component, {
prop: item.prop,
scope: newScope.row
});
}
// Formatter
else if (item.formatter) {
return item.formatter(
newScope.row,
newScope.column,
newScope.row[item.prop],
newScope.$index
);
}
// Dict tag
else if (item.dict) {
let data = item.dict.find((d) => d.value == value);
if (data) {
// Use el-tag
return (
<el-tag
{...{
props: {
size: "small",
"disable-transitions": true,
effect: "dark",
...data
}
}}>
{data.label}
</el-tag>
);
} else {
return value;
}
}
// Empty text
else if (isNull(value)) {
return scope.emptyText;
}
// Value
else {
return value;
}
}
},
header: (scope) => {
let slot = this.$scopedSlots[`header-${item.prop}`];
if (slot) {
return slot({
scope
});
} else {
return scope.column.label;
}
}
};
}
// Children element
const childrenEl = item.children ? item.children.map(deep) : null;
return (
<el-table-column key={`crud-table-column-${index}`} {...params}>
{childrenEl}
</el-table-column>
);
};
return deep(item);
});
},
opRender(item) {
const { rowEdit, rowDelete, getPermission } = this.crud;
if (!item) {
return null;
}
const render = (scope) => {
// Use op layout
return (item.layout || ["edit", "delete"]).map((vnode) => {
if (["edit", "update", "delete"].includes(vnode)) {
// Get permission
const perm = getPermission(vnode);
if (perm) {
let clickEvent = () => { };
let buttonText = null;
switch (vnode) {
case "edit":
case "update":
clickEvent = rowEdit;
buttonText = this.crud.dict.label.update;
break;
case "delete":
clickEvent = rowDelete;
buttonText = this.crud.dict.label.delete;
break;
}
return (
<el-button
size="mini"
type="text"
on-click={() => {
clickEvent(scope.row);
}}>
{buttonText}
</el-button>
);
}
} else {
// Use custom render
return renderNode(vnode, { scope, $scopedSlots: this.$scopedSlots });
}
});
};
return (
<el-table-column
{...{
props: {
label: "操作",
width: "160px",
...item
},
scopedSlots: {
default: (scope) => {
let el = null;
// Dropdown op
if (item.name == "dropdown-menu") {
const slot = this.$scopedSlots["table-op-dropdown-menu"];
const { width } = item["dropdown-menu"] || {};
const items = render(scope).map((e) => {
return <el-dropdown-item>{e}</el-dropdown-item>;
});
el = (
<el-dropdown
{...{
on,
props: {
trigger: "click",
...item.props
}
}}>
{slot ? (
slot({ scope })
) : (
<span class="el-dropdown-link">
<span>更多操作</span>
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
)}
<el-dropdown-menu
style={{ width }}
class="cl-crud__op-dropdown-menu"
{...{ slot: "dropdown" }}>
{items}
</el-dropdown-menu>
</el-dropdown>
);
} else {
el = render(scope);
}
return <div class="cl-table__op">{el}</div>;
}
}
}}
/>
);
},
emptyRender() {
const empty = this.$scopedSlots["table-empty"];
const scope = {
h: this.$createElement,
scope: this
};
if (empty) {
this.$scopedSlots.empty = () => {
return empty(scope)[0];
};
}
},
appendRender() {
return this.$slots["append"];
},
changeSort(prop, order) {
if (order === "desc") {
order = "descending";
}
if (order === "asc") {
order = "ascending";
}
this.$refs["table"].sort(prop, order);
},
sortChange({ prop, order }) {
if (order === "descending") {
order = "desc";
}
if (order === "ascending") {
order = "asc";
}
if (!order) {
prop = null;
}
if (this.crud.test.sortLock) {
this.crud.refresh({
prop,
order,
page: 1
});
}
},
selectionChange(selection) {
this.dispatch("cl-crud", "table.selection-change", { selection });
this.$emit("selection-change", selection);
},
bindEmit() {
const funcs = [
"select",
"select-all",
"cell-mouse-enter",
"cell-mouse-leave",
"cell-click",
"cell-dblclick",
"row-click",
"row-contextmenu",
"row-dblclick",
"header-click",
"header-contextmenu",
"filter-change",
"current-change",
"header-dragend",
"expand-change"
];
funcs.forEach((name) => {
this.emit[name] = (...args) => {
this.$emit.apply(this, [name, ...args]);
};
});
},
bindMethods() {
[
"clearSelection",
"toggleRowSelection",
"toggleAllSelection",
"toggleRowExpansion",
"setCurrentRow",
"clearSort",
"clearFilter",
"doLayout",
"sort"
].forEach(e => {
this[e] = this.$refs["table"][e];
});
},
calcMaxHeight() {
return this.$nextTick(() => {
const el = this.crud.$el.parentNode;
let { height = "" } = this.props || {};
if (el) {
let rows = el.querySelectorAll(".cl-crud .el-row");
if (!rows[0] || !rows[0].isConnected) {
return false;
}
let h = 20;
for (let i = 0; i < rows.length; i++) {
let f = true;
for (let j = 0; j < rows[i].childNodes.length; j++) {
if (rows[i].childNodes[j].className == "cl-table") {
f = false;
}
}
if (f) {
h += rows[i].clientHeight + 10;
}
}
let h1 = Number(String(height).replace("px", ""));
let h2 = el.clientHeight - h;
this.maxHeight = h1 > h2 ? h1 : h2;
}
});
}
},
render() {
return (
<div class="cl-table">
{
<el-table
ref="table"
data={this.data}
v-loading={this.crud.loading}
{...{
on: {
"selection-change": this.selectionChange,
"sort-change": this.sortChange,
...this.emit,
...this.on
},
props: {
"max-height": this.maxHeight + "px",
border: true,
size: "mini",
...this.props
},
scopedSlots: {
...this.$scopedSlots
},
slots: {
...this.$slots
}
}}>
{this.columnRender()}
</el-table>
}
</div>
);
}
};