123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- <template>
- <div id="c-table">
- <el-table
- ref="table"
- :row-key="rowKey"
- :data="data"
- size="small"
- border
- stripe
- :max-height="height !== null ? height : ''"
- @select="handleSelectionChange"
- @select-all="handleSelectAll"
- :show-summary="useSum"
- @row-click="rowClick"
- :summary-method="computedSum"
- >
- <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>
- </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>
- </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>
- <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 传递
- interface fieldsItem {
- custom: string;
- label: string;
- options: string;
- showTip: boolean;
- model: string;
- }
- interface operaItem {
- method: string;
- model: string;
- type: string;
- icon: string;
- confirmWord: string;
- label: string;
- confirm: boolean;
- methodZh: string;
- }
- interface dataItem {
- _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: () => [] },
- 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);
- 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);
- if (this_fields.length > 0) {
- let format: any = _.get(this_fields[0], `format`, false);
- if (format) {
- let res;
- if (_.isFunction(format)) {
- res = format(cellValue);
- } else {
- res = toFormat({
- model: this_fields[0].model,
- value: cellValue,
- });
- }
- return res;
- } else {
- return cellValue;
- }
- }
- };
- const toFormat = (e: { model: string; value: string }) => {};
- 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);
- else if (!_.isString(methodZh)) {
- methodZh = label;
- self = false;
- }
- if (confirm) {
- let word = self ? methodZh : `您确认${methodZh}该数据?`;
- if (confirmWord) word = confirmWord;
- ElMessageBox.confirm(word, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
- .then(() => {
- 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 = () => {
- nextTick(() => {
- table.value.clearSelection();
- selected.value.forEach((info: any) => {
- let d = data.value.filter((p) => p._id === info._id);
- if (d.length > 0) table.value.toggleRowSelection(d[0]);
- });
- });
- };
- const selectReset = () => {
- table.value.clearSelection();
- };
- const changePage = (page: number = currentPage.value) => {
- 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 = (columns: any, data: any) => {
- 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);
- };
- </script>
- <style scoped>
- .page {
- background-color: #fff;
- padding: 8px;
- height: 50px;
- }
- .el-pagination {
- position: absolute;
- right: 10px;
- background-color: #fff;
- }
- </style>
|