<template> <div> <BaseTable :searchConfig="searchConfig" :operateBtnConfig="operateBtn" :tableData="tableData" :tableConfig="tableConfig" :pagination="pagination" :tableLoading="tableLoading" @search="search" @reset="reset" @selectionChange="selectionChange" @changePage="changePage" @keyupEnterNative="fetchList" @receiveSearch="receiveSearch" ref="BaseTable" idField="otherId" > <template v-slot:operate="{row}"> <el-link type="primary" size="medium" :underline="false" @click="editRow(row)" style="margin-right: 10px;">编辑</el-link> <el-link type="danger" size="medium" :underline="false" @click="deleteData(row)">删除</el-link> </template> </BaseTable>
<editForm v-if="editVisible" :visible="editVisible" :editId="editId" :companyList ="companyList" :editType="editType" @close="(type) => {type === 'reload' && this.fetchList();this.reloadCreators();editVisible = false}" ></editForm> </div> </template> <script> import { rules, searchConfig, tableConfig } from "./config"; import BaseTable from "@/components/BaseTable/index.vue" import baseMixins from "@/components/BaseTable/baseMixins" import editForm from "./components/editForm.vue" import { getCodingTypeList, getCreators ,} from "@/api/system/encodingRules" import{invoiceConfig ,invoiceConfigDelete} from "@/api/system/tickets" import { deepClone } from '@/utils'; import { getBranchCompany } from "@/api/shippingWorkOrder/index";
export default { data() { return { rules: rules, searchConfig: {}, tableConfig: tableConfig, tableData: [], editVisible: false, editType: 'add', editId: [], typeOptions: [], companyOptions: [], creatorsList: [], companyList:[], }; }, mixins: [ baseMixins ], components: { BaseTable, editForm }, computed: { operateBtn: function() { return [ { name: '新增', plain: false, icon: 'el-icon-plus', event: this.handleAdd }, { name: '删除', type: 'danger', icon: 'el-icon-delete', event: this.deleteData }, ] }, }, created() { this.fetchList() Promise.allSettled([this.getBranchCompany(), this.getCodingTypeList(), this.getCreators()]).then(() => { setTimeout(() => { this.init() }, 200) }) }, methods: { init() { this.searchConfig = searchConfig({companyOptions: this.companyOptions, typeOptions: this.typeOptions, creatorsList: this.creatorsList}) },
reloadCreators() { Promise.allSettled([this.getCreators()]).then(() => { this.init() }) },
// 录入人 async getCreators() { let { code, data } = await getCreators(); if (code == 0) { this.creatorsList = data.map(r => {return {value: r}}); } },
fetchList() { const { pageNo , pageSize } = this.pagination const {...rest } = this.searchForm const data = { pageNo, pageSize, ...rest } this.tableLoading = true invoiceConfig(data).then(res => { const { code, msg, data: { list, total } } = res
if (code === 0) { list.forEach((r, i) => { r.monthLength = 2 r.dayLength = 2 r.otherId = new Date().getTime() + i }) this.tableData = list; this.$set(this.pagination, 'total', total) } else { this.tableData = [] msg && this.$message.warning(msg) } }).finally(() => { this.tableLoading = false }) },
getCodingTypeList() { getCodingTypeList().then(res => { this.typeOptions = res.data }) },
getBranchCompany() { getBranchCompany().then((res) => { this.companyList = res.data; this.companyOptions = deepClone(res.data.map(r => { return {...r, checked: false} })); }); },
// 新增 handleAdd() { this.editVisible = false this.editType = 'add' this.editId = {} setTimeout(() => {this.editVisible = true}, 100) },
// 编辑 editRow(row) { this.editVisible = false this.editId = row this.editType = 'edit' setTimeout(() => {this.editVisible = true}, 100) },
// 删除 deleteData(row) { console.log(row); let ids = [] if (row) { ids.push(row.openInvoiceConfigId); } else { if (!this.getSelectRows()) return; ids = this.selectRows.map(r => r.openInvoiceConfigId) } console.log(ids); this.$confirm(`确认删除该编码规则吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { invoiceConfigDelete(ids).then(res => { const { code, data, msg} = res if (code === 0 && data) { this.$message.success(msg || '删除成功') this.reloadCreators() this.fetchList() } else { this.$message.warning(msg || '删除失败') } }) }); }, }, }; </script> <style scoped lang="scss"> </style> =============================================================================== 引入解释: config.js 配置 import { changeOptionsName } from '@/utils/utils' import { rulesItem } from '@/utils/constants'
export const searchConfig = ({ companyOptions, typeOptions, creatorsList }) => {
return { inline: true, form: [ { type: 'select', name: 'deptId', placeholder: '公司', options: changeOptionsName(companyOptions, 'id', 'name') || [], }, { type: 'input', name: 'taxNum', placeholder: '企业税号', options: creatorsList || [], }, ] } }
export const tableConfig = { highlightCurrentRow: true, fixedSelection: 'left', columns: [ { prop: 'deptName', label: '公司名', minWidth: 100 }, { prop: 'taxNum', label: '企业税号', }, { prop: 'appKey', label: 'APP KEY', }, { prop: 'appSecret', label: 'APP SECRET', }, { prop: 'token', label: 'TOKEN', minWidth: 80 }, { prop: 'extensionNum', label: '分机号', minWidth: 80 }, { prop: 'address', label: '地址', minWidth: 80 }, { prop: 'phone', label: '电话', minWidth: 80 }, { prop: 'payee', label: '收款人', minWidth: 110 }, { prop: 'checker', label: '复核人', minWidth: 110 }, { prop: 'drawer', label: '开票人', minWidth: 110 }, { prop: 'creator', label: '录入人', }, { prop: 'createTime', label: '录入时间', minWidth: 150 }, { prop: 'updater', label: '修改人', }, { prop: 'updateTime', label: '修改时间', minWidth: 150 }, { prop: 'operate', label: '操作', fixed: 'right', width: 96, align: 'center', headerAlign: 'center', columShow: 'true', slot: true } ] }
export const editRules = { taxNum: rulesItem, deptId:rulesItem, appKey:rulesItem, appSecret:rulesItem, token:rulesItem, extensionNum:rulesItem, } searchConfig 中是搜索菜单中的搜索条件 tableConfig : 是配置tableb表格 tableConfig ={ highlightCurrentRow :true 行高亮 fixedSelection: 'left', 左对齐 columns: [{}] 列参数 } editRules: 必填判断0 ======================================================================== 编辑弹框: <template> <BaseDialog :title="typeName[editType]" :visible.sync="visible" width="500px" @close="handleClose" @submit="submit" :loading="loading"> <el-form :model="form" :rules="editRules" ref="form" label-position="right"> <el-form-item label="公司名" prop="deptId"> <el-select v-model="form.deptId" filterable placeholder> <el-option v-for="(item, index) in companyList" :key="index" :label="item.name" :value="item.id"></el-option> </el-select> </el-form-item> <el-form-item label="企业税号" prop="taxNum"> <el-input v-model="form.taxNum" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="APP_KEY" prop="appKey"> <el-input v-model="form.appKey" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="APP_SECRET" prop="appSecret"> <el-input v-model="form.appSecret" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="TOKEN" prop="token"> <el-input v-model="form.token" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="分机号" prop="extensionNum"> <el-input v-model="form.extensionNum" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="地址" prop="address"> <el-input v-model="form.address" style="width: 194px;" type="textarea" show-word-limit maxlength="500" rows="5"></el-input> </el-form-item> <el-form-item label="电话" prop="phone"> <el-input v-model="form.phone" style="width: 194px;"></el-input> </el-form-item> <el-form-item label="收款人" prop="payee"> <el-select v-model="form.payee" filterable placeholder> <el-option v-for="(item, index) in listPayee" :key="index" :label="item.username" :value="item.username" ></el-option> </el-select> </el-form-item> <el-form-item label="复核人" prop="checker"> <el-select v-model="form.checker" filterable placeholder> <el-option v-for="(item, index) in listChecker" :key="index" :label="item.username" :value="item.username" ></el-option> </el-select> </el-form-item> <el-form-item label="开票人" prop="drawer"> <el-select v-model="form.drawer" filterable placeholder> <el-option v-for="(item, index) in listDrawer" :key="index" :label="item.username" :value="item.username" ></el-option> </el-select> </el-form-item> </el-form> </BaseDialog> </template>
<script> import { editRules } from "../config"; //调用的必填项参数 import { invoiceConfigCreate,invoiceConfigUpdate,invoiceConfigGet } from "@/api/system/tickets"; //接口数据 import {getAssignUser} from "@/api/shippingWorkOrder/index" //接口数据
export default { props: { editType: { // add edit copyAdd type: String, default: 'add' }, visible: false, editId: { type: Object, default: () => {} }, companyList:{ type: Array, default: () => [] } }, data() { return { dialogVisible: this.visible, typeName: { add: '新增', edit: '编辑', }, typeOptions:[], listDrawer:[], listChecker:[], listPayee:[], form: { }, editIdInfo:this.editId, editRules: editRules, loading: false, }; }, created() { this.getAssignUserList(); if(this.editType == 'edit'){ this.invoiceConfigGetList() } }, mounted() { }, methods: { handleClose(type) { this.$emit('close', type) }, // 提交 submit() { this.$refs.form.validate((valid) => { if (!valid) return false; const data = { ...this.form, } this.loading = true if (this.editType != 'edit') { this.handleRes(invoiceConfigCreate(data)) } else { this.handleRes(invoiceConfigUpdate(data)) } }); }, handleRes(_Promise) { _Promise.then(res => { const { code, msg } = res this.$message.success(msg || this.editType == 'add' ? '新增成功' : '编辑成功') this.handleClose('reload') }).finally(() => this.loading = false) }, getAssignUserList(){ getAssignUser().then((res)=>{ this.listDrawer = res.data; this.listChecker = res.data; this.listPayee = res.data; }) }, invoiceConfigGetList(){ console.log(this.editIdInfo,"222"); let params={ id:this.editIdInfo.openInvoiceConfigId } invoiceConfigGet(params).then((res)=>{ this.form = { ...res.data } }) } } }; </script>
<style lang="scss" scoped> .el-form-item{ ::v-deep .el-form-item__label{ width: 100px !important; } ::v-deep .el-form-item__content{ .el-select{ width: 300px; } .el-input{ width: 300px !important; } .el-textarea{ width: 300px !important; } } } .title { display: inline-block; padding: 0px 5px; color: var(--current-color); font-size: 16px; font-weight: 400; margin-bottom: 7px; margin-left: -5px; }
::v-deep .el-dialog__footer { text-align: center; }
.flip-list-move { transition: transform 0.5s; }
.no-move { transition: transform 0s; } </style> ======================================================================= 引入的base tabe 文件夹 BaseTable 下的index.vue <template> <div class="container baseTable-container"> <!-- 搜索栏 --> <div class="search search-box"> <template v-if="searchConfig.form && searchConfig.form.length"> <el-form ref="searchRef" :model="searchForm" :inline="searchConfig.inline" :label-width="searchConfig.labelWidth" @submit.native.prevent> <el-form-item v-for="(item, index) in searchConfig.form" :label="item.label" :key="index" :prop="item.name"> <el-input v-if="item.type === 'input'" v-model="searchForm[item.name]" :style="item.style" :placeholder="item.placeholder" :clearable="item.clearable === false ? false : true" :disabled="item.disabled" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative" @dblclick.native="v => commonInputDblclick(v, item)" :suffix-icon="item.suffixIcon" :prefix-icon="item.prefixIcon" ></el-input>
<el-input v-if="item.type === 'textarea'" type="textarea" v-model="searchForm[item.name]" :style="item.style" :placeholder="item.placeholder" :clearable="item.clearable === false ? false : true" :disabled="item.disabled" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative" @dblclick.native="v => commonInputDblclick(v, item)" :suffix-icon="item.suffixIcon" :prefix-icon="item.prefixIcon"></el-input>
<el-input v-if="item.type === 'number'" type="number" v-model="searchForm[item.name]" :style="item.style" :placeholder="item.placeholder" :clearable="item.clearable === false ? false : true" :disabled="item.disabled" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative" @dblclick.native="v => commonInputDblclick(v, item)" :suffix-icon="item.suffixIcon" :prefix-icon="item.prefixIcon"></el-input>
<el-date-picker v-if="item.type === 'date'" :type="item.datePickerType || 'daterange'" :clearable="item.clearable === false ? false : true" :disabled="item.disabled" :style="item.style" v-model="searchForm[item.name]" :range-separator="item.rangeSeparator || '至'" :start-placeholder="item.startPlaceholder || '开始时间'" :end-placeholder="item.endPlaceholder || '结束时间'" :value-format="item.valueFormat || (['daterange', 'data'].includes(item.datePickerType) ? 'yyyy-MM-dd' : ['datetimerange'].includes(item.datePickerType) ? 'yyyy-MM-dd HH:hh:mm' : 'yyyy-MM-dd')" :default-time="item.defaultTime || (item.datePickerType === 'datetimerange' ? ['00:00:00', '23:59:59'] : null)" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative" ></el-date-picker>
<el-time-select v-if="item.type === 'timeSelect'" v-model="searchForm[item.name]" :disabled="item.disabled" :clearable="item.clearable === false ? false : true" :style="item.style" :picker-options="item.pickerOptions" :placeholder="item.placeholder" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative"> </el-time-select>
<el-time-picker v-if="item.type === 'timePicker'" :is-range="item.isRange" :disabled="item.disabled" :clearable="item.clearable === false ? false : true" v-model="searchForm[item.name]" :style="item.style" :picker-options="item.pickerOptions" :range-separator="item.rangeSeparator || '至'" :start-placeholder="item.startPlaceholder || '开始时间'" :end-placeholder="item.endPlaceholder || '结束时间'" :placeholder="item.placeholder" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative"> </el-time-picker>
<el-select v-if="item.type === 'select'" v-model="searchForm[item.name]" :style="item.style" :placeholder="item.placeholder" :clearable="item.clearable === false ? false : true" filterable :disabled="item.disabled" :readonly="item.readonly" @keyup.enter.native="keyupEnterNative" @change="v => {commonSelectChange(v, item)}"> <template v-if="typeof(item.renderOption) === 'function'"> <el-option v-for="(selectItem, selectIndex) in item.options" :key="selectIndex" :label="item.renderOption(selectItem)" :value="selectItem.value"> {{ item.renderOption(selectItem) }} </el-option> </template> <template v-else> <el-option v-for="(selectItem, selectIndex) in item.options" :key="selectIndex" :label="selectItem.label" :value="selectItem.value"></el-option> </template> </el-select>
<template v-if="item.type === 'selectGroup'"> <el-col :span="11"> <el-select v-model="searchForm[item.list[0].name]" :style="item.list[0].style" :clearable="item.list[0].clearable === false ? false : true" :placeholder="item.list[0].placeholder || ''"> <el-option v-for="(selectItem, selectIndex) in item.list[0].options" :key="selectIndex" :label="selectItem.label" :value="selectItem.value"></el-option> </el-select> </el-col> <el-col style="text-align: center;" :span="2">—</el-col> <el-col :span="11"> <el-select v-model="searchForm[item.list[1].name]" :style="item.list[1].style" :clearable="item.list[1].clearable === false ? false : true" :placeholder="item.list[1].placeholder || ''"> <el-option v-for="(selectItem, selectIndex) in item.list[1].options" :key="selectIndex" :label="selectItem.label" :value="selectItem.value"></el-option> </el-select> </el-col> </template>
<template v-if="item.type === 'inputGroup'"> <el-col :span="11"> <el-input v-model="searchForm[item.list[0].name]" :type="item.list[0].type" :style="item.list[0].style" :placeholder="item.list[0].placeholder || ''" :clearable="item.list[0].clearable === false ? false : true" :disabled="item.list[0].disabled" :readonly="item.list[0].readonly" @keyup.enter.native="keyupEnterNative" ></el-input> </el-col> <el-col style="text-align: center;" :span="2">—</el-col> <el-col :span="11"> <el-input v-model="searchForm[item.list[1].name]" :type="item.list[0].type" :style="item.list[1].style" :placeholder="item.list[1].placeholder || ''" :clearable="item.list[1].clearable === false ? false : true" :disabled="item.list[1].disabled" :readonly="item.list[1].readonly" @keyup.enter.native="keyupEnterNative" ></el-input> </el-col> </template>
<v-selectpage v-if="item.type === 'selectPage'" :style="item.style" :ref="`selectPage_${item.name}`" v-model="searchForm[item.name]" :data="item.options" :key-field="item.keyField" :result-format="item.resultFormat || resultFormat" :show-field="item.showField" :search-field="item.searchField || 'searchField'" :params="item.searchParams" :multiple="item.multiple" :clearable="item.clearable === false ? false : true" :disabled="item.disabled" :tb-columns="item.tbColumns || defaultSelectPageColumns" :placeholder="item.placeholder || ' '"> </v-selectpage>
<base-selectPage v-if="item.type === 'baseSelectPage'" :style="item.style" v-model="searchForm[item.name]" :data="item.options" :keyField="item.keyField" :showField="item.showField" :searchField="item.searchField || 'keyword'" :concatField="item.concatField" :defaultRow="item.defaultRow || {}" :searchParams="item.searchParams" :multiple="item.multiple" :tb-columns="item.tbColumns || defaultSelectPageColumns" :disabled="item.disabled" :clearable="item.clearable === false ? false : true" :placeholder="item.placeholder || ''" @change="v => {commonSelectChange(v, item)}"> </base-selectPage>
<el-cascader v-if="item.type === 'cascader'" v-model="searchForm[item.name]" :clearable="item.clearable === false ? false : true" :options="item.options" :style="item.style" :placeholder="item.placeholder" :disabled="item.disabled" :readonly="item.readonly"></el-cascader>
<treeselect v-if="item.type === 'treeselect'" v-model="searchForm[item.name]" :options="item.options" :normalizer="typeof(item.normalizer) === 'function' ? item.normalizer : normalizer" :show-count="item.showCount === false ? false : true" :placeholder="item.placeholder" :disabled="item.disabled" :multiple="item.multiple" :clearable="item.clearable === false ? false : true"/>
<template v-if="item.type === 'slot'"> <slot :name="item.name" :form="searchForm"></slot> </template> </el-form-item>
<!-- 三种布局类型 last / bottom / more --> <el-form-item v-if="!searchConfig.layoutType || searchConfig.layoutType === 'last'"> <el-button type="primary" @click="search">查 询</el-button> <el-button @click="reset" v-if="searchConfig.showResetBtn === false ? false : true">重 置</el-button> </el-form-item> </el-form> <div v-if="searchConfig.layoutType === 'bottom'" class="searchBtn"> <el-button type="primary" @click="search">查 询</el-button> <el-button @click="reset" v-if="searchConfig.showResetBtn === false ? false : true">重 置</el-button> </div> </template> <template v-else> <p class="search-loading"><i class="el-icon-loading"></i> 加载中...</p> </template> </div>
<div class="line"></div>
<!-- 操作栏 --> <div class="operateBtn btn-box" v-if="operateBtnConfig.length"> <div class="operateBtn-left"> <!-- formcreate 循环按钮使用自定义组件 --> <BaseBtnList :list="operateBtnConfig"></BaseBtnList> </div>
<div class="operateBtn-right"> <slot name="operateBtnRight"></slot> </div> </div>
<!-- Table栏 --> <div class="baseTable" v-if="tableConfig.showTable === false ? false : true"> <el-table v-if="!tableConfig.isVirtualTable" ref="table" class="tableMaxHeightClass" v-loading="tableLoading" :data="tableData" :tooltip-effect="tableConfig.tooltipEffect || 'dark'" style="width: 100%" :border="tableConfig.border === false ? false : true" :highlight-current-row="tableConfig.highlightCurrentRow" :current-row-key="tableConfig.currentRowKey || 'id'" :row-key="tableConfig.rowKey" :max-height="tableMaxHeight" :row-class-name="toString.call(tableConfig.rowClassName) === '[object Undefined]' && idField ? defaultRowClassName : tableConfig.rowClassName" :cell-class-name="tableConfig.cellClassName" :header-row-class-name="tableConfig.headerRowClassName" :header-cell-class-name="tableConfig.headerCellClassName" :span-method="({row, column, rowIndex, columnIndex}) => toString.call(tableConfig.spanMethod) === '[object Undefined]' ? () => {} : tableConfig.spanMethod({row, column, rowIndex, columnIndex, tableRef: $refs.table})" @select="select" @select-all="selectAll" @selection-change="selectionChange" @row-dblclick="rowDblclick" @row-click="rowClick" @cell-click="cellClick" @row-contextmenu="rowContextmenu" @header-dragend="headerDragend" @sort-change="sortChange"> <el-table-column type="expand" v-if="tableConfig.showExpand" align="center" width="55"> <template slot-scope="props"> <slot name="expand" :props="props" :row="props.row"></slot> </template> </el-table-column>
<el-table-column type="selection" v-if="tableConfig.showSelection === false ? false : true" :reserve-selection="tableConfig.reserveSelection === true ? true : false" :fixed="tableConfig.fixedSelection" :selectable="tableConfig.selectable" align="center" width="44"> </el-table-column>
<el-table-column type="index" v-if="tableConfig.showIndex === false ? false : true" align="center" label="序号" width="56"> </el-table-column>
<template v-for="(item, index) in tableConfig.columns"> <el-table-column v-if="(item.columShow === 'false' || item.columShow === false) ? false : true" :key="index" :width="item.width" :min-width="item.minWidth || 120" :prop="item.prop" :label="item.label" :fixed="item.fixed" :sortable="item.sortable === false ? false : true" :render-header="item.renderHeader" :align="item.align || 'left'" :header-align="item.headerAlign || 'left'" :class-name="item.className" :sort-method="item.sortMethod" :show-overflow-tooltip="item.showOverflowTooltip === false ? false : true"> <template slot-scope="scope" v-if="!item.formatter"> <slot v-if="item.slot" :name="item.prop" :scope="scope" :index="scope.$index" :row="scope.row"></slot> <span v-else>{{ scope.row[item.prop] }}</span> </template>
<!-- 合并单元格 传children --> <template v-if="item.children"> <el-table-column v-for="(childItem, childIndex) in item.children" :key="childIndex" :label="childItem.label" :prop="childItem.prop" :width="childItem.width" :min-width="childItem.minWidth || 100" :align="childItem.align || 'left'" :header-align="childItem.headerAlign || 'left'" :class-name="childItem.className" show-overflow-tooltip :sortable="item.sortable === false ? false : true" :sort-method="item.sortMethod" > <template slot-scope="scope"> <slot v-if="childItem.slot" :name="childItem.prop" :scope="scope" :index="scope.$index" :row="scope.row"></slot> <span v-else>{{ scope.row[childItem.prop] ? scope.row[childItem.prop] : childItem.empty }}</span> </template> </el-table-column> </template> </el-table-column> </template> </el-table>
<!-- 虚拟表格 --> <u-table v-else ref="table" class="tableMaxHeightClass" v-loading="tableLoading" :data="tableData" :tooltip-effect="tableConfig.tooltipEffect || 'dark'" style="width: 100%" :border="tableConfig.border === false ? false : true" :highlight-current-row="tableConfig.highlightCurrentRow" :current-row-key="tableConfig.currentRowKey || 'id'" :max-height="tableConfig.tableMaxHeight || tableMaxHeight" :row-class-name="toString.call(tableConfig.rowClassName) === '[object Undefined]' && idField ? defaultRowClassName : tableConfig.rowClassName" :cell-class-name="tableConfig.cellClassName" :header-row-class-name="tableConfig.headerRowClassName" :header-cell-class-name="tableConfig.headerCellClassName" :span-method="({row, column, rowIndex, columnIndex}) => toString.call(tableConfig.spanMethod) === '[object Undefined]' ? () => {} : tableConfig.spanMethod({row, column, rowIndex, columnIndex, tableRef: $refs.table})" @select="select" @select-all="selectAll" @selection-change="selectionChange" @row-dblclick="rowDblclick" @row-click="rowClick" @cell-click="cellClick" @row-contextmenu="rowContextmenu" @header-dragend="headerDragend" @sort-change="sortChange" use-virtual :row-height="tableConfig.rowHeight || uTableRowHeight" > <u-table-column type="expand" v-if="tableConfig.showExpand" align="center" width="56"> <template slot-scope="props"> <slot name="expand" :props="props" :row="props.row"></slot> </template> </u-table-column>
<u-table-column type="selection" v-if="tableConfig.showSelection === false ? false : true" :fixed="tableConfig.fixedSelection" :selectable="tableConfig.selectable" align="center" header-align="center" width="40"> </u-table-column>
<!-- 有些需要在序号前加东西 --> <u-table-column v-if="tableConfig.showOther" align="center" width="56"> <template slot-scope="scope"> <slot name="showOther" :scope="scope" :index="scope.$index" :row="scope.row"></slot> </template> </u-table-column>
<u-table-column type="index" v-if="tableConfig.showIndex === false ? false : true" align="center" label="序号" class-name="uIndex" width="56"> </u-table-column>
<template v-for="(item, index) in tableConfig.columns"> <u-table-column v-if="(item.columShow === 'false' || item.columShow === false) ? false : true" :key="index" :width="item.width" :min-width="item.minWidth || 120" :prop="item.prop" :label="item.label" :fixed="item.fixed" :sortable="item.sortable === false ? false : true" :render-header="item.renderHeader" :align="item.align || 'left'" :header-align="item.headerAlign || 'left'" :class-name="item.className" :sort-method="item.sortMethod" :show-overflow-tooltip="item.showOverflowTooltip === false ? false : true"> <template slot-scope="scope" v-if="!item.formatter"> <slot v-if="item.slot" :name="item.prop" :scope="scope" :index="scope.$index" :row="scope.row"></slot> <span v-else>{{ scope.row[item.prop] }}</span> </template>
<!-- 合并单元格 传children --> <template v-if="item.children"> <u-table-column v-for="(childItem, childIndex) in item.children" :key="childIndex" :label="childItem.label" :prop="childItem.prop" :width="childItem.width" :min-width="childItem.minWidth || 100" :align="childItem.align || 'left'" :header-align="childItem.headerAlign || 'left'" :class-name="childItem.className" show-overflow-tooltip :sortable="item.sortable === false ? false : true" :sort-method="item.sortMethod" > <template slot-scope="scope"> <slot v-if="childItem.slot" :name="childItem.prop" :scope="scope" :index="scope.$index" :row="scope.row"></slot> <span v-else>{{ scope.row[childItem.prop] ? scope.row[childItem.prop] : childItem.empty }}</span> </template> </u-table-column> </template> </u-table-column> </template> </u-table>
<div class="pagination" v-if="tableConfig.showPagination === false ? false : true"> <el-pagination background @size-change="val => handlePageChange(val, 'pageSize')" @current-change="val => handlePageChange(val, 'pageNo')" :current-page="basePagination.pageNo" :page-sizes="basePagination.pageSizes" :page-size="basePagination.pageSize" :pager-count="pagerCount" :layout="pagerCount === 7 ? 'total, sizes, prev, pager, next, jumper' : 'total, pager'" :total="basePagination.total"> </el-pagination> </div> </div> </div> </template>
<script> import tableConfigMixins from "@/views/mixin/tableConfigMixins" import { defaultSelectPageColumns } from "@/utils/constants"; import { resetSelectPage } from "@/utils/utils"; import { deepClone } from '@/utils';
import treeselect from "@riophae/vue-treeselect"; import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import BaseBtnList from '@/components/BaseBtnList/index.vue'
export default { name: 'BaseTable', props: { // 搜索栏配置 searchConfig: { type: Object, default: () => { return { inline: true, layoutType: 'last', form: [ { // type: 'input', // name: 'name', // label: '活动名称', // placeholder: '活动名称' } ] } } }, // 操作栏配置 operateBtnConfig: { type: Array, default: () => { return [] } }, // table数据 tableData: { type: Array, default: () => { return [] } }, // table栏配置 tableConfig: { showTable: true, showSelection: true, isVirtualTable: false, // 是否虚拟表格 columns: [ { prop: '', label: '', minWidth: '' } ] }, tableLoading: false, // 分页配置 pagination: { type: Object, default: () => { return { pageNo: 1, total: 0, pageSize: 30, pageSizes: [ 30, 100, 500, 1000 ] } } }, // table的唯一id字段, 用做默认row-class-name使用 idField: String }, components: {treeselect, BaseBtnList}, data() { return { // 选中的数据 selectRows: [], // 表单 searchForm: {}, // 分页 basePagination: this.pagination, // 分页组件的默认table配置 defaultSelectPageColumns: defaultSelectPageColumns, uTableRowHeight: 30, pagerCount: document.body.clientWidth < 992 ? 5 : 7 } }, mixins: [ tableConfigMixins ], created() { }, mounted() { this.searchConfig?.form?.forEach(r => { if (r.defaultValue != null) { this.$nextTick(() => { this.$set(this.searchForm, r.name, r.defaultValue) }) } }) }, methods: { select(rows, row) { this.$emit('select', rows, row) }, selectAll(rows) { this.$emit('selectAll', rows) }, selectionChange(rows) { this.selectRows = rows this.$emit('selectionChange', rows) }, search() { let obj = deepClone(this.searchForm) let searchForm = {} for (const key in obj) { if (key != 'undefined') { searchForm[key] = obj[key] } } this.$emit('search', searchForm) }, reset() { this.$refs.searchRef.resetFields()
this.searchConfig.form.forEach(r => { if (r.type === 'selectPage') { resetSelectPage(`selectPage_${r.name}`, this) } })
this.searchForm = {}
this.$nextTick(() => { this.searchConfig?.form?.forEach(r => { if (r.defaultValue != null) { this.$set(this.searchForm, r.name, r.type === 'selectPage' ? String(typeof(r.defaultValue) === 'function' ? r.defaultValue() : r.defaultValue) : typeof(r.defaultValue) === 'function' ? r.defaultValue() : r.defaultValue) } }) // 重置数据 let obj = deepClone(this.searchForm) let searchForm = {} for (const key in obj) { if (key != 'undefined') { searchForm[key] = obj[key] } } this.$emit('reset', searchForm) }) }, handlePageChange(val, type) { this.$set(this.basePagination, type, val) type === 'pageSize' && (this.$set(this.basePagination, 'pageNo', 1)) this.$emit('changePage', this.basePagination) }, rowDblclick(row, column, event) { this.$emit('rowDblclick', row, column, event) }, rowClick(row, column, event) { this.$emit('rowClick', row, column, event) }, cellClick(row, column, cell, event) { this.$emit('cellClick', row, column, cell, event) }, rowContextmenu(row, column, event) { this.$emit('rowContextmenu', row, column, event) }, // 设置默认值 setDefaultValue() { this.searchConfig?.form?.forEach(r => { if (r.defaultValue != null) { this.$set(this.searchForm, r.name, r.type === 'selectPage' ? String(typeof(r.defaultValue) === 'function' ? r.defaultValue() : r.defaultValue) : typeof(r.defaultValue) === 'function' ? r.defaultValue() : r.defaultValue) } }) }, resultFormat(res) { const {list, total} = res.data return {list, totalRow: total} }, // 输入框触发enter事件 keyupEnterNative() { this.$emit('keyupEnterNative') }, // 列宽拖拽 headerDragend(newWidth, oldWidth, column, event) { this.$emit('headerDragend', newWidth, oldWidth, column, event) }, // 设置勾选时默认的rowClassName defaultRowClassName({row}) { const isSelect = this.selectRows.some(r => r[this.idField] === row[this.idField]) if (isSelect) return'select-row' }, commonSelectChange(v, item) { toString.call(item.change) === '[object Function]' ? item.change(v) : null }, commonInputDblclick(v, item) { toString.call(item.dblclick) === '[object Function]' ? item.dblclick(v) : null }, // 转换tree数据结构 normalizer(node) { if (node.children && !node.children.length) { delete node.children; } return { id: node.id, label: node.name, children: node.children }; }, sortChange({ column, prop, order }) { this.$emit('sortChange', {column, prop, order, tableData: this.$refs?.table?.tableData}) } }, watch: { 'searchConfig': function() { this.setDefaultValue() }, 'pagination': { handler(n, o) { this.basePagination = n }, deep: true }, tableData: function(n) { if (n.length && this.tableConfig.isVirtualTable) { this.$nextTick(() => { this.uTableRowHeight = document.querySelectorAll('.uIndex')?.[1]?.getBoundingClientRect()?.height || 30 }) } }, 'searchForm': { handler(n, o) { this.$emit('receiveSearch', n) }, deep: true }, } } </script>
<style lang="scss" scoped> .container { padding: 0 0 20px; min-height: calc(100vh - 94px); width: calc(100% - 20px); margin: 0 auto; margin-top: 10px; border-radius: 4px; } .line { width: 100%; height: 10px; } .search, .operateBtn, .baseTable{ } .search { padding: 17px 20px 7px; border-radius: 4px; .search-loading { padding-bottom: 20px; font-size: 15px; } ::v-deep .el-form-item{ margin-bottom: 10px; margin-top: 0; } ::v-deep .el-button--small { transform: translateY(-1px); } } .operateBtn { padding: 16px 20px 6px; border-top-left-radius: 4px; border-top-right-radius: 4px; display: flex; justify-content: space-between; align-items: center; ::v-deep .iconfont { font-size: 12px; margin-right: 5px; } .operateBtn-left { ::v-deep .el-button{ margin-left: 0; margin-right: 10px; margin-bottom: 10px; } } .operateBtn-right { max-width: 40%; } } .baseTable { padding: 0 20px 15px; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; }
.searchBtn { text-align: center; padding: 0 0 20px; }
.pagination { text-align: right; padding-top: 15px; }
@media screen and (max-width: 1366px) { ::v-deep .el-select, ::v-deep .el-input { max-width: 120px; } ::v-deep .el-date-editor--daterange.el-input, ::v-deep .el-date-editor--daterange.el-input__inner, ::v-deep .el-date-editor--timerange.el-input, ::v-deep .el-date-editor--timerange.el-input__inner { width: 220px; } ::v-deep .el-form-item__label { vertical-align: super !important; } .el-form-item { margin-bottom: 10px !important; margin-right: 10px !important; } .search .el-form-item.el-form-item--small .v-selectpage { width: 120px; } .search .el-form-item.el-form-item--mini .v-selectpage div.sp-input-container div.sp-base{ width: 120px; } .pagination { max-width: 95%; } } </style> baseTable 文件夹下的 baseMixins.js import baseFetchOptions from "@/views/mixin/baseFetchOptions" import { deepClone } from "@/utils/index";
export default { data() { return { tableLoading: false, searchForm: {}, tableData: [], selectRows: [], pagination: { pageNo: 1, total: 0, pageSize: 30, pageSizes: [ 30, 100, 500, 1000 ] } } }, mixins: [ baseFetchOptions ], created() { }, methods: { // 默认的请求列表函数 fetchList() {}, // 搜索 search(form) { this.searchForm = form this.fetchList() }, // 接收改变的searchForm receiveSearch(form) { this.searchForm = form }, // 重置 reset(form) { this.searchForm = form this.pagination.pageNo = 1 this.fetchList() }, // 选中行 selectionChange(rows) { this.selectRows = rows }, // 分页事件 changePage(pageCnfig) { this.pagination = deepClone(pageCnfig) this.fetchList() }, // 限制多选 getSelectRows() { const { length } = this.selectRows if (!length) { this.$message.warning("至少选择一条数据!") return false } return true }, // 限制单选 getSelectRow() { const { length } = this.selectRows if (length != 1) { this.$message.warning("请选择一条数据!") return false } return true } } } =============================================================== baseTable 下的index 引入的 tableConfigMixins tableConfigMixins: // 此mixins用做处理所有table max height import { debounce } from 'throttle-debounce' export default { data() { return { tableMaxHeight: localStorage.getItem('tableMaxHeight') || 1000, otherHeight: 0, // 防止有别的高度没计算到 } }, computed: { }, created() { }, mounted() { this.setTableMaxHeight()
window.onresize = debounce(200, () => { this.setTableMaxHeight() }) }, methods: { setTableMaxHeight() { this.$nextTick(() => { setTimeout(() => { const { height } = document.body.getBoundingClientRect() const { height: navHeight } = document.querySelector('.navbar').parentElement.getBoundingClientRect() const { height: tableHeaderHeight, top: tableHeaderMarginTop } = document.querySelector('.el-table__header-wrapper').getBoundingClientRect()
// 使用属性控制 const h1 = height - 30 - tableHeaderHeight - tableHeaderMarginTop - this.otherHeight this.tableMaxHeight = h1 < 450 ? 450 : h1
// 使用class和属性控制 if (document.querySelector('.el-table.tableMaxHeightClass')) { const { height: searchH } = document.querySelector('.search-box')?.getBoundingClientRect() || {height: 0} const { height: btnH } = document.querySelector('.btn-box')?.getBoundingClientRect() || {height: 0} const { height: paginationH } = document.querySelector('.pagination')?.getBoundingClientRect() || {} const h = (height - navHeight - searchH - btnH - paginationH - 80 - this.otherHeight) const _h = h < 450 ? 450 : h document.querySelector('.el-table.tableMaxHeightClass').style.maxHeight = _h + 'px' this.tableMaxHeight = _h } // 使用class和属性控制, 虚拟 table 使用 if (document.querySelector('.tableMaxHeightClass')?.querySelector('.el-table')) { const { height: searchH } = document.querySelector('.search-box')?.getBoundingClientRect() const { height: btnH } = document.querySelector('.btn-box')?.getBoundingClientRect() const { height: paginationH } = document.querySelector('.pagination')?.getBoundingClientRect() || {} const h = (height - navHeight - searchH - btnH - paginationH - 80 - this.otherHeight) const _h = h < 450 ? 450 : h document.querySelector('.tableMaxHeightClass').querySelector('.el-table').style.maxHeight = _h + 'px' this.tableMaxHeight = _h } },500) }) } }, } ==================================================================================== import { defaultSelectPageColumns } from "@/utils/constants"; defaultSelectPageColumns : // 基础分页下拉Columns export const defaultSelectPageColumns = [ { title: 'CODE', data: 'code', width: 130 }, { title: '中文名', data: 'nameCn', minWidth: 180 }, { title: '英文名', data: 'nameEn', minWidth: 250 }, ] ==================================================================================== import { resetSelectPage } from "@/utils/utils"; resetSelectPage : /** * 清除selectPage选中项 * @param {*} name * @param {*} $vm vue实例 */ export function resetSelectPage (name, $vm) { const el = toString.call($vm.$refs[name]) === '[object Array]' ? $vm.$refs[name][0] : $vm.$refs[name] el?.remove?.()
// 处理重置后多选selectPage会展开下拉框问题 setTimeout(() => { el?.close?.() }) } ============================================================================================ import { deepClone } from '@/utils'; // 深拷贝对象 // https://github.com/JakHuang/form-generator/blob/dev/src/utils/index.js#L107 export function deepClone(obj) { const _toString = Object.prototype.toString
// null, undefined, non-object, function if (!obj || typeof obj !== 'object') { return obj }
// DOM Node if (obj.nodeType && 'cloneNode' in obj) { return obj.cloneNode(true) }
// Date if (_toString.call(obj) === '[object Date]') { return new Date(obj.getTime()) }
// RegExp if (_toString.call(obj) === '[object RegExp]') { const flags = [] if (obj.global) { flags.push('g') } if (obj.multiline) { flags.push('m') } if (obj.ignoreCase) { flags.push('i') }
return new RegExp(obj.source, flags.join('')) }
const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}
for (const key in obj) { result[key] = deepClone(obj[key]) }
return result } ==================================================================== import BaseBtnList from '@/components/BaseBtnList/index.vue' BaseBtnList : <template> <div> <template v-for="(item, index) in list"> <el-button :type="item.type || 'primary'" :icon="typeof item.icon != 'function' ? item.icon : null" :key="index" :plain="item.plain === false ? false : true" :loading="item.loading === true ? true : false" :style="item.style" :class="item.class" :disabled="item.disabled" @click="item.event()" v-if="item.show === false ? false : true" >{{ item.name }}</el-button > </template> </div> </template>
<script> export default { props: { list: { type: Array, default: () => [], }, }, data() { return {
}; }, created() {}, methods: { }, }; </script> <style scoped lang="scss"> ::v-deep .el-button+.el-button { margin-bottom: 10px; } </style> ================================================================= import treeselect from "@riophae/vue-treeselect"; import "@riophae/vue-treeselect/dist/vue-treeselect.css"; 此引入需要安装插件 安装的命令如下,哪里需要哪里引入,同时使用的components中也需要在页面中使用 yarn add @riophae/vue-treeselect 标签:el,const,表格,return,item,import,组件,table,row From: https://www.cnblogs.com/rockyjs/p/17934502.html