| | |
| | | <template> |
| | | <div class="data-table"> |
| | | <div class="data-table" ref="refDataTable"> |
| | | <div v-if="showForm" class="forms" :class="{ collapse: !formExpand }"> |
| | | <Form :model="form" v-bind="formObject.options" ref="refForm"> |
| | | <Row> |
| | | <Col |
| | | v-for="(col, index) in formList" |
| | | :key="`${index}`" |
| | | :span="col.span" |
| | | > |
| | | <FormItem :label="`${col.label}`"> |
| | | <Input |
| | | v-if="col.type == 'input'" |
| | | v-model="form[col.field]" |
| | | size="small" |
| | | > |
| | | <template v-if="col.suffix" #suffix> |
| | | <Icon :type="col.suffix" @click="col.onSuffixClick" /> |
| | | </template> |
| | | </Input> |
| | | <DatePicker |
| | | v-else-if="col.type == 'daterange'" |
| | | type="daterange" |
| | | v-model="form[col.field]" |
| | | separator=" 至 " |
| | | size="small" |
| | | style="width: 100%" |
| | | @on-change="col.onChange" |
| | | ></DatePicker> |
| | | <DatePicker |
| | | v-else-if="col.type == 'date'" |
| | | type="date" |
| | | v-model="form[field]" |
| | | size="small" |
| | | @on-change="col.onChange" |
| | | /> |
| | | <Select |
| | | v-else-if="col.type == 'select'" |
| | | v-model="form[col.field]" |
| | | size="small" |
| | | > |
| | | <Option |
| | | v-for="(opt, i) in col.options" |
| | | :key="i" |
| | | :value="col.value" |
| | | >{{ col.label }}</Option |
| | | > |
| | | </Select> |
| | | <Checkbox |
| | | v-else-if="col.type == 'checkbox'" |
| | | v-model="form[col.field]" |
| | | size="small" |
| | | >{{ form.label }}</Checkbox |
| | | > |
| | | </FormItem> |
| | | </Col> |
| | | </Row> |
| | | </Form> |
| | | <div class="btns"> |
| | | <Button type="primary" size="small" |
| | | ><Icon type="md-settings" @click="onShowFormDialog" |
| | | /></Button> |
| | | <Button |
| | | v-if="formExpand" |
| | | v-show="showFormExpand" |
| | | type="primary" |
| | | size="small" |
| | | @click="formExpand = !formExpand" |
| | | ><Icon type="ios-arrow-up" |
| | | /></Button> |
| | | <Button |
| | | v-else |
| | | v-show="showFormExpand" |
| | | type="primary" |
| | | size="small" |
| | | @click="formExpand = !formExpand" |
| | | ><Icon type="ios-arrow-down" |
| | | /></Button> |
| | | </div> |
| | | </div> |
| | | <div class="buttons" v-if="showTopButtons"> |
| | | <Space> |
| | | <Button |
| | | v-for="(btn, i) in buttons" |
| | | :key="i" |
| | | type="primary" |
| | | size="small" |
| | | @click="btn.onClick" |
| | | >{{ btn.showName }}</Button |
| | | > |
| | | </Space> |
| | | <Space class="ivu-fr"> |
| | | <Poptip title="分页器位置" placement="left-start"> |
| | | <Tooltip content="设置分页器位置" transfer> |
| | | <Button size="small"><Icon type="md-apps" /></Button> |
| | | </Tooltip> |
| | | <template #content> |
| | | <RadioGroup |
| | | v-model="paganationPlacement" |
| | | @on-change="onPaganationPlacementChange" |
| | | > |
| | | <Radio label="top"> |
| | | <Icon type="md-at"></Icon> |
| | | <span>上</span> |
| | | </Radio> |
| | | <Radio label="bottom"> |
| | | <Icon type="md-basketball"></Icon> |
| | | <span>下</span> |
| | | </Radio> |
| | | </RadioGroup> |
| | | </template> |
| | | </Poptip> |
| | | <Poptip popper-class="col-list" placement="left-start"> |
| | | <Tooltip content="调整列" transfer> |
| | | <Button size="small"><Icon type="md-settings" /></Button> |
| | | </Tooltip> |
| | | <template #title> |
| | | <Checkbox |
| | | :indeterminate="columnListIndeterminate" |
| | | :model-value="columnCheckAll" |
| | | @click.prevent="onColumnCheckAll" |
| | | >全选</Checkbox |
| | | > |
| | | </template> |
| | | <template #content> |
| | | <CheckboxGroup |
| | | v-model="columnShowList" |
| | | @on-change="onShowColumnChange" |
| | | > |
| | | <Checkbox v-for="(col, i) in columns" :key="i" :label="col.key">{{ |
| | | toColumnLabel(col) |
| | | }}</Checkbox> |
| | | </CheckboxGroup> |
| | | </template> |
| | | </Poptip> |
| | | <Poptip title="设置字体大小" placement="left-start"> |
| | | <Tooltip content="调整字体" transfer> |
| | | <Button size="small"><Icon type="md-apps" /></Button> |
| | | </Tooltip> |
| | | <template #content> |
| | | <RadioGroup v-model="fontSize"> |
| | | <Radio label="small"> |
| | | <Icon type="md-at"></Icon> |
| | | <span>小</span> |
| | | </Radio> |
| | | <Radio label="default"> |
| | | <Icon type="md-basketball"></Icon> |
| | | <span>中</span> |
| | | </Radio> |
| | | <Radio label="large"> |
| | | <Icon type="ios-briefcase"></Icon> |
| | | <span>大</span> |
| | | </Radio> |
| | | </RadioGroup> |
| | | </template> |
| | | </Poptip> |
| | | <Tooltip content="打印" transfer> |
| | | <Button size="small"><Icon type="ios-print" /></Button> |
| | | </Tooltip> |
| | | </Space> |
| | | <Page |
| | | v-if="paged && paganationPlacement == 'top'" |
| | | class="absolute" |
| | | :model-value="page" |
| | | :total="total" |
| | | :page-size="limit" |
| | | :page-size-opts="limits" |
| | | show-sizer |
| | | show-total |
| | | show-elevator |
| | | transfer |
| | | simple |
| | | @on-change="onPageChange" |
| | | @on-page-size-change="onPageSizeChange" |
| | | @on-prev="onPrev" |
| | | @on-next="onNext" |
| | | /> |
| | | </div> |
| | | <Table |
| | | class="ivu-table-mini" |
| | | :row-class-name="() => ['no-wrap', 'col-gap-none']" |
| | | :columns="columns" |
| | | class="col-gap-none" |
| | | :row-class-name="() => ['no-wrap']" |
| | | :columns="tableColumns" |
| | | :data="data" |
| | | :height="tableHeight - 70" |
| | | border |
| | | :height="tableHeight" |
| | | :highlight-row="highlightRow" |
| | | :size="fontSize" |
| | | @on-row-click="onRowClick" |
| | | @on-current-change="onCurrentChange" |
| | | @on-selection-change="onSelectionChange" |
| | | ref="refTable" |
| | | ></Table> |
| | | <Page |
| | | v-if="paged && paganationPlacement == 'bottom'" |
| | | class="text-center" |
| | | :model-value="page" |
| | | :total="total" |
| | |
| | | @on-next="onNext" |
| | | ref="refPage" |
| | | /> |
| | | <Spin fix :show="loading" /> |
| | | <FormDialog |
| | | v-model="formDialogVisible" |
| | | :list="formObject.items" |
| | | @ok="onFormDialogOK" |
| | | ></FormDialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ref } from "vue"; |
| | | import FormDialog from "./form-dialog.vue"; |
| | | export default { |
| | | name: "DataTable", |
| | | components: { |
| | | FormDialog, |
| | | }, |
| | | props: { |
| | | tableHeight: Number | String, |
| | | // 容器高度 |
| | | height: Number | String, |
| | | // 常规顶部按钮 |
| | | buttons: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | // 是否分页 |
| | | paged: { |
| | | type: Boolean, |
| | | default: () => true, |
| | | }, |
| | | // 列定义 |
| | | columns: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | // 数据源 |
| | | data: { |
| | | type: Array, |
| | | default: () => [], |
| | | }, |
| | | // 数据总数 |
| | | total: { |
| | | type: Number, |
| | | default: () => 0, |
| | | }, |
| | | // 当前页 |
| | | page: { |
| | | type: Number, |
| | | default: () => 1, |
| | | }, |
| | | // 每页几条数据 |
| | | limit: { |
| | | type: Number, |
| | | default: () => 10, |
| | | }, |
| | | // limit列表 |
| | | limits: { |
| | | type: Array, |
| | | default: () => [10, 20, 30, 40], |
| | | }, |
| | | // 记录加载状态 |
| | | loading: Boolean, |
| | | // 单选模式 |
| | | highlightRow: Boolean, |
| | | showForm: { |
| | | type: Boolean, |
| | | default: () => true, |
| | | }, |
| | | showTopButtons: { |
| | | type: Boolean, |
| | | default: () => true, |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: () => "default", |
| | | }, |
| | | pagePlacement: { |
| | | type: String, |
| | | default: () => "bottom", // top/bottom |
| | | }, |
| | | formObject: { |
| | | type: Object, |
| | | default: () => ({}), |
| | | }, |
| | | }, |
| | | data() { |
| | | return {}; |
| | | return { |
| | | // 配置哪些列显示 |
| | | columnListIndeterminate: false, |
| | | columnCheckAll: true, |
| | | columnShowList: [], |
| | | // 配置字体 |
| | | fontSize: this.size, |
| | | // 查询表单 |
| | | // 查询表单的定义 |
| | | form: {}, |
| | | formExpand: false, |
| | | showFormExpand: true, |
| | | formDialogVisible: false, |
| | | formShowList: [], |
| | | paganationPlacement: this.pagePlacement, |
| | | }; |
| | | }, |
| | | computed: { |
| | | // 数据列表高度 |
| | | tableHeight() { |
| | | let formHeight = this.showForm |
| | | ? this.refDataTable?.querySelector(".forms")?.clientHeight + 9 |
| | | : 0; |
| | | let buttonHeight = this.showTopButtons |
| | | ? this.refDataTable?.querySelector(".buttons")?.clientHeight |
| | | : 0; |
| | | let pageHeight = |
| | | this.paged && this.paganationPlacement == "bottom" ? 70 - 18 : 0; |
| | | return this.height - formHeight - buttonHeight - pageHeight; |
| | | }, |
| | | // 实际要显示的列 |
| | | tableColumns() { |
| | | return this.columns.filter((col) => |
| | | this.columnShowList.includes(col.key) |
| | | ); |
| | | }, |
| | | // 实际要显示的表单项 |
| | | formList() { |
| | | return ( |
| | | this.formObject.items?.filter((l) => |
| | | this.formShowList.includes(l.field) |
| | | ) || [] |
| | | ); |
| | | }, |
| | | }, |
| | | methods: { |
| | | toColumnLabel(col) { |
| | | if (col.key == "index") return "序号"; |
| | | else if (col.key == "row_button") return "操作按钮"; |
| | | else return col.title; |
| | | }, |
| | | onPaganationPlacementChange() { |
| | | this.$nextTick(() => this.resize()); |
| | | }, |
| | | // 配置列-全选 |
| | | onColumnCheckAll() { |
| | | if (this.columnListIndeterminate) { |
| | | this.columnCheckAll = false; |
| | | } else { |
| | | this.columnCheckAll = !this.columnCheckAll; |
| | | } |
| | | this.columnListIndeterminate = false; |
| | | |
| | | if (this.columnCheckAll) { |
| | | this.columnShowList = this.columns.map((c) => c.key); |
| | | } else { |
| | | this.columnShowList = []; |
| | | } |
| | | }, |
| | | // 配置列-单选 |
| | | onShowColumnChange(data) { |
| | | if (data.length == this.columns.length) { |
| | | this.columnListIndeterminate = false; |
| | | this.columnCheckAll = true; |
| | | } else if (data.length > 0) { |
| | | this.columnListIndeterminate = true; |
| | | this.columnCheckAll = false; |
| | | } else { |
| | | this.columnListIndeterminate = false; |
| | | this.columnCheckAll = false; |
| | | } |
| | | }, |
| | | onPageChange(page) { |
| | | this.$emit("on-change", page); |
| | | }, |
| | |
| | | onCurrentChange(currentRow) { |
| | | this.$emit("on-current-change", currentRow); |
| | | }, |
| | | onRowClick(row, index, emit = true) { |
| | | this.clickedRowIndex = index; |
| | | this.onCurrentChange(row); |
| | | if (emit) this.$emit("on-row-click", row, index); |
| | | }, |
| | | onCellClick(row, column, data, event) { |
| | | event.stopPropagation(); |
| | | let index = row._index; |
| | | if (index === undefined) index = this.rows.find((r) => r.id == row.id); |
| | | if (column.key == "row_button") this.onRowClick(row, index, false); |
| | | else this.onRowClick(row, index, true); |
| | | onRowClick(row, index) { |
| | | this.$emit("on-row-click", row, index); |
| | | }, |
| | | onSelect(selection, row) { |
| | | this.$emit("on-select", selection, row); |
| | |
| | | onColumnWidthResize(newWidth, oldWidth, column, event) { |
| | | this.$emit("on-column-width-resize", newWidth, oldWidth, column, event); |
| | | }, |
| | | onShowFormDialog() { |
| | | this.formDialogVisible = true; |
| | | }, |
| | | resize() { |
| | | this.$nextTick(() => { |
| | | let height = this.refForm?.$el?.clientHeight; |
| | | if (height < 50) { |
| | | this.formExpand = false; |
| | | this.showFormExpand = false; |
| | | } else { |
| | | this.formExpand = true; |
| | | this.showFormExpand = true; |
| | | } |
| | | }); |
| | | }, |
| | | onFormDialogOK(showList) { |
| | | this.formShowList = showList; |
| | | }, |
| | | }, |
| | | setup() { |
| | | const refDataTable = ref(null); |
| | | const refForm = ref(null); |
| | | const refTable = ref(null); |
| | | const refPage = ref(null); |
| | | return { refTable, refPage }; |
| | | return { refDataTable, refForm, refTable, refPage }; |
| | | }, |
| | | watch: { |
| | | columns(columns) { |
| | | this.columnShowList = columns.map((c) => c.key); |
| | | }, |
| | | ["formObject.items"](list) { |
| | | this.formShowList = list.map((l) => l.field); |
| | | }, |
| | | formShowList() { |
| | | this.resize(); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | <style lang="less"> |
| | | .data-table { |
| | | height: 100%; |
| | | position: relative; |
| | | .forms { |
| | | margin-bottom: 9px; |
| | | padding: 0 10px; |
| | | position: relative; |
| | | &.collapse { |
| | | height: 30px; |
| | | padding-right: 100px; |
| | | overflow: hidden; |
| | | } |
| | | .btns { |
| | | position: absolute; |
| | | right: 10px; |
| | | bottom: 0; |
| | | display: flex; |
| | | flex-direction: row; |
| | | gap: 10px; |
| | | z-index: 1; |
| | | } |
| | | .ivu-form .ivu-form-item-label, |
| | | .ivu-checkbox-wrapper { |
| | | font-size: 12px; |
| | | } |
| | | .ivu-input-wrapper { |
| | | .ivu-icon { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | } |
| | | .buttons { |
| | | padding: 0 9px; |
| | | line-height: 33px; |
| | | position: relative; |
| | | } |
| | | .text-center { |
| | | text-align: center; |
| | | } |
| | | } |
| | | .col-list { |
| | | .ivu-checkbox-group { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | } |
| | | .ivu-page { |
| | | padding: 9px; |
| | | } |
| | | .absolute { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 180px; |
| | | padding: 0px; |
| | | } |
| | | </style> |