|
@@ -1,341 +1,214 @@
|
|
|
<template>
|
|
|
- <div id="c-table">
|
|
|
- <el-table
|
|
|
- ref="table"
|
|
|
- :row-key="rowKey"
|
|
|
- :data="data"
|
|
|
- border
|
|
|
- stripe
|
|
|
- :max-height="height !== null ? height : ''"
|
|
|
- @select="handleSelectionChange"
|
|
|
- @select-all="handleSelectAll"
|
|
|
- :show-summary="useSum"
|
|
|
- @row-click="rowClick"
|
|
|
- :summary-method="computedSum"
|
|
|
- :header-cell-style="{ background: '#F5F7FA' }"
|
|
|
- >
|
|
|
- <el-table-column type="selection" width="55" v-if="select" :prop="rowKey" :reserve-selection="true"> </el-table-column>
|
|
|
- <template v-for="(item, index) in fields">
|
|
|
- <template v-if="item.custom">
|
|
|
- <el-table-column :key="index" align="center" :label="item.label" v-bind="item.options" :show-overflow-tooltip="item.showTip || true">
|
|
|
- <template v-slot="{ row }">
|
|
|
- <slot :name="item.model" v-bind="{ item, row }"></slot>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <el-table-column
|
|
|
- :key="index"
|
|
|
- align="center"
|
|
|
- :label="item.label"
|
|
|
- :prop="item.model"
|
|
|
- :formatter="toFormatter"
|
|
|
- :sortable="true"
|
|
|
- v-bind="item.options"
|
|
|
- :show-overflow-tooltip="item.showTip === false ? item.showTip : true"
|
|
|
- >
|
|
|
- </el-table-column>
|
|
|
- </template>
|
|
|
+ <el-table :data="list" border stripe @selection-change="toSelect">
|
|
|
+ <el-table-column type="selection" width="55" v-if="select"> </el-table-column>
|
|
|
+ <template v-for="(item, index) in fields">
|
|
|
+ <template v-if="item.custom">
|
|
|
+ <el-table-column :key="index" :label="item.label" v-bind="item.options" align="center" :show-overflow-tooltip="item.showTip || true">
|
|
|
+ <template v-slot="{ row }">
|
|
|
+ <slot :name="item.model" v-bind="{ item, row }"></slot>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
</template>
|
|
|
- <template v-if="opera.length > 0">
|
|
|
- <el-table-column label="操作" align="center" :width="operaWidth">
|
|
|
- <template v-slot="{ row, $index }">
|
|
|
- <template v-for="(item, index) in opera">
|
|
|
- <template v-if="display(item, row)">
|
|
|
- <template v-if="vOpera">
|
|
|
- <el-link
|
|
|
- v-opera="item.method"
|
|
|
- :key="`${item.model}-column-${index}`"
|
|
|
- :type="item.type || 'primary'"
|
|
|
- :icon="item.icon || ''"
|
|
|
- size="small"
|
|
|
- style="padding-right: 10px"
|
|
|
- :underline="false"
|
|
|
- @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
|
|
|
- >
|
|
|
- {{ item.label }}
|
|
|
- </el-link>
|
|
|
- </template>
|
|
|
- <template v-else>
|
|
|
- <el-link
|
|
|
- :key="`${item.model}-column-${index}`"
|
|
|
- :type="item.type || 'primary'"
|
|
|
- :icon="item.icon || ''"
|
|
|
- size="small"
|
|
|
- style="padding-right: 10px"
|
|
|
- :underline="false"
|
|
|
- @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
|
|
|
- >
|
|
|
- {{ item.label }}
|
|
|
- </el-link>
|
|
|
- </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-table-column
|
|
|
+ :key="index"
|
|
|
+ :label="item.label"
|
|
|
+ :prop="item.model"
|
|
|
+ :formatter="toFormatter"
|
|
|
+ v-bind="item.options"
|
|
|
+ :sortable="true"
|
|
|
+ align="center"
|
|
|
+ :show-overflow-tooltip="item.showTip === false ? item.showTip : true"
|
|
|
+ ></el-table-column>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ <template v-if="opera.length > 0">
|
|
|
+ <el-table-column label="操作" fixed="right" width="110" align="center">
|
|
|
+ <template v-slot="{ row, $index }">
|
|
|
+ <template v-for="(item, index) in opera">
|
|
|
+ <template v-if="display(item, row)">
|
|
|
+ <template v-if="vOpera">
|
|
|
+ <el-link
|
|
|
+ :key="`${item.model}-column-${index}`"
|
|
|
+ :type="item.type || 'primary'"
|
|
|
+ size="small"
|
|
|
+ :underline="false"
|
|
|
+ class="link"
|
|
|
+ v-opera="item.method"
|
|
|
+ @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
|
|
|
+ >
|
|
|
+ {{ item.label }}
|
|
|
+ </el-link>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <el-link
|
|
|
+ :key="`${item.model}-column-${index}`"
|
|
|
+ :type="item.type || 'primary'"
|
|
|
+ size="small"
|
|
|
+ :underline="false"
|
|
|
+ class="link"
|
|
|
+ @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
|
|
|
+ >
|
|
|
+ {{ item.label }}
|
|
|
+ </el-link>
|
|
|
</template>
|
|
|
</template>
|
|
|
</template>
|
|
|
- </el-table-column>
|
|
|
- </template>
|
|
|
- </el-table>
|
|
|
- <el-row type="flex" align="middle" justify="end" v-if="usePage">
|
|
|
- <el-col :span="24" class="page">
|
|
|
- <el-pagination
|
|
|
- background
|
|
|
- layout="sizes,total, prev, pager, next"
|
|
|
- :page-sizes="[10, 20, 50, 100, 200]"
|
|
|
- :total="total"
|
|
|
- :page-size="limit"
|
|
|
- v-model:current-page="currentPage"
|
|
|
- @current-change="changePage"
|
|
|
- @size-change="sizeChange"
|
|
|
- >
|
|
|
- </el-pagination>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </template>
|
|
|
+ </el-table>
|
|
|
+ <el-row justify="end" class="page" v-if="usePage">
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ layout="sizes,total, prev, pager, next"
|
|
|
+ :page-sizes="[10, 20, 50, 100, 200]"
|
|
|
+ :total="total"
|
|
|
+ :page-size="limit"
|
|
|
+ v-model:current-page="currentPage"
|
|
|
+ @current-change="changePage"
|
|
|
+ @size-change="sizeChange"
|
|
|
+ >
|
|
|
+ </el-pagination>
|
|
|
+ </el-row>
|
|
|
</template>
|
|
|
-<script setup lang="ts">
|
|
|
-import type { Ref } from 'vue';
|
|
|
-import { ref, toRefs, nextTick, getCurrentInstance } from 'vue';
|
|
|
-import { ElMessageBox } from 'element-plus';
|
|
|
-
|
|
|
-import _ from 'lodash';
|
|
|
-const { proxy } = getCurrentInstance() as any;
|
|
|
|
|
|
-// #region 传递
|
|
|
+<script setup lang="ts">
|
|
|
+// 基础
|
|
|
+import _ from 'lodash'
|
|
|
+import type { Ref } from 'vue'
|
|
|
+import { onMounted, toRefs, getCurrentInstance, ref } from 'vue'
|
|
|
+import { ElMessageBox } from 'element-plus'
|
|
|
+const { proxy } = getCurrentInstance() as any
|
|
|
+const emit = defineEmits(['toSelect', 'query'])
|
|
|
|
|
|
+// 参数
|
|
|
interface fieldsItem {
|
|
|
- custom: string;
|
|
|
- label: string;
|
|
|
- options: string;
|
|
|
- showTip: boolean;
|
|
|
- model: string;
|
|
|
+ // 是否自定义
|
|
|
+ custom: string
|
|
|
+ // 名称
|
|
|
+ label: string
|
|
|
+ // 绑定键值
|
|
|
+ model: string
|
|
|
+ // 其他
|
|
|
+ options: string
|
|
|
+ // 是否超出隐藏
|
|
|
+ showTip: boolean
|
|
|
}
|
|
|
interface operaItem {
|
|
|
- method: string;
|
|
|
- model: string;
|
|
|
- type: string;
|
|
|
- icon: string;
|
|
|
- confirmWord: string;
|
|
|
- label: string;
|
|
|
- confirm: boolean;
|
|
|
- methodZh: string;
|
|
|
+ // 名称
|
|
|
+ label: string
|
|
|
+ model: string
|
|
|
+ // 绑定键值
|
|
|
+ method: string
|
|
|
+ // 颜色
|
|
|
+ type: string
|
|
|
+ // 是否开启弹框
|
|
|
+ confirm: boolean
|
|
|
+ // 是否自定义文字说明
|
|
|
+ confirmWord: string
|
|
|
+ // 简短文字
|
|
|
+ methodZh: string
|
|
|
}
|
|
|
interface dataItem {
|
|
|
- _id?: string;
|
|
|
+ _id?: string
|
|
|
}
|
|
|
|
|
|
const props = defineProps({
|
|
|
+ // 列表配置
|
|
|
fields: { type: Array<fieldsItem>, required: true },
|
|
|
- data: { type: Array<dataItem>, required: true },
|
|
|
- opera: { type: Array<operaItem>, default: () => [] },
|
|
|
- rowKey: { type: String, default: '_id' },
|
|
|
- select: { type: Boolean, default: false },
|
|
|
- selected: { type: Array, default: () => [] },
|
|
|
+ // 操作配置
|
|
|
+ opera: { type: Array<operaItem>, required: true },
|
|
|
+ // 列表数据
|
|
|
+ list: { type: Array<dataItem>, required: true },
|
|
|
+ // 是否开启多选
|
|
|
+ select: { type: Boolean, default: true },
|
|
|
+ // selected: { type: Array, default: () => [] },
|
|
|
+ vOpera: { type: Boolean, default: false },
|
|
|
+ // 分页
|
|
|
usePage: { type: Boolean, default: true },
|
|
|
- total: { type: Number, default: 0 },
|
|
|
- useSum: { type: Boolean, default: false },
|
|
|
- sumcol: { type: Array, default: () => [] },
|
|
|
- sumres: { type: String, default: 'total' },
|
|
|
- // limit: { type: Number, default: 10 },
|
|
|
- height: null,
|
|
|
- operaWidth: { type: Number, default: 200 },
|
|
|
- vOpera: { type: Boolean, default: false }
|
|
|
-});
|
|
|
-const { fields } = toRefs(props);
|
|
|
-const { data } = toRefs(props);
|
|
|
-const { opera } = toRefs(props);
|
|
|
-const { rowKey } = toRefs(props);
|
|
|
-const { select } = toRefs(props);
|
|
|
-const { selected } = toRefs(props);
|
|
|
-const { usePage } = toRefs(props);
|
|
|
-const { total } = toRefs(props);
|
|
|
-const { useSum } = toRefs(props);
|
|
|
-const { sumcol } = toRefs(props);
|
|
|
-const { sumres } = toRefs(props);
|
|
|
-// const { limit } = toRefs(props);
|
|
|
-const { height } = toRefs(props);
|
|
|
-const { operaWidth } = toRefs(props);
|
|
|
-const { vOpera } = toRefs(props);
|
|
|
-// #endregion
|
|
|
-const emit = defineEmits(['method', 'handleSelect', 'query', 'rowClick']);
|
|
|
-
|
|
|
-let pageSelected: Ref<any[]> = ref([]);
|
|
|
-let currentPage: Ref<number> = ref(1);
|
|
|
+ total: { type: Number, default: 0 }
|
|
|
+})
|
|
|
+const { fields } = toRefs(props)
|
|
|
+const { opera } = toRefs(props)
|
|
|
+const { list } = toRefs(props)
|
|
|
+const { select } = toRefs(props)
|
|
|
+// const { selected } = toRefs(props);
|
|
|
+const { vOpera } = toRefs(props)
|
|
|
+const { usePage } = toRefs(props)
|
|
|
+const { total } = toRefs(props)
|
|
|
|
|
|
-let limit: number = proxy.$limit;
|
|
|
-const toFormatter = (row: any, column: { property: string }, cellValue: string, index: string) => {
|
|
|
- let this_fields = fields.value.filter((fil) => fil.model === column.property);
|
|
|
+let limit: number = proxy.$limit
|
|
|
+let currentPage: Ref<number> = ref(1)
|
|
|
+// 请求
|
|
|
+onMounted(async () => {})
|
|
|
+// 多选
|
|
|
+const toSelect = (val: Array<[]>) => {
|
|
|
+ emit(`toSelect`, val)
|
|
|
+}
|
|
|
+// 格式化内容
|
|
|
+const toFormatter = (row: any, column: { property: string }, cellValue: string) => {
|
|
|
+ let this_fields = fields.value.filter((fil) => fil.model === column.property)
|
|
|
if (this_fields.length > 0) {
|
|
|
- let format: any = _.get(this_fields[0], `format`, false);
|
|
|
+ let format: any = _.get(this_fields[0], `format`, false)
|
|
|
if (format) {
|
|
|
- let res;
|
|
|
+ let res
|
|
|
if (_.isFunction(format)) {
|
|
|
- res = format(cellValue);
|
|
|
+ res = format(cellValue)
|
|
|
} else {
|
|
|
- res = toFormat({
|
|
|
- model: this_fields[0].model,
|
|
|
- value: cellValue
|
|
|
- });
|
|
|
+ res = toFormat({ model: this_fields[0].model, value: cellValue })
|
|
|
}
|
|
|
- return res;
|
|
|
+ return res
|
|
|
} else {
|
|
|
- return cellValue;
|
|
|
+ return cellValue
|
|
|
}
|
|
|
}
|
|
|
-};
|
|
|
-const toFormat = (e: { model: string; value: string }) => {};
|
|
|
+}
|
|
|
+const toFormat = (e: { model: string; value: string }) => {
|
|
|
+ console.log(e)
|
|
|
+}
|
|
|
+// 过滤
|
|
|
+const display = (item: operaItem, row: any) => {
|
|
|
+ let display: any = _.get(item, `display`, true)
|
|
|
+ if (display === true) return true
|
|
|
+ else {
|
|
|
+ let res = display(row)
|
|
|
+ return res
|
|
|
+ }
|
|
|
+}
|
|
|
+// 操作
|
|
|
const handleOpera = (data: string, method: any, confirm = false, methodZh: string, label: string, index: string, confirmWord: string) => {
|
|
|
- let self = true;
|
|
|
- if (_.isFunction(methodZh)) methodZh = methodZh(data);
|
|
|
+ let self = true
|
|
|
+ if (_.isFunction(methodZh)) methodZh = methodZh(data)
|
|
|
else if (!_.isString(methodZh)) {
|
|
|
- methodZh = label;
|
|
|
- self = false;
|
|
|
+ methodZh = label
|
|
|
+ self = false
|
|
|
}
|
|
|
if (confirm) {
|
|
|
- let word = self ? methodZh : `您确认${methodZh}该数据?`;
|
|
|
- if (confirmWord) word = confirmWord;
|
|
|
+ let word = self ? methodZh : `您确认${methodZh}该数据?`
|
|
|
+ if (confirmWord) word = confirmWord
|
|
|
ElMessageBox.confirm(word, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
|
|
|
.then(() => {
|
|
|
- emit(method, data, index);
|
|
|
+ emit(method, data, index)
|
|
|
})
|
|
|
- .catch(() => {});
|
|
|
- } else emit(method, data, index);
|
|
|
-};
|
|
|
-const handleSelectionChange = (selection: string, row: never) => {
|
|
|
- //根据row是否再pageSelected中,判断是添加还是删除
|
|
|
- let isSelecteds = _.cloneDeep(pageSelected);
|
|
|
- const is_has = isSelecteds.value.findIndex((f) => f[rowKey.value] === row[rowKey.value]);
|
|
|
- if (is_has <= -1) {
|
|
|
- // 没有找到,属于添加
|
|
|
- isSelecteds.value.push(row);
|
|
|
- } else {
|
|
|
- // 找到了,删除
|
|
|
- isSelecteds.value.splice(is_has, 1);
|
|
|
- }
|
|
|
- pageSelected.value = isSelecteds.value;
|
|
|
- emit(`handleSelect`, isSelecteds);
|
|
|
-};
|
|
|
-const handleSelectAll = (selection: any) => {
|
|
|
- //处于没全选状态,选择之后一定是全选,只有处于全选状态时,才会反选(全取消)
|
|
|
- let res: any = [];
|
|
|
- if (selection.length > 0) {
|
|
|
- //全选
|
|
|
- res = _.uniqBy(pageSelected.value.concat(selection), rowKey.value);
|
|
|
- } else {
|
|
|
- //全取消
|
|
|
- res = _.differenceBy(pageSelected.value, data.value, rowKey.value);
|
|
|
- }
|
|
|
- pageSelected.value = res;
|
|
|
- emit(`handleSelect`, res);
|
|
|
-};
|
|
|
-
|
|
|
-const table = ref<any>();
|
|
|
-
|
|
|
-const initSelection = (select: any, data: any) => {
|
|
|
- nextTick(() => {
|
|
|
- table.value.clearSelection();
|
|
|
- select.forEach((info: any) => {
|
|
|
- let d = data.find((p: any) => p._id == info._id);
|
|
|
- console.log(d);
|
|
|
- if (d) table.value.toggleRowSelection(d);
|
|
|
- });
|
|
|
- });
|
|
|
-};
|
|
|
-
|
|
|
-const selectReset = () => {
|
|
|
- table.value.clearSelection();
|
|
|
-};
|
|
|
-defineExpose({ initSelection });
|
|
|
-
|
|
|
+ .catch(() => {})
|
|
|
+ } else emit(method, data, index)
|
|
|
+}
|
|
|
+// 分页
|
|
|
const changePage = (page: number = currentPage.value) => {
|
|
|
- emit('query', { skip: (page - 1) * limit, limit: limit });
|
|
|
-};
|
|
|
+ emit('query', { skip: (page - 1) * limit, limit: limit })
|
|
|
+}
|
|
|
const sizeChange = (limits: number) => {
|
|
|
- limit = limits;
|
|
|
- currentPage.value = 1;
|
|
|
- emit('query', { skip: 0, limit: limit });
|
|
|
-};
|
|
|
-const rowClick = (row: any, column: string, event: string) => {
|
|
|
- emit(`rowClick`, row);
|
|
|
-};
|
|
|
-const display = (item: operaItem, row: any) => {
|
|
|
- let display: any = _.get(item, `display`, true);
|
|
|
- if (display === true) return true;
|
|
|
- else {
|
|
|
- let res = display(row);
|
|
|
- return res;
|
|
|
- }
|
|
|
-};
|
|
|
-// 计算合计
|
|
|
-const computedSum = (e: { columns: any; data: any }) => {
|
|
|
- const { columns, data } = e;
|
|
|
-
|
|
|
- if (columns.length <= 0 || data.length <= 0) return '';
|
|
|
- const result = [];
|
|
|
- const reg = new RegExp(/^\d+$/);
|
|
|
- for (const column of columns) {
|
|
|
- // 判断有没有prop属性
|
|
|
- const prop = _.get(column, 'property');
|
|
|
- if (!prop) {
|
|
|
- result.push('');
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 判断是否需要计算
|
|
|
- const inlist = sumcol.value.find((f) => f == prop);
|
|
|
- if (!inlist) {
|
|
|
- result.push('');
|
|
|
- continue;
|
|
|
- }
|
|
|
- let res: number | unknown = 0;
|
|
|
- // 整理出要计算的属性(只取出数字或者可以为数字的值)
|
|
|
- const resetList = data.map((i: any) => {
|
|
|
- const d = _.get(i, prop);
|
|
|
- return d * 1;
|
|
|
- });
|
|
|
- let p1: any;
|
|
|
- if (sumres.value === 'total') {
|
|
|
- res = totalComputed(p1, resetList);
|
|
|
- } else if (sumres.value === 'avg') {
|
|
|
- res = avgComputed(resetList);
|
|
|
- } else if (sumres.value === 'max') {
|
|
|
- res = maxComputed(resetList);
|
|
|
- } else if (sumres.value === 'min') {
|
|
|
- res = minComputed(resetList);
|
|
|
- }
|
|
|
- result.push(res);
|
|
|
- }
|
|
|
- result[0] = '合计';
|
|
|
- return result;
|
|
|
-};
|
|
|
-// 合计计算
|
|
|
-const totalComputed = (columns: any, data: any) => {
|
|
|
- const total = data.reduce((p: number, n: string) => p + parseFloat(n), 0);
|
|
|
- return total;
|
|
|
-};
|
|
|
-// 平均值计算
|
|
|
-const avgComputed = (data: any) => {
|
|
|
- let p1: any;
|
|
|
- const total = totalComputed(p1, data);
|
|
|
- return _.round(_.divide(total, data.length), 2);
|
|
|
-};
|
|
|
-// 最大值计算
|
|
|
-const maxComputed = (data: any) => {
|
|
|
- return _.max(data);
|
|
|
-};
|
|
|
-// 最小值计算
|
|
|
-const minComputed = (data: any) => {
|
|
|
- return _.min(data);
|
|
|
-};
|
|
|
+ limit = limits
|
|
|
+ currentPage.value = 1
|
|
|
+ emit('query', { skip: 0, limit: limit })
|
|
|
+}
|
|
|
</script>
|
|
|
-
|
|
|
-<style scoped>
|
|
|
-.page {
|
|
|
- background-color: #fff;
|
|
|
- padding: 8px;
|
|
|
- height: 50px;
|
|
|
+<style scoped lang="scss">
|
|
|
+.link {
|
|
|
+ padding: 0 5px 0 0;
|
|
|
}
|
|
|
-.el-pagination {
|
|
|
- position: absolute;
|
|
|
- right: 10px;
|
|
|
- background-color: #fff;
|
|
|
+.page {
|
|
|
+ margin: 10px 0 0 0;
|
|
|
}
|
|
|
</style>
|