zrlibs
2025-03-21 cd24c776d772c7ad797891afd779b3b072293ec2
src/components/examples/data-table.vue
@@ -1,15 +1,196 @@
<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"
@@ -25,44 +206,174 @@
      @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);
    },
@@ -89,17 +400,8 @@
    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);
@@ -116,19 +418,100 @@
    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>