|
|
@@ -1,23 +1,26 @@
|
|
|
<template>
|
|
|
<div class="sub-table-input">
|
|
|
- <bs-table v-bind="tableOptions" ref="bsTableRef">
|
|
|
+ <bs-table v-bind="options" ref="bsTableRef">
|
|
|
<template #bottom>
|
|
|
<div class="table-bottom-btns">
|
|
|
<div class="custom-table-btn">
|
|
|
- <div class="ct-btn">
|
|
|
- <slot v-if="slots.tableLeftBtn" name="tableLeftBtn"></slot>
|
|
|
+ <div class="ct-btn" v-if="isShowAddBtn">
|
|
|
<a-button type="text" style="width: 100%" @click="handleAddBtn">
|
|
|
<template #icon> <PlusOutlined /></template>
|
|
|
<span>新增</span>
|
|
|
</a-button>
|
|
|
</div>
|
|
|
|
|
|
- <div class="ct-btn">
|
|
|
+ <div class="ct-btn" v-if="isShowBatchDeleteBtn">
|
|
|
<a-button type="text" @click="handleBatchDelete" style="width: 100%" :disabled="selectedData.length === 0">
|
|
|
<template #icon> <DeleteOutlined /></template>
|
|
|
<span>批量删除</span>
|
|
|
</a-button>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div class="ct-btn" v-for="(btnSlot, index) in optBtnSlots" :key="index">
|
|
|
+ <slot :name="btnSlot"></slot>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -26,25 +29,44 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="jsx">
|
|
|
- import { useSlots, h, ref, toRefs, onMounted, nextTick, watch } from 'vue';
|
|
|
+ import { useSlots, h, ref, toRefs, onMounted, nextTick, watch, computed } from 'vue';
|
|
|
import BsTable, { useBsTable } from '/@/components/BsUi/Table';
|
|
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
|
|
- import { cloneDeep, isEmpty } from 'lodash';
|
|
|
- import { DISPLAY_STATE } from '/@/components/BsUi/constant.js';
|
|
|
+ import { cloneDeep, has, isEmpty } from 'lodash';
|
|
|
+ import { DISPLAY_STATE, ROW_KEY_FIELD } from '/@/components/BsUi/constant.js';
|
|
|
+ import { getUUID } from 'ant-design-vue/es/vc-dialog/util.js';
|
|
|
|
|
|
const props = defineProps({
|
|
|
- customColumns: {
|
|
|
- required: false,
|
|
|
- default: [],
|
|
|
- },
|
|
|
value: {
|
|
|
required: true,
|
|
|
default: undefined,
|
|
|
},
|
|
|
- rowKey: {
|
|
|
- required: false,
|
|
|
- default: 'id',
|
|
|
+ tableOptions: {
|
|
|
+ type: Object,
|
|
|
+ default: {
|
|
|
+ gridOptions: {},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ isShowAddBtn: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ isShowBatchDeleteBtn: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ optBtnSlots: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [],
|
|
|
},
|
|
|
+ isEditRow: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const rowKey = computed(() => {
|
|
|
+ return props.tableOptions.rowKey;
|
|
|
});
|
|
|
|
|
|
const emits = defineEmits('change', 'update:value', 'add');
|
|
|
@@ -53,37 +75,6 @@
|
|
|
const selectedData = ref([]);
|
|
|
const bsTableRef = ref(null);
|
|
|
|
|
|
- const bsTableAdapter = {
|
|
|
- toolbarConfig: {
|
|
|
- enable: false,
|
|
|
- },
|
|
|
- searchConfig: {
|
|
|
- enable: false,
|
|
|
- },
|
|
|
- gridOptions: {
|
|
|
- border: true,
|
|
|
- minHeight: '0',
|
|
|
- data: [],
|
|
|
- columns: [],
|
|
|
- onCheckboxChange(props) {
|
|
|
- const gridRef = getGridRef();
|
|
|
- selectedData.value = gridRef.getCheckboxRecords(true);
|
|
|
- },
|
|
|
- onCheckboxAll(props) {
|
|
|
- const gridRef = getGridRef();
|
|
|
- selectedData.value = gridRef.getCheckboxRecords(true);
|
|
|
- },
|
|
|
- },
|
|
|
- pagerConfig: {
|
|
|
- enable: false,
|
|
|
- isFixed: false,
|
|
|
- },
|
|
|
- };
|
|
|
-
|
|
|
- const bsTableBean = useBsTable({ tableOptions: bsTableAdapter });
|
|
|
-
|
|
|
- const { tableOptions, setTablePropsValue: setValue, getTablePropsValue: getValue, getGridRef } = bsTableBean;
|
|
|
-
|
|
|
const selectAndSeqCols = [
|
|
|
{ type: 'checkbox', width: 60, align: 'center', fixed: 'left' },
|
|
|
{ type: 'seq', width: 50, align: 'center', fixed: 'left' },
|
|
|
@@ -106,12 +97,16 @@
|
|
|
icon={h(DeleteOutlined)}
|
|
|
type='text'
|
|
|
onClick={() => {
|
|
|
- const tableData = getValue('gridOptions.data');
|
|
|
+ const tableData = props.value;
|
|
|
const deepCloneData = cloneDeep(tableData);
|
|
|
deepCloneData.splice(rowIndex, 1);
|
|
|
- // setValue('gridOptions.data', deepCloneData);
|
|
|
emits('update:value', deepCloneData);
|
|
|
emits('change', deepCloneData);
|
|
|
+ emits('delete-row', {
|
|
|
+ gridRef: getGridRef(),
|
|
|
+ bsTable: bsTableBean,
|
|
|
+ tableData: deepCloneData,
|
|
|
+ });
|
|
|
}}
|
|
|
></a-button>
|
|
|
</a-tooltip>
|
|
|
@@ -128,35 +123,152 @@
|
|
|
},
|
|
|
},
|
|
|
];
|
|
|
- const gridOptions = getValue('gridOptions');
|
|
|
- gridOptions.columns = [...selectAndSeqCols, ...optionsCols];
|
|
|
- if (!isEmpty(props.customColumns)) {
|
|
|
- gridOptions.columns = [...selectAndSeqCols, ...props.customColumns, ...optionsCols];
|
|
|
- }
|
|
|
+
|
|
|
+ const bsTableAdapter = computed(() => {
|
|
|
+ const customOpt = {
|
|
|
+ toolbarConfig: {
|
|
|
+ enable: false,
|
|
|
+ },
|
|
|
+ searchConfig: {
|
|
|
+ enable: false,
|
|
|
+ },
|
|
|
+ pagerConfig: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ pageSizeOptions: ['100'],
|
|
|
+ },
|
|
|
+ async request({ pageNum, pageSize }) {
|
|
|
+ const sourceData = isEmpty(props.value) ? [] : cloneDeep(props.value);
|
|
|
+ const startIndex = (pageNum - 1) * pageSize;
|
|
|
+ const endIndex = startIndex + pageSize;
|
|
|
+ return {
|
|
|
+ list: sourceData.slice(startIndex, endIndex),
|
|
|
+ total: sourceData.length,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ gridOptions: {
|
|
|
+ loading: false,
|
|
|
+ border: true,
|
|
|
+ minHeight: '100px',
|
|
|
+ data: [],
|
|
|
+ columns: [],
|
|
|
+ rowKey: 'id',
|
|
|
+ height: '400px',
|
|
|
+ onCheckboxChange(props) {
|
|
|
+ const gridRef = getGridRef();
|
|
|
+ selectedData.value = gridRef.getCheckboxRecords(true);
|
|
|
+ },
|
|
|
+ onCheckboxAll(props) {
|
|
|
+ const gridRef = getGridRef();
|
|
|
+ selectedData.value = gridRef.getCheckboxRecords(true);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const editOpt = {
|
|
|
+ keepSource: true,
|
|
|
+ editConfig: {
|
|
|
+ trigger: 'click',
|
|
|
+ mode: 'row',
|
|
|
+ showStatus: true,
|
|
|
+ },
|
|
|
+ onEditActivated(params) {
|
|
|
+ const { $grid, row, column } = params;
|
|
|
+ },
|
|
|
+ onEditClosed(params) {
|
|
|
+ const { $grid, row, column } = params;
|
|
|
+ const tableData = cloneDeep(props.value);
|
|
|
+ tableData.forEach((item) => {
|
|
|
+ if ((has(item, rowKey.value) && item[rowKey.value] === row[rowKey.value]) || item[ROW_KEY_FIELD] === row[ROW_KEY_FIELD]) {
|
|
|
+ item[column.field] = row[column.field];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ emits('update:value', tableData);
|
|
|
+ emits('change', tableData);
|
|
|
+ },
|
|
|
+ };
|
|
|
+ if (props.isEditRow) {
|
|
|
+ return {
|
|
|
+ ...customOpt,
|
|
|
+ gridOptions: {
|
|
|
+ ...customOpt.gridOptions,
|
|
|
+ ...editOpt,
|
|
|
+ ...props.tableOptions.gridOptions,
|
|
|
+ columns: [...selectAndSeqCols, ...props.tableOptions.gridOptions?.columns, ...optionsCols],
|
|
|
+ },
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ return {
|
|
|
+ ...customOpt,
|
|
|
+ gridOptions: {
|
|
|
+ ...customOpt.gridOptions,
|
|
|
+ ...props.tableOptions.gridOptions,
|
|
|
+ columns: [...selectAndSeqCols, ...props.tableOptions.gridOptions?.columns],
|
|
|
+ },
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const bsTableBean = useBsTable({ tableOptions: bsTableAdapter.value });
|
|
|
+ const { tableOptions: options, setTablePropsValue: setValue, getTablePropsValue: getValue, getGridRef, refreshTable } = bsTableBean;
|
|
|
+
|
|
|
const handleAddBtn = () => {
|
|
|
- const tableData = getValue('gridOptions.data');
|
|
|
+ const newData = isEmpty(props.value) ? [] : [...props.value];
|
|
|
+ newData.push({
|
|
|
+ [ROW_KEY_FIELD]: getUUID(),
|
|
|
+ });
|
|
|
+ emits('update:value', newData);
|
|
|
+ emits('change', newData);
|
|
|
emits('add', {
|
|
|
gridRef: getGridRef(),
|
|
|
bsTable: bsTableBean,
|
|
|
- tableData,
|
|
|
+ tableData: newData,
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const handleBatchDelete = () => {
|
|
|
- const tableData = getValue('gridOptions.data');
|
|
|
- const newTableData = tableData.filter((v) => selectedData.value.findIndex((v1) => v[props.rowKey] === v1[props.rowKey]) === -1);
|
|
|
- // setValue('gridOptions.data', newTableData);
|
|
|
+ const tableData = props.value;
|
|
|
+ const newTableData = tableData.filter(
|
|
|
+ (v) =>
|
|
|
+ selectedData.value.findIndex((v1) => {
|
|
|
+ if (has(v, rowKey.value)) {
|
|
|
+ return v[rowKey.value] === v1[rowKey.value];
|
|
|
+ } else {
|
|
|
+ return v[ROW_KEY_FIELD] === v1[ROW_KEY_FIELD];
|
|
|
+ }
|
|
|
+ }) === -1
|
|
|
+ );
|
|
|
emits('update:value', newTableData);
|
|
|
emits('change', newTableData);
|
|
|
+ selectedData.value = [];
|
|
|
+ emits('delete-rows', {
|
|
|
+ gridRef: getGridRef(),
|
|
|
+ bsTable: bsTableBean,
|
|
|
+ tableData: newTableData,
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
watch(
|
|
|
() => props.value,
|
|
|
(val) => {
|
|
|
- setValue('gridOptions.data', val);
|
|
|
+ // setValue('gridOptions.data', val);
|
|
|
+ refreshTable();
|
|
|
},
|
|
|
{ immediate: true }
|
|
|
);
|
|
|
+
|
|
|
+ defineExpose({
|
|
|
+ validator() {
|
|
|
+ return new Promise(async (resolve, reject) => {
|
|
|
+ const gridRef = getGridRef();
|
|
|
+ const errMap = await gridRef.validate(true);
|
|
|
+ if (errMap) {
|
|
|
+ reject(errMap);
|
|
|
+ } else {
|
|
|
+ resolve(true);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
@@ -173,7 +285,7 @@
|
|
|
padding: 0;
|
|
|
}
|
|
|
:deep(.vxe-grid--layout-body-wrapper) {
|
|
|
- padding: 10px;
|
|
|
+ padding: 0;
|
|
|
}
|
|
|
|
|
|
:deep(.top-bottom) {
|