table_v2.vue 15 KB
<script lang="ts" setup name="Table">
import { ref, computed } from 'vue'
import { QuestionFilled } from '@element-plus/icons-vue'
import { setItemsDisabled, tagMethod, tagType, changeNum } from '@/utils/common';
import PageNav from '../PageNav/index.vue'
import BtnPopover from '../Popover/index.vue'

const props = defineProps({
  tableInfo: {
    type: Object,
    default: {}
  },
  popverInfo: {
    type: Object,
    default: {}
  }
})

const emits = defineEmits([
  'tableBtnClick',
  'tableSwitchBeforeChange',
  'tableSwitchChange',
  'tableSelectChange',
  'tableSelectionChange',
  'tableCheckboxAllSelectChange',
  'tableCheckboxSelectChange',
  'tablePageChange',
  'tableRowClick',
  'tableRowDblClick',
  'tableInputChange'
]);

const tableRef = ref()

defineExpose({
  tableRef
});

const showPage = computed(() => {
  return props.tableInfo.showPage ?? true
})
const maxHeight = computed(() => {
  return props.tableInfo.maxHeight ? props.tableInfo.maxHeight : showPage.value ? 'calc(100% - 44px)' : '100%'
})
const multiple = computed(() => {
  return props.tableInfo.multiple ?? false
})
const tableDataLoading = computed(() => {
  return props.tableInfo.loading ?? false;
})
const tableData = computed(() => {
  return props.tableInfo.data
})
const tableFields = computed(() => {
  return props.tableInfo.fields
})
const actionInfo: any = computed(() => {
  return props.tableInfo.actionInfo ?? {}
})
const popverData: any = computed(() => {
  return props.popverInfo ?? {}
})
const rowKey = computed(() => {
  return props.tableInfo.rowKey ?? ''
})
const currentRowKey = computed(() => {
  return props.tableInfo.currentRowKey ?? ''
})
const editForm = computed(() => {
  const readonly = props.tableInfo.readonly ?? false
  const editInfo = props.tableInfo.editInfo ?? {}
  return setItemsDisabled(editInfo, readonly)
  // return editInfo
})
const pageInfo = computed(() => {
  return props.tableInfo.page
})

const tableRowClassName = (row, rowIndex) => {
  if (rowIndex === 1) {
    return 'warning-row'
  } else if (rowIndex === 3) {
    return 'success-row'
  }
  return ''
}

const beforeChange = (scope, item) => {
  return new Promise((resolve, reject) => {
    emits('tableSwitchBeforeChange', scope, item.field, (state) => {
      if (state) {
        resolve(true)
      } else {
        reject(false)
      }
    });
  })
}
/** field表示当前列对应的prop。同一个表格可能存在多个状态列修改。 */
const switchChange = (val, scope, field) => {
  emits('tableSwitchChange', val, scope, field)
}
const selectChange = (val, scope) => {
  emits('tableSelectChange', val, scope)
}
const handleClick = (scope, btn) => {
  if (scope.row.disabled || btn.disabled) return
  emits('tableBtnClick', scope, btn)
}
const selectionChange = (val) => {
  const tId = props.tableInfo.id
  emits('tableSelectionChange', val, tId)
}

const tableCheckboxSelectChange = (select, row) => {
  emits('tableCheckboxSelectChange', select, row);
}

const tableCheckboxAllSelectChange = (select, row) => {
  emits('tableCheckboxAllSelectChange', select, row);
}

const inputChange = (val, scope) => {
  emits('tableInputChange', val, scope)
}

const rowClick = (row, column, event) => {
  setTimeout(() => {
    emits('tableRowClick', row, props.tableInfo.id)
  }, 200)
}

const rowDblClick = (row, column) => {
  emits('tableRowDblClick', row, column)
}

const pageChange = (page) => {
  let info = { ...page, id: props.tableInfo.id }
  emits('tablePageChange', info)
}

onMounted(() => {
  // setTimeout(() => {
  //   if (tableRef.value) {
  //     const tablePanelDom = document.getElementById(`${props.tableInfo.id}`)
  //     if (tablePanelDom) {
  //       const tableDom = tablePanelDom.childNodes[0]
  //       tablePanelDom.style.width = tableDom.offsetWidth ? tableDom.offsetWidth + 'px' : '100%'
  //     }
  //   }
  // }, 200);
})
</script>

