import { Component, Prop, Vue, Emit, Watch } from "vue-property-decorator"; import { State } from "vuex-class"; import { formatDateTime } from "@/libs/util"; /** * @version:1.0.0 * @name:动态表格组件 * @date:2019-08-30 * 动态表格组件各参数详解: * csTable: { * formWidth: (number) 表格的form表单弹出窗占屏幕的百分比 * parentVm: (object) 当前引用表格组件的vue实例 * dataSrc: (string) 请求表格数据列表的后台接口url路径 * delSrc: (string) 删除单条数据的后台接口url路径 * batchDelUri:(string)批量删除数据的后台接口url路径 * dataProp: (string)解析后台列表数据的结构 * postMethod: (boolean)表格数据列表的请求方式,true:post方法;false:get方法 * pageableProp:{ * pageSize:(string)解析后台分页数据的每页个数 * total: (string)解析后台分页数据的总个数 * currentPage:(string)解析后台分页数据的当前页 * } * search:{ * show:(boolean)是否显示搜索区域,当前只支持一个搜索 * prop:(string)搜索传给后台的字段 * value:(any)搜索传给后台的数据值 * placeholder:(string)搜索区域的placeholder * componentName:(string)搜索的组件名称 * } * showActions:(boolean)是否显示表格顶部数据 * actions:{ * replace:(boolean)替换表格默认顶部的操作按钮,true:直接替换默认的操作按钮组;false:与默认的操作按钮数组 * def: (array) 需要替换或合并的数组 * } * showDefaultOpColumn:(boolean)是否显示默认的操作列 * dataFun:(Function||boolean)替换默认的第一次表格数据加载方法 * showIndex:(boolean)是否显示表格数据的序号 * emptyModel:(object)当前表格数据的实例空数据 * columns:(array)表格的列 * paginationByFront:(boolean),//是否需要前端分页 * dataPropFront:(string),//前端分页解析的数据格式 * showPage:(boolean)是否显示分页 * } */ import TableSearchDate from "./table-search-date.vue" import TableSearchInput from "./table-search-input.vue" import AdvanceSearchForm from "./advance-search-form.vue" @Component({ components: { TableSearchDate, TableSearchInput, AdvanceSearchForm } }) export default class CsDynamicTable extends Vue { @Prop(Object) prop: any; @State userInfo!: any; @State private menuList!: Array; @State isAdministrator!: boolean; defaultSearchComName: String = "TableSearchInput"; isShowPagination: boolean = true; timer: any = null; self: any = null; tableHeight: number = 0 tableData: Array = []; actions: any = { def: [] }; csTableVm: any = this; multipleSelection: Array = []; currentRow: any = null; pageTimeout: any = null;//防止用户切换当前页面过快 pagination: any = { pageSize: this.prop.pageInfo ? this.prop.pageInfo.pageSize : 10, total: 0, pageSizes: this.prop.pageInfo ? this.prop.pageInfo.pageSizes : [10, 20, 50, 100], currentPage: 1, order: this.prop.order || "", showTotal: true, showSizer: true }; formInfo: any = Object.assign({ title: "", size: "full", action: "save", width: 75, model: this.prop.emptyModel, }); dialogVisible: boolean = false; paginationTableData: Array = [];//前端分页的数据 /** * 默认操作列 * @type {{title: string; key: string; minWidth: number; align: string; render: ((h, params) => any)}} */ defaultOperateColumn: any = { title: '操作', key: 'action', minWidth: 200, align: 'center', render: (h, params) => { let menus = [{ icon: 'ios-eye', action: 'detail', label: '详情', type: 'info', access: true }, { icon: 'ios-create-outline', action: 'update', label: '编辑', type: 'primary', access: this.renderCondition("edit") }, { icon: 'md-trash', action: 'delete', label: '删除', type: 'error', access: this.renderCondition("delete") }]; let clickEvent = this.clickEvent; return h('div', [ h('CsButton', { props: { actions: menus, click: clickEvent, data: params.row, size: "small", styleCss: "marginRight:5px", disabled: params.row._disabled }, }) ]); } } /** * 检测是否具有特定操作的权限 * @param action */ renderCondition(action) { // if (this.userInfo.actionsMap && this.prop.module) { // let actions = this.userInfo.actionsMap[this.prop.module]; // if (actions) { // return actions.indexOf(action) >= 0; // } // } return true; } clickEvent(name: string, obj: any) { if (name == 'delete') { this.handleDelete(obj.id) } else { this.formInfo.title = name == 'update' ? "编辑" : "详情"; this.formInfo.action = name == 'update' ? 'update' : 'detail'; this.formInfo.model = _.cloneDeep(obj); this.dialogVisible = true; (this.$globalFun() as any).getMethod('show') } } paginationData() { let tableData = this.paginationTableData.slice((this.pagination.currentPage - 1) * this.pagination.pageSize, this.pagination.currentPage * this.pagination.pageSize); let response = { data: { content: tableData, totalElements: this.paginationTableData.length, totalPages: Math.ceil(this.paginationTableData.length / this.pagination.pageSize), size: this.pagination.pageSize, number: this.pagination.currentPage - 1 }, rtnCode: 0 } this.afterGetData(response) } /** * 将表格请求数据之后处理的逻辑剥离出来,然后根据父组件传来的successBack属性来决定是否执行对应的成功回调方法 * @param {Object} response */ afterGetData(response: any): void { //取消表格选中数据 if ((this.$refs.elTable as any)) { (this.$refs.elTable as any).selectAll(false); } let tableData: Array = [] if (this.prop.paginationByFront) { tableData = eval("response." + this.prop.dataPropFront); } else { tableData = eval("response." + this.prop.dataProp); } if (this.prop.disableFun) { this.prop.disableFun(tableData) } if (this.prop.paginationByFront) { this.paginationTableData = _.cloneDeep(tableData); this.prop.dataFun = this.paginationData; this.prop.paginationByFront = false; this.paginationData(); } else { this.tableData = tableData this.constructPageInfo(response) if (this.prop.successBack) { this.prop.successBack.apply(this, [].concat(response)); } } } getSearchData(isGetParam: boolean = false) { let data = this.prop.postData || {}; let queryStr = ""; try { queryStr = "&"; if (Array.isArray(this.prop.search.value)) { data[this.prop.search.prop] = [this.prop.search.value.join(",")]; queryStr = queryStr + this.prop.search.prop + "=" + this.prop.search.value.join(","); } else { data[this.prop.search.prop] = [this.prop.search.value]; queryStr = queryStr + this.prop.search.prop + "=" + this.prop.search.value; } } catch (error) { console.warn(`construct searchParams error,${error}`); } return isGetParam ? queryStr : data; } getData() { if (this.prop.dataFun) { this.prop.dataFun.apply(this, [].concat(this.prop.dataFunArgs)) return } if (this.prop.postMethod) { let postData = this.getSearchData(false); this.getApiData('post', this.prop.dataSrc + this.constructPageRequest(true) + "&randomCount=" + new Date().getTime(), postData, (response: any) => { this.afterGetData(response) }); } else { this.getApiData('get', this.prop.dataSrc + this.constructPageRequest(true) + this.getSearchData(true) + "&randomCount=" + new Date().getTime(), { params: {} }, (response: any) => { this.afterGetData(response) }); } } /** * 组装分页信息 * @param response */ constructPageInfo(response) { if (this.prop.pageableProp) { Object.keys(this.prop.pageableProp).forEach((key) => { /** * 由于在搜索不到数据时,如果后台接口返回一个非分页结构数据导致分页组装信息出错, * 导致执行页码变更或者size变更事件,所以在下面加上判断规避该问题 */ let value = eval("response." + this.prop.pageableProp[key]); if (typeof (value) == 'undefined') { this.pagination = { pageSize: 10, total: 0, pageSizes: [10, 20, 50, 100], currentPage: 1, order: this.prop.order || "", showTotal: true, showSizer: true } } else { if (key == "currentPage") { /** * iView分页组件存在问题描述如下 * 比如有11条数据,每页10条会分成两页,然后在第二页删除掉第第11条数据,那么页面会切到第一页,但是不会显示任何数据 * 这里加上if判断,如果表格数据为空,然后查询到的数据总数又是大于0,那么默认当前页减少一页,触发watch里面的currentPage变更,重新加载数据 */ if (this.tableData.length == 0 && this.pagination.total > 0) { this.pagination[key] = value } else { this.pagination[key] = value + 1 } } else { this.pagination[key] = value } } }) } } handleCurrentRowChange(row) { this.currentRow = row } /** * 组装分布请求 * @param query * @returns {*} */ constructPageRequest(query) { if (query) { /** * postParams:用来表示获取数据url有参数传递 */ if (this.prop.postParams && this.prop.postParams) { return "&size=" + this.pagination.pageSize + "&page=" + (this.pagination.currentPage - 1) + "&sort=" + this.pagination.order } return "?size=" + this.pagination.pageSize + "&page=" + (this.pagination.currentPage - 1) + "&sort=" + this.pagination.order } else { return { size: this.pagination.pageSize, page: this.pagination.currentPage - 1, order: this.pagination.order } } } /** * 分页大小变更时事件 * @param pageSize */ handleSizeChange(pageSize) { let tempPage = _.cloneDeep(this.pagination.currentPage); this.pagination.pageSize = pageSize; if (tempPage != 1) { this.pagination.currentPage = 1 } else { this.getData(); } } /** * 页数变更时事件 * @param pageSize */ handleCurrentPageChange(currPage) { console.log(currPage) this.pagination.currentPage = currPage; } keyupEnter (e) { let maxPage = Math.ceil(this.pagination.total/this.pagination.pageSize); this.handleCurrentPageChange(Number(e.target.value)>maxPage?maxPage.toString():e.target.value) } handleSortChange(col) { if (col.column) { this.pagination.order = col.key + "," + (col.order == "asc" ? "ASC" : "DESC") } if (col.order) { this.getData() } } /** * 刷新数据 */ refreshData() { this.getData() } search() { this.getData() } advanceSearch() { let data = this.prop.postData || {}; if (this.prop.advanceSearch && this.prop.advanceSearch.length > 0) { this.prop.advanceSearch.forEach(item => { if (item.value && item.value.length > 0) { if (Array.isArray(item.value)) { if (item.type == "date") { if (item.value[0] && item.value[1]) { let time0 = formatDateTime(new Date(item.value[0]), item.format ? item.format : "yyyy-MM-dd hh:mm:ss"); let time1 = formatDateTime(new Date(item.value[1]), item.format ? item.format : "yyyy-MM-dd hh:mm:ss"); data[item.prop] = [time0 + "," + time1]; } else { delete data[item.prop]; } } else { data[item.prop] = [_.join(item.value, ",")]; } } else { if (item.type == "date" && item.value) { let time0 = formatDateTime(new Date(item.value), item.format ? item.format : "yyyy-MM-dd hh:mm:ss"); data[item.prop] = [time0]; } else { data[item.prop] = [item.value]; } } } else { delete data[item.prop]; } }) } this.prop.postData = data; this.getData(); } isShowPage(){ if (this.prop.showPage == false) { this.isShowPagination=false; } } /** * 获取表头上的操作按钮 * @returns {*} */ getActions() { if (this.prop.showActions == false) { return null; } Object.assign(this.actions, { width: 5, def: [{ name: '增加', handler: () => { this.formInfo.title = "增加"; this.formInfo.action = "save" this.formInfo.model = Object.assign({}, this.prop.emptyModel) this.dialogVisible = true; (this.$globalFun() as any).getMethod('show') }, type: 'primary', icon: 'md-add', access: this.renderCondition("add") }, { name: '删除', handler: () => { if (this.prop.batchDeleFun) { this.prop.batchDeleFun.apply(this, [this.multipleSelection]) } else if (this.prop.batchDelUri) { this.batchDeleteRequest(); } else { this.$Message.info('请先配置批量删除路径或批量删除方法'); } }, type: 'error', icon: 'md-trash', access: this.renderCondition("delete") }] }) if (this.prop.actions) { this.prop.actions.def.forEach((item) => { item.handler = () => { item.action.apply(this, [].concat(item)) } }) if (this.prop.actions.replace) { Object.assign(this.actions, this.prop.actions) } else { this.actions.def = this.actions.def.concat(this.prop.actions.def) } } return this.actions } handleSelect(selection, row) { this.multipleSelection = selection; } handleRowClick(row, index) { this.$emit('rowClick', row) //如果点击的是selection则return,否则会出触发2次handleSelect if (row.type == 'selection') { //var index = this.tableData.indexOf(row) return false; } //默认点击不选中,如果传参数rowClickSelect为true时单击row切换选中状态 if (this.prop.rowClickSelect) { (this.$refs.elTable as any).toggleRowSelection(row); } if (this.prop.rowClick) { this.prop.rowClick.apply(this, [row]) } } /** * 删除一条记录 * @param id */ handleDelete(id) { (this.$globalFun() as any).getMethod('show') this.$Modal.confirm({ title: '提示', content: '此操作将永久删除, 是否继续?', okText: '确定', cancelText: '取消', onOk: () => { if (this.prop.delFun) { this.prop.delFun.apply(this, [id]) } else { if (this.prop.apiResource) { this.prop.apiResource.delete({ id: id }).then(() => { (this.$globalFun() as any).getMethod('close') this.$Message.success('删除成功'); this.getData(); this.multipleSelection = [] }) } else { let delSrc = this.prop.delSrc; if (!delSrc.endsWith("/")) { delSrc = delSrc + "/"; } this.getApiData('delete', delSrc + id, {}, (response) => { this.$Message.success('删除成功'); (this.$globalFun() as any).getMethod('close') this.getData(); this.multipleSelection = [] }) } } }, onCancel: () => { this.$Message.info('已取消删除'); // Todo (this.$globalFun() as any).getMethod('close') } }) } batchDeleteRequest(uri = this.prop.batchDelUri) { if (this.multipleSelection.length > 0) { (this.$globalFun() as any).getMethod('show') this.$Modal.confirm({ title: '提示', content: '此操作将永久删除, 是否继续?', okText: '确定', cancelText: '取消', onOk: () => { var ids = this.multipleSelection.map((item) => { return item.id; }) this.getApiData('post', uri, ids, (response) => { this.$Message.success('删除成功'); (this.$globalFun() as any).getMethod('close') this.getData(); this.multipleSelection = [] }) }, onCancel: () => { this.$Message.info('已取消删除'); (this.$globalFun() as any).getMethod('close') } }) } else { this.$Message.warning('请先选择需要删除的选项'); } } get paginationDef() { if (this.prop.pageInfo) { Object.assign(this.pagination, this.prop.pageInfo) } return this.pagination } get currentPage() { return this.pagination.currentPage } get spinShow() { return false } get selectedLength() { return this.multipleSelection.length; } created() { this.getData() clearInterval(this.timer) /** * 新增定时器刷新页面 */ if (this.prop.timeout) { this.timer = setInterval(() => { if (this.multipleSelection.length == 0) { this.getData(); } }, this.prop.timeout) } if (this.prop.showDefaultOpColumn) { this.prop.columns.push(this.defaultOperateColumn); } this.getActions() this.isShowPage() } cancel() { (this.$refs.dialog as any).$children[1].cancel() } closeModal() { this.dialogVisible = !1; (this.$globalFun() as any).getMethod('close') } submit() { let children = (this.$refs.dialog as any).$children.filter((item) => { return jq(item.$el).find(".ivu-form").length > 0; }) children[0].submit() } initWindowEvent() { window.addEventListener('resize', () => { this.$nextTick(() => { if (jq(".ivu-modal :visible").length == 0) { this.calculateTableHeight() } }) }) } calculateTableHeight() { let height = jq(this.$parent.$el).height() - jq(".i-table-top-btns").height() - jq(".ivu-page").height() - jq(".page-name").height() - 20 - 50; if (jq("div[class$='-search']").length > 0) { height -= jq("div[class$='-search']").height() } if (jq(".i-table-title").length > 0) { height = height - jq(".i-table-title").height() } this.tableHeight = height; } changeDate(datetime) { //这里的date就是我们想要的本地时间 console.info(datetime) this.prop.search.value = datetime //这里就可以对后面传进来的参数进行操作了 } mounted() { let isCalculateTableHeight = (this.$globalFun() as any).getMethod('isCalculateTableHeight', true) if (isCalculateTableHeight) { this.initWindowEvent(); this.$nextTick(() => { if (jq(".ivu-modal :visible").length == 0) { this.calculateTableHeight() } }) } } @Watch('currentPage') currentPageChange(val) { /** * 防止页码切换过快,导致异步数据因返回速度差异导致渲染出错 */ clearTimeout(this.pageTimeout) this.pageTimeout = setTimeout(() => { this.getData(); }, 150); } }