<template>
  <div class="table_panel" :id="props.tableInfo.id">
    <el-table-v2 ref="tableRef" :class="{ 'no-edit': props.tableInfo.readonly }" :data="tableData"
      :highlight-current-row="true" stripe border height="100%" :max-height="maxHeight" :row-key="rowKey"
      :current-row-key="currentRowKey" v-loading="tableDataLoading" @row-click="rowClick" @row-dblclick="rowDblClick"
      @selection-change="selectionChange" @select="tableCheckboxSelectChange" @select-all="tableCheckboxAllSelectChange"
      style="width: 100%; display: inline-block;" :style="{ 'min-height': props.tableInfo.minHeight ?? '394px' }"
      tooltip-effect="light" :tooltip-options="{ placement: 'top', popperClass: 'table_cell_tooltip' }">
      <el-table-column type="selection" :width="32" align="center" v-if="multiple" />
      <el-table-column v-for="item in tableFields" :label="item.label" :width="item.width" :min-width="item.minWidth"
        :fixed="item.fixed" :align="item.align" :sortable="item.sortable ?? false" :prop="item.field"
        :class-name="item.columClass" show-overflow-tooltip>
        <template #default="scope" v-if="item.type == 'index'">
          <span v-if="showPage">{{ (pageInfo.curr - 1) * pageInfo.limit + scope.$index + 1 }}</span>
          <span v-else>{{ scope.$index + 1 }}</span>
        </template>
        <template #default="scope" v-else-if="item.type == 'tag'">
          <el-tag :type="tagType(scope.row, item.field)">{{
            tagMethod(scope.row, item.field)
            }}</el-tag>
        </template>
        <template #default="scope" v-else-if="item.type == 'filter'">
          <span>{{ tagMethod(scope.row, item.field) }}</span>
        </template>
        <template #default="scope" v-else-if="item.type == 'chnum'">
          <span>{{ changeNum(scope.row[item.field]?scope.row[item.field]:0, item.fixedNum ?? 0) }}</span>
          <span v-if="item.unit">{{ item.unit }}</span>
        </template>
        <template #default="scope" v-else-if="item.type == 'switch'">
          <el-switch v-model="scope.row[item.field]" inline-prompt :active-value="item.activeValue"
            :inactive-value="item.inactiveValue" :width="item.switchWidth" :active-text="item.activeText"
            :inactive-text="item.inactiveText" :before-change="() => beforeChange(scope, item)"
            @change="(val) => switchChange(val, scope, item.field)" />
        </template>
        <template #default="scope" v-else-if="item.type == 'text_btn'">
          <span v-if="scope.row[item.field]" class="text_btn"
            @click="handleClick(scope, { label: item.label, value: item.value })" v-preReClick>{{ scope.row[item.field]
            }}</span>
          <span v-else>--</span>
        </template>
        <template #default="scope" v-else-if="item.type == 'btn'">
          <template v-for="btn in item.btns">
            <span v-if="btn.visible ?? true" class="text_btn" @click="handleClick(scope, btn)" v-preReClick>{{ btn.label
              }}</span>
          </template>
        </template>
        <template #default="scope" v-else-if="item.type == 'input'">
          <el-input v-model="scope.row[item.field]" placeholder="请输入"></el-input>
        </template>
        <template #default="scope" v-else-if="item.type == 'tooltip'">
          <el-tooltip :placement="item.placement??'bottom-start'" effect="light" popper-class="table_tooltip"
            :trigger="item.trigger">
            <template #content>
              <div style="width: 236px; text-align: justify">
                <p class="tips_title">不符合规则</p>
                <p>1.规范规则【规范规则1】</p>
                <p>2.多字段约束规则【多字段约束规则1】</p>
              </div>
            </template>
            <span>{{ scope.row[item.field] ?? "--" }}</span>
          </el-tooltip>
        </template>
        <template #default="scope" v-else-if="item.type && item.type == 'edit'">
          <template v-if="scope.row.status == 'edit'">
            <el-select v-if="editForm[item.field] && editForm[item.field].type == 'select'"
              v-model="scope.row[item.field]" :placeholder="editForm[item.field].placeholder"
              :clearable="editForm[item.field].clearable" :disabled="editForm[item.field].disabled"
              @change="val => selectChange(val, scope)">
              <el-option v-if="props.tableInfo.readonly" label="--" value="" />
              <el-option v-for="opt in editForm[item.field].options" :key="opt.value" :label="opt.label"
                :value="opt.value" />
            </el-select>
            <el-input v-else-if="editForm[item.field] && editForm[item.field].type == 'input'"
              v-model="scope.row[item.field]" :placeholder="editForm[item.field].placeholder"
              :clearable="editForm[item.field].clearable" :disabled="editForm[item.field].disabled"
              @change="val => inputChange(val, scope)"></el-input>
            <template v-else-if="editForm[item.field] && editForm[item.field].type == 'btn'">
              <template v-for="btn in scope.row[item.field].btns">
                <span v-if="btn.visible ?? true" class="text_btn" @click="handleClick(scope, btn)" v-preReClick>{{
                  btn.label }}</span>
              </template>
            </template>
            <template v-else-if="editForm[item.field] && editForm[item.field].type == 'comb'">
              <div v-if="scope.row['ruleType'] == 'Serial'" class="input_comb">
                <span>
                  <span>起始值</span>
                  <el-input v-model="scope.row.start" style="width: 40px"></el-input>
                </span>
                <span>
                  <span>最大值</span>
                  <el-input v-model="scope.row.max" style="width: 100px"></el-input>
                </span>
                <span>
                  <span>长度</span>
                  <el-input v-model="scope.row.len" style="width: 40px"></el-input>
                </span>
                <span>
                  <span>步长</span>
                  <el-input v-model="scope.row.step" style="width: 40px"></el-input>
                </span>
              </div>
              <el-input v-else-if="scope.row['ruleType'] == 'Fixed'" v-model="scope.row.fixedValue" clearable></el-input>
              <el-select v-else-if="scope.row['ruleType'] == 'Separator'" v-model="scope.row.separator" clearable>
                <el-option v-if="props.tableInfo.readonly" label="--" value="" />
                <el-option v-for="opt in editForm[item.field].codeRule.Separator.options" :key="opt.value"
                  :label="opt.label" :value="opt.value" />
              </el-select>
              <el-select v-else-if="scope.row['ruleType'] == 'Date'" v-model="scope.row.format" clearable>
                <el-option v-if="props.tableInfo.readonly" label="--" value="" />
                <el-option v-for="opt in editForm[item.field].codeRule.Date.options" :key="opt.value" :label="opt.label"
                  :value="opt.value" />
              </el-select>
              <el-select v-else-if="scope.row['ruleType'] == 'Guid'" v-model="scope.row.mode" clearable>
                <el-option v-if="props.tableInfo.readonly" label="--" value="" />
                <el-option v-for="opt in editForm[item.field].codeRule.Guid.options" :key="opt.value" :label="opt.label"
                  :value="opt.value" />
              </el-select>
              <span v-else>{{ scope.row[item.field] ?? "--" }}</span>
            </template>
            <span v-else>{{ scope.row[item.field] ?? "--" }}</span>
          </template>
          <span v-else>{{ scope.row[item.field] ?? "--" }}</span>
        </template>
        <template #default="scope" v-else>
          <!--- 直接用??会导致空字符串时无法显示'--',此处需要区分0和“” -->
          <span>{{ (scope.row[item.field] !== 0 && !scope.row[item.field]) ? "--" : (item.getName ? item.getName(scope) :
            scope.row[item.field]) }}</span>
        </template>
      </el-table-column>
      <el-table-column :width="actionInfo.width" :class-name="actionInfo.columClass" fixed="right"
        v-if="actionInfo.show ?? true">
        <template #header>
          <div class="header_title">
            <span>{{ actionInfo.label ?? "操作" }}</span>
            <el-tooltip placement="top" effect="light" popper-class="table_tooltip" v-if="actionInfo.tooltip">
              <template #content>
                <div style="width: 236px; text-align: justify">
                  帮助:初始化为系统底层操作,为会员创建数据库及系统表操作,一旦初始化将不可以删除会员。
                </div>
              </template>
              <el-icon>
                <QuestionFilled />
              </el-icon>
            </el-tooltip>
          </div>
        </template>
        <template #default="scope">
          <!-- 某些操作按钮需要根据当前行信息设置显示隐藏,actionInfo.btns使用回调函数返回。 -->
          <template v-for="(btn, b) in Array.isArray(actionInfo.btns) ? actionInfo.btns : actionInfo.btns(scope)">
            <span class="operate_btn" :class="{ active: btn.visible ?? true }" v-if="btn.visible ?? true">
              <BtnPopover v-if="btn.value == 'authority'" :popover-info="{ scope, btn, ...popverData }"
                @showPopver="handleClick" @popverBtnClick="handleClick" />
              <span class="text_btn" :class="{ 'is-disabled': !!btn.disabled }" v-else @click="handleClick(scope, btn)"
                v-preReClick>{{
                  btn.label }}</span>
            </span>
          </template>
          <template v-if="props.tableInfo.id.indexOf('maintenance') > -1 && scope.row.isInit == 'N'
            ">
            <span class="text_btn" @click="handleClick(scope, { label: '初始化', value: 'init' })" v-preReClick>初始化</span>
          </template>
          <template v-else-if="props.tableInfo.id.indexOf('product-menu') > -1 && scope.row.menuType != 'F'
            ">
            <span class="text_btn" @click="handleClick(scope, { label: '添加子菜单', value: 'menu' })"
              v-preReClick>添加子菜单</span>
          </template>
        </template>
      </el-table-column>
    </el-table-v2>
    <PageNav v-if="showPage" :class="[pageInfo.type]" :pageInfo="pageInfo" @pageChange="pageChange" />
  </div>
</template>

<style lang="scss" scoped>
.table_panel {
  width: 100%;
  height: 100%;
  min-height: 436px;
  overflow: hidden;
  position: relative;
}

:deep(.el-table) {
  .header_title {
    display: flex;
    align-items: center;

    .el-icon {
      color: #b2b2b2;
      width: 16px;
      height: 16px;
      margin-left: 8px;

      svg {
        width: 100%;
        height: 100%;
      }
    }
  }

  .input_comb {
    >span {
      margin-right: 12px;

      >span {
        margin-right: 4px;
      }
    }
  }


  .operate_btn {
    &.active {
      padding-right: 4px;
      position: relative;

      &~.active {
        padding-left: 4px;
        margin-left: 1px;

        &::before {
          content: '';
          width: 1px;
          height: 8px;
          background-color: var(--el-disabled-border-color);
          position: absolute;
          left: -1px;
          top: 50%;
          transform: translateY(-50%);
        }
      }

      &:last-child {
        padding-right: 0;
      }
    }
  }

}
</style